From d6ab7584cbb42040d68f2cfb1c5d579c0842e9be Mon Sep 17 00:00:00 2001 From: redtide Date: Mon, 8 May 2023 14:43:33 +0200 Subject: [PATCH] New repository, for git history check the original --- .clang-format | 5 + .clang-tidy | 37 + .editorconfig | 18 + .gitattributes | 32 + .github/workflows/build.yml | 482 + .gitignore | 34 + .gitmodules | 18 + AUTHORS.md | 13 + CHANGELOG.md | 774 + CMakeLists.txt | 53 + CONTRIBUTING.md | 223 + FUNDING.yml | 1 + GOVERNANCE.md | 56 + LICENSE | 25 + README.md | 110 + cmake/BundleDylibs.cmake | 32 + cmake/LV2Config.cmake | 126 + cmake/MakeUninstall.cmake.in | 21 + cmake/PluginsUninstall.cmake | 19 + cmake/PuredataConfig.cmake | 70 + cmake/StringUtility.cmake | 11 + cmake/VSTConfig.cmake | 48 + library | 1 + mac/dmg-back.png | Bin 0 -> 18921 bytes mac/dmg-back.svg | 251 + mac/dmg-back@2x.png | Bin 0 -> 40969 bytes plugins/CMakeLists.txt | 60 + plugins/common/plugin/FileTrie.cpp | 108 + plugins/common/plugin/FileTrie.h | 52 + .../common/plugin/InstrumentDescription.cpp | 224 + plugins/common/plugin/InstrumentDescription.h | 56 + plugins/common/plugin/MessageUtils.cpp | 39 + plugins/common/plugin/MessageUtils.h | 19 + plugins/common/plugin/NativeHelpers.cpp | 144 + plugins/common/plugin/NativeHelpers.h | 26 + plugins/common/plugin/NativeHelpers.mm | 27 + plugins/common/plugin/RMSFollower.h | 188 + plugins/common/plugin/SfizzFileScan.cpp | 286 + plugins/common/plugin/SfizzFileScan.h | 44 + plugins/common/plugin/SfizzForeignPaths.cpp | 53 + plugins/common/plugin/SfizzForeignPaths.h | 12 + plugins/common/plugin/SfizzForeignPaths.mm | 27 + plugins/common/plugin/SfizzSettings.cpp | 145 + plugins/common/plugin/SfizzSettings.h | 16 + plugins/common/plugin/SfizzSettings.mm | 38 + plugins/editor/CMakeLists.txt | 158 + plugins/editor/cmake/Vstgui.cmake | 261 + .../external/color-spaces/ColorSpaces.h | 129 + plugins/editor/external/color-spaces/LICENSE | 22 + plugins/editor/external/stb_image/LICENSE | 37 + plugins/editor/external/stb_image/stb_image.h | 7897 ++++++++++ plugins/editor/external/vstgui4 | 1 + plugins/editor/layout/about.fl | 68 + plugins/editor/layout/main.fl | 464 + .../editor/resources/Fonts/Roboto-Regular.ttf | Bin 0 -> 171272 bytes .../Fonts/sfizz-fluentui-system-f20.ttf | Bin 0 -> 229796 bytes .../Fonts/sfizz-fluentui-system-r20.ttf | Bin 0 -> 275084 bytes .../resources/Fonts/sfizz-misc-icons.ttf | Bin 0 -> 4472 bytes .../editor/resources/Themes/Dark/theme.xml | 40 + .../editor/resources/Themes/Default/theme.xml | 40 + plugins/editor/resources/background.png | Bin 0 -> 6312 bytes plugins/editor/resources/background@2x.png | Bin 0 -> 13771 bytes .../resources/background_button_about.png | Bin 0 -> 6933 bytes .../resources/background_button_about@2x.png | Bin 0 -> 16768 bytes plugins/editor/resources/icon_white.png | Bin 0 -> 3412 bytes plugins/editor/resources/icon_white.svg | 253 + plugins/editor/resources/icon_white@2x.png | Bin 0 -> 7336 bytes plugins/editor/resources/knob.knob | Bin 0 -> 22266 bytes plugins/editor/resources/knob48.png | Bin 0 -> 7382 bytes plugins/editor/resources/knob48@2x.png | Bin 0 -> 92347 bytes plugins/editor/resources/logo.png | Bin 0 -> 11712 bytes plugins/editor/resources/logo.svg | 246 + plugins/editor/resources/logo_orange.png | Bin 0 -> 27303 bytes plugins/editor/resources/logo_orange.svg | 246 + plugins/editor/resources/logo_orange@2x.png | Bin 0 -> 29240 bytes plugins/editor/resources/logo_shaded.svg | 246 + plugins/editor/resources/logo_text.png | Bin 0 -> 5414 bytes plugins/editor/resources/logo_text.svg | 252 + plugins/editor/resources/logo_text@2x.png | Bin 0 -> 10935 bytes plugins/editor/resources/logo_text_shaded.png | Bin 0 -> 2471 bytes .../editor/resources/logo_text_shaded@2x.png | Bin 0 -> 4726 bytes plugins/editor/resources/logo_text_white.png | Bin 0 -> 2591 bytes .../editor/resources/logo_text_white@2x.png | Bin 0 -> 4056 bytes plugins/editor/src/editor/ColorHelpers.cpp | 81 + plugins/editor/src/editor/ColorHelpers.h | 38 + plugins/editor/src/editor/DlgAbout.cpp | 323 + plugins/editor/src/editor/DlgAbout.h | 58 + plugins/editor/src/editor/EditIds.cpp | 44 + plugins/editor/src/editor/EditIds.h | 109 + plugins/editor/src/editor/EditValue.h | 67 + plugins/editor/src/editor/Editor.cpp | 2345 +++ plugins/editor/src/editor/Editor.h | 31 + plugins/editor/src/editor/EditorController.h | 53 + plugins/editor/src/editor/EditorLibs.cpp | 8 + plugins/editor/src/editor/EditorLibs.h | 8 + plugins/editor/src/editor/GUIComponents.cpp | 1350 ++ plugins/editor/src/editor/GUIComponents.h | 521 + plugins/editor/src/editor/GUIDefs.cpp | 26 + plugins/editor/src/editor/GUIDefs.h | 28 + plugins/editor/src/editor/GUIHelpers.cpp | 78 + plugins/editor/src/editor/GUIHelpers.h | 30 + plugins/editor/src/editor/GUIPiano.cpp | 403 + plugins/editor/src/editor/GUIPiano.h | 71 + plugins/editor/src/editor/ImageHelpers.cpp | 87 + plugins/editor/src/editor/ImageHelpers.h | 24 + plugins/editor/src/editor/NativeHelpers.cpp | 340 + plugins/editor/src/editor/NativeHelpers.h | 20 + plugins/editor/src/editor/NativeHelpers.mm | 111 + plugins/editor/src/editor/Theme.cpp | 173 + plugins/editor/src/editor/Theme.h | 68 + plugins/editor/src/editor/VSTGUIHelpers.cpp | 35 + plugins/editor/src/editor/VSTGUIHelpers.h | 10 + plugins/editor/src/editor/VSTGUIHelpers.mm | 26 + plugins/editor/src/editor/layout/about.hpp | 28 + plugins/editor/src/editor/layout/main.hpp | 247 + .../editor/src/editor/utility/vstgui_after.h | 9 + .../editor/src/editor/utility/vstgui_before.h | 14 + plugins/editor/tools/layout-maker/LICENSE | 23 + plugins/editor/tools/layout-maker/README | 2 + .../tools/layout-maker/sources/layout.h | 50 + .../tools/layout-maker/sources/main.cpp | 190 + .../tools/layout-maker/sources/reader.cpp | 316 + .../tools/layout-maker/sources/reader.h | 21 + plugins/lv2/CMakeLists.txt | 145 + plugins/lv2/LICENSE.md.in | 12 + .../external/ardour/ardour/lv2_extensions.h | 282 + plugins/lv2/lv2.version | 4 + plugins/lv2/lv2ui.version | 4 + plugins/lv2/manifest.ttl.in | 18 + plugins/lv2/resources/DefaultInstrument.sfz | 3 + plugins/lv2/resources/DefaultScale.scl | 14 + plugins/lv2/sfizz.cpp | 1978 +++ plugins/lv2/sfizz.ttl.in | 917 ++ plugins/lv2/sfizz_lv2.h | 163 + plugins/lv2/sfizz_lv2_common.cpp | 125 + plugins/lv2/sfizz_lv2_plugin.h | 163 + plugins/lv2/sfizz_ui.cpp | 903 ++ plugins/lv2/sfizz_ui.ttl.in | 17 + plugins/lv2/vendor/lv2/atom/atom.h | 247 + plugins/lv2/vendor/lv2/atom/forge.h | 670 + plugins/lv2/vendor/lv2/atom/util.h | 510 + plugins/lv2/vendor/lv2/buf-size/buf-size.h | 38 + plugins/lv2/vendor/lv2/core/attributes.h | 46 + plugins/lv2/vendor/lv2/core/lv2.h | 470 + plugins/lv2/vendor/lv2/core/lv2_util.h | 90 + .../lv2/vendor/lv2/data-access/data-access.h | 60 + .../lv2/vendor/lv2/dynmanifest/dynmanifest.h | 146 + plugins/lv2/vendor/lv2/event/event-helpers.h | 242 + plugins/lv2/vendor/lv2/event/event.h | 285 + .../lv2/instance-access/instance-access.h | 28 + plugins/lv2/vendor/lv2/log/log.h | 100 + plugins/lv2/vendor/lv2/log/logger.h | 144 + plugins/lv2/vendor/lv2/midi/midi.h | 235 + plugins/lv2/vendor/lv2/morph/morph.h | 35 + plugins/lv2/vendor/lv2/options/options.h | 136 + .../lv2/vendor/lv2/parameters/parameters.h | 55 + plugins/lv2/vendor/lv2/patch/patch.h | 60 + .../lv2/vendor/lv2/port-groups/port-groups.h | 64 + .../lv2/vendor/lv2/port-props/port-props.h | 40 + plugins/lv2/vendor/lv2/presets/presets.h | 35 + .../lv2/vendor/lv2/resize-port/resize-port.h | 76 + plugins/lv2/vendor/lv2/state/state.h | 379 + plugins/lv2/vendor/lv2/time/time.h | 46 + plugins/lv2/vendor/lv2/ui/ui.h | 529 + plugins/lv2/vendor/lv2/units/units.h | 62 + plugins/lv2/vendor/lv2/uri-map/uri-map.h | 108 + plugins/lv2/vendor/lv2/urid/urid.h | 127 + plugins/lv2/vendor/lv2/worker/worker.h | 170 + plugins/lv2/vstgui_helpers.cpp | 246 + plugins/lv2/vstgui_helpers.h | 101 + plugins/puredata/CMakeLists.txt | 57 + plugins/puredata/example.sfz | 22 + plugins/puredata/external/pd/LICENSE.txt | 30 + plugins/puredata/external/pd/bin/pd.def | 601 + plugins/puredata/external/pd/include/m_pd.h | 911 ++ plugins/puredata/sfizz_puredata.c | 336 + plugins/puredata/sfizz~-help.pd | 60 + plugins/vst/CMakeLists.txt | 376 + plugins/vst/IdleUpdateHandler.h | 47 + plugins/vst/OrderedEventProcessor.h | 37 + plugins/vst/OrderedEventProcessor.hpp | 103 + plugins/vst/SfizzVstController.cpp | 496 + plugins/vst/SfizzVstController.h | 76 + plugins/vst/SfizzVstEditor.cpp | 544 + plugins/vst/SfizzVstEditor.h | 85 + plugins/vst/SfizzVstIDs.h | 22 + plugins/vst/SfizzVstParameters.h | 113 + plugins/vst/SfizzVstProcessor.cpp | 981 ++ plugins/vst/SfizzVstProcessor.h | 146 + plugins/vst/SfizzVstState.cpp | 187 + plugins/vst/SfizzVstState.h | 44 + plugins/vst/SfizzVstUpdates.cpp | 174 + plugins/vst/SfizzVstUpdates.h | 241 + plugins/vst/SfizzVstUpdates.hpp | 41 + plugins/vst/Vst2PluginEntry.c | 41 + plugins/vst/Vst2PluginFactory.cpp | 18 + plugins/vst/VstPluginDefs.h.in | 11 + plugins/vst/VstPluginFactory.cpp | 52 + plugins/vst/X11RunLoop.cpp | 231 + plugins/vst/X11RunLoop.h | 51 + plugins/vst/cmake/Vst3.cmake | 129 + plugins/vst/external/VST_SDK/VST3_SDK/base | 1 + .../external/VST_SDK/VST3_SDK/pluginterfaces | 1 + .../vst/external/VST_SDK/VST3_SDK/public.sdk | 1 + plugins/vst/external/ring_buffer/LICENSE | 23 + .../ring_buffer/ring_buffer/ring_buffer.cpp | 105 + .../ring_buffer/ring_buffer/ring_buffer.h | 69 + .../ring_buffer/ring_buffer/ring_buffer.tcc | 61 + plugins/vst/external/sfzt_auwrapper | 1 + plugins/vst/gpl-3.0.txt | 674 + plugins/vst/mac/Info.au.plist | 50 + plugins/vst/mac/Info.vst3.plist | 24 + plugins/vst/mac/PkgInfo | 1 + plugins/vst/mac/audiounitconfig.h.in | 15 + plugins/vst/vst3.def | 4 + plugins/vst/vst3.version | 7 + plugins/vst/win/Plugin.ico | Bin 0 -> 351974 bytes plugins/vst/win/desktop.ini | 2 + screenshot.png | Bin 0 -> 44587 bytes scripts/create_mac_icon.sh | 18 + scripts/create_windows_icon.sh | 18 + scripts/generate_ui_fonts.sh | 30 + scripts/innosetup.iss.in | 77 + scripts/mingw_dwrite_3.h | 12280 ++++++++++++++++ scripts/misc-icons/LICENSE.md | 30 + scripts/misc-icons/README.md | 14 + scripts/misc-icons/generate_icons_font.json | 9 + scripts/misc-icons/generate_icons_font.py | 54 + scripts/misc-icons/icons/discord.svg | 1 + scripts/misc-icons/icons/github.svg | 1 + scripts/misc-icons/icons/opencollective.svg | 1 + scripts/misc-icons/icons/sfzformat.svg | 8 + scripts/misc-icons/icons/sfztools.svg | 11 + scripts/package-osx-bundles.sh | 40 + scripts/plugin.pkg/package.xml | 22 + scripts/plugin.pkg/welcome.txt | 3 + scripts/run_clang_tidy.sh | 53 + 237 files changed, 52150 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 AUTHORS.md create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 CONTRIBUTING.md create mode 100644 FUNDING.yml create mode 100644 GOVERNANCE.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/BundleDylibs.cmake create mode 100644 cmake/LV2Config.cmake create mode 100644 cmake/MakeUninstall.cmake.in create mode 100644 cmake/PluginsUninstall.cmake create mode 100644 cmake/PuredataConfig.cmake create mode 100644 cmake/StringUtility.cmake create mode 100644 cmake/VSTConfig.cmake create mode 160000 library create mode 100644 mac/dmg-back.png create mode 100644 mac/dmg-back.svg create mode 100644 mac/dmg-back@2x.png create mode 100644 plugins/CMakeLists.txt create mode 100644 plugins/common/plugin/FileTrie.cpp create mode 100644 plugins/common/plugin/FileTrie.h create mode 100644 plugins/common/plugin/InstrumentDescription.cpp create mode 100644 plugins/common/plugin/InstrumentDescription.h create mode 100644 plugins/common/plugin/MessageUtils.cpp create mode 100644 plugins/common/plugin/MessageUtils.h create mode 100644 plugins/common/plugin/NativeHelpers.cpp create mode 100644 plugins/common/plugin/NativeHelpers.h create mode 100644 plugins/common/plugin/NativeHelpers.mm create mode 100644 plugins/common/plugin/RMSFollower.h create mode 100644 plugins/common/plugin/SfizzFileScan.cpp create mode 100644 plugins/common/plugin/SfizzFileScan.h create mode 100644 plugins/common/plugin/SfizzForeignPaths.cpp create mode 100644 plugins/common/plugin/SfizzForeignPaths.h create mode 100644 plugins/common/plugin/SfizzForeignPaths.mm create mode 100644 plugins/common/plugin/SfizzSettings.cpp create mode 100644 plugins/common/plugin/SfizzSettings.h create mode 100644 plugins/common/plugin/SfizzSettings.mm create mode 100644 plugins/editor/CMakeLists.txt create mode 100644 plugins/editor/cmake/Vstgui.cmake create mode 100644 plugins/editor/external/color-spaces/ColorSpaces.h create mode 100644 plugins/editor/external/color-spaces/LICENSE create mode 100644 plugins/editor/external/stb_image/LICENSE create mode 100644 plugins/editor/external/stb_image/stb_image.h create mode 160000 plugins/editor/external/vstgui4 create mode 100644 plugins/editor/layout/about.fl create mode 100644 plugins/editor/layout/main.fl create mode 100644 plugins/editor/resources/Fonts/Roboto-Regular.ttf create mode 100644 plugins/editor/resources/Fonts/sfizz-fluentui-system-f20.ttf create mode 100644 plugins/editor/resources/Fonts/sfizz-fluentui-system-r20.ttf create mode 100644 plugins/editor/resources/Fonts/sfizz-misc-icons.ttf create mode 100644 plugins/editor/resources/Themes/Dark/theme.xml create mode 100644 plugins/editor/resources/Themes/Default/theme.xml create mode 100644 plugins/editor/resources/background.png create mode 100644 plugins/editor/resources/background@2x.png create mode 100644 plugins/editor/resources/background_button_about.png create mode 100644 plugins/editor/resources/background_button_about@2x.png create mode 100644 plugins/editor/resources/icon_white.png create mode 100644 plugins/editor/resources/icon_white.svg create mode 100644 plugins/editor/resources/icon_white@2x.png create mode 100644 plugins/editor/resources/knob.knob create mode 100644 plugins/editor/resources/knob48.png create mode 100644 plugins/editor/resources/knob48@2x.png create mode 100644 plugins/editor/resources/logo.png create mode 100644 plugins/editor/resources/logo.svg create mode 100644 plugins/editor/resources/logo_orange.png create mode 100644 plugins/editor/resources/logo_orange.svg create mode 100644 plugins/editor/resources/logo_orange@2x.png create mode 100644 plugins/editor/resources/logo_shaded.svg create mode 100644 plugins/editor/resources/logo_text.png create mode 100644 plugins/editor/resources/logo_text.svg create mode 100644 plugins/editor/resources/logo_text@2x.png create mode 100644 plugins/editor/resources/logo_text_shaded.png create mode 100644 plugins/editor/resources/logo_text_shaded@2x.png create mode 100644 plugins/editor/resources/logo_text_white.png create mode 100644 plugins/editor/resources/logo_text_white@2x.png create mode 100644 plugins/editor/src/editor/ColorHelpers.cpp create mode 100644 plugins/editor/src/editor/ColorHelpers.h create mode 100644 plugins/editor/src/editor/DlgAbout.cpp create mode 100644 plugins/editor/src/editor/DlgAbout.h create mode 100644 plugins/editor/src/editor/EditIds.cpp create mode 100644 plugins/editor/src/editor/EditIds.h create mode 100644 plugins/editor/src/editor/EditValue.h create mode 100644 plugins/editor/src/editor/Editor.cpp create mode 100644 plugins/editor/src/editor/Editor.h create mode 100644 plugins/editor/src/editor/EditorController.h create mode 100644 plugins/editor/src/editor/EditorLibs.cpp create mode 100644 plugins/editor/src/editor/EditorLibs.h create mode 100644 plugins/editor/src/editor/GUIComponents.cpp create mode 100644 plugins/editor/src/editor/GUIComponents.h create mode 100644 plugins/editor/src/editor/GUIDefs.cpp create mode 100644 plugins/editor/src/editor/GUIDefs.h create mode 100644 plugins/editor/src/editor/GUIHelpers.cpp create mode 100644 plugins/editor/src/editor/GUIHelpers.h create mode 100644 plugins/editor/src/editor/GUIPiano.cpp create mode 100644 plugins/editor/src/editor/GUIPiano.h create mode 100644 plugins/editor/src/editor/ImageHelpers.cpp create mode 100644 plugins/editor/src/editor/ImageHelpers.h create mode 100644 plugins/editor/src/editor/NativeHelpers.cpp create mode 100644 plugins/editor/src/editor/NativeHelpers.h create mode 100644 plugins/editor/src/editor/NativeHelpers.mm create mode 100644 plugins/editor/src/editor/Theme.cpp create mode 100644 plugins/editor/src/editor/Theme.h create mode 100644 plugins/editor/src/editor/VSTGUIHelpers.cpp create mode 100644 plugins/editor/src/editor/VSTGUIHelpers.h create mode 100644 plugins/editor/src/editor/VSTGUIHelpers.mm create mode 100644 plugins/editor/src/editor/layout/about.hpp create mode 100644 plugins/editor/src/editor/layout/main.hpp create mode 100644 plugins/editor/src/editor/utility/vstgui_after.h create mode 100644 plugins/editor/src/editor/utility/vstgui_before.h create mode 100644 plugins/editor/tools/layout-maker/LICENSE create mode 100644 plugins/editor/tools/layout-maker/README create mode 100644 plugins/editor/tools/layout-maker/sources/layout.h create mode 100644 plugins/editor/tools/layout-maker/sources/main.cpp create mode 100644 plugins/editor/tools/layout-maker/sources/reader.cpp create mode 100644 plugins/editor/tools/layout-maker/sources/reader.h create mode 100644 plugins/lv2/CMakeLists.txt create mode 100644 plugins/lv2/LICENSE.md.in create mode 100644 plugins/lv2/external/ardour/ardour/lv2_extensions.h create mode 100644 plugins/lv2/lv2.version create mode 100644 plugins/lv2/lv2ui.version create mode 100644 plugins/lv2/manifest.ttl.in create mode 100644 plugins/lv2/resources/DefaultInstrument.sfz create mode 100644 plugins/lv2/resources/DefaultScale.scl create mode 100644 plugins/lv2/sfizz.cpp create mode 100644 plugins/lv2/sfizz.ttl.in create mode 100644 plugins/lv2/sfizz_lv2.h create mode 100644 plugins/lv2/sfizz_lv2_common.cpp create mode 100644 plugins/lv2/sfizz_lv2_plugin.h create mode 100644 plugins/lv2/sfizz_ui.cpp create mode 100644 plugins/lv2/sfizz_ui.ttl.in create mode 100644 plugins/lv2/vendor/lv2/atom/atom.h create mode 100644 plugins/lv2/vendor/lv2/atom/forge.h create mode 100644 plugins/lv2/vendor/lv2/atom/util.h create mode 100644 plugins/lv2/vendor/lv2/buf-size/buf-size.h create mode 100644 plugins/lv2/vendor/lv2/core/attributes.h create mode 100644 plugins/lv2/vendor/lv2/core/lv2.h create mode 100644 plugins/lv2/vendor/lv2/core/lv2_util.h create mode 100644 plugins/lv2/vendor/lv2/data-access/data-access.h create mode 100644 plugins/lv2/vendor/lv2/dynmanifest/dynmanifest.h create mode 100644 plugins/lv2/vendor/lv2/event/event-helpers.h create mode 100644 plugins/lv2/vendor/lv2/event/event.h create mode 100644 plugins/lv2/vendor/lv2/instance-access/instance-access.h create mode 100644 plugins/lv2/vendor/lv2/log/log.h create mode 100644 plugins/lv2/vendor/lv2/log/logger.h create mode 100644 plugins/lv2/vendor/lv2/midi/midi.h create mode 100644 plugins/lv2/vendor/lv2/morph/morph.h create mode 100644 plugins/lv2/vendor/lv2/options/options.h create mode 100644 plugins/lv2/vendor/lv2/parameters/parameters.h create mode 100644 plugins/lv2/vendor/lv2/patch/patch.h create mode 100644 plugins/lv2/vendor/lv2/port-groups/port-groups.h create mode 100644 plugins/lv2/vendor/lv2/port-props/port-props.h create mode 100644 plugins/lv2/vendor/lv2/presets/presets.h create mode 100644 plugins/lv2/vendor/lv2/resize-port/resize-port.h create mode 100644 plugins/lv2/vendor/lv2/state/state.h create mode 100644 plugins/lv2/vendor/lv2/time/time.h create mode 100644 plugins/lv2/vendor/lv2/ui/ui.h create mode 100644 plugins/lv2/vendor/lv2/units/units.h create mode 100644 plugins/lv2/vendor/lv2/uri-map/uri-map.h create mode 100644 plugins/lv2/vendor/lv2/urid/urid.h create mode 100644 plugins/lv2/vendor/lv2/worker/worker.h create mode 100644 plugins/lv2/vstgui_helpers.cpp create mode 100644 plugins/lv2/vstgui_helpers.h create mode 100644 plugins/puredata/CMakeLists.txt create mode 100644 plugins/puredata/example.sfz create mode 100644 plugins/puredata/external/pd/LICENSE.txt create mode 100644 plugins/puredata/external/pd/bin/pd.def create mode 100644 plugins/puredata/external/pd/include/m_pd.h create mode 100644 plugins/puredata/sfizz_puredata.c create mode 100644 plugins/puredata/sfizz~-help.pd create mode 100644 plugins/vst/CMakeLists.txt create mode 100644 plugins/vst/IdleUpdateHandler.h create mode 100644 plugins/vst/OrderedEventProcessor.h create mode 100644 plugins/vst/OrderedEventProcessor.hpp create mode 100644 plugins/vst/SfizzVstController.cpp create mode 100644 plugins/vst/SfizzVstController.h create mode 100644 plugins/vst/SfizzVstEditor.cpp create mode 100644 plugins/vst/SfizzVstEditor.h create mode 100644 plugins/vst/SfizzVstIDs.h create mode 100644 plugins/vst/SfizzVstParameters.h create mode 100644 plugins/vst/SfizzVstProcessor.cpp create mode 100644 plugins/vst/SfizzVstProcessor.h create mode 100644 plugins/vst/SfizzVstState.cpp create mode 100644 plugins/vst/SfizzVstState.h create mode 100644 plugins/vst/SfizzVstUpdates.cpp create mode 100644 plugins/vst/SfizzVstUpdates.h create mode 100644 plugins/vst/SfizzVstUpdates.hpp create mode 100644 plugins/vst/Vst2PluginEntry.c create mode 100644 plugins/vst/Vst2PluginFactory.cpp create mode 100644 plugins/vst/VstPluginDefs.h.in create mode 100644 plugins/vst/VstPluginFactory.cpp create mode 100644 plugins/vst/X11RunLoop.cpp create mode 100644 plugins/vst/X11RunLoop.h create mode 100644 plugins/vst/cmake/Vst3.cmake create mode 160000 plugins/vst/external/VST_SDK/VST3_SDK/base create mode 160000 plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces create mode 160000 plugins/vst/external/VST_SDK/VST3_SDK/public.sdk create mode 100644 plugins/vst/external/ring_buffer/LICENSE create mode 100644 plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp create mode 100644 plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.h create mode 100644 plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc create mode 160000 plugins/vst/external/sfzt_auwrapper create mode 100644 plugins/vst/gpl-3.0.txt create mode 100644 plugins/vst/mac/Info.au.plist create mode 100644 plugins/vst/mac/Info.vst3.plist create mode 100644 plugins/vst/mac/PkgInfo create mode 100644 plugins/vst/mac/audiounitconfig.h.in create mode 100644 plugins/vst/vst3.def create mode 100644 plugins/vst/vst3.version create mode 100644 plugins/vst/win/Plugin.ico create mode 100644 plugins/vst/win/desktop.ini create mode 100644 screenshot.png create mode 100755 scripts/create_mac_icon.sh create mode 100755 scripts/create_windows_icon.sh create mode 100755 scripts/generate_ui_fonts.sh create mode 100644 scripts/innosetup.iss.in create mode 100644 scripts/mingw_dwrite_3.h create mode 100644 scripts/misc-icons/LICENSE.md create mode 100644 scripts/misc-icons/README.md create mode 100644 scripts/misc-icons/generate_icons_font.json create mode 100755 scripts/misc-icons/generate_icons_font.py create mode 100644 scripts/misc-icons/icons/discord.svg create mode 100644 scripts/misc-icons/icons/github.svg create mode 100644 scripts/misc-icons/icons/opencollective.svg create mode 100644 scripts/misc-icons/icons/sfzformat.svg create mode 100644 scripts/misc-icons/icons/sfztools.svg create mode 100755 scripts/package-osx-bundles.sh create mode 100644 scripts/plugin.pkg/package.xml create mode 100644 scripts/plugin.pkg/welcome.txt create mode 100755 scripts/run_clang_tidy.sh diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..81339e1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +BasedOnStyle: WebKit +SortIncludes: false + +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..fd268a0 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,37 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,abseil-*,bugprone-*,performance-*,-abseil-no-internal-dependencies' +WarningsAsErrors: 'clang-diagnostic-*,clang-analyzer-*,abseil-*,bugprone-*,performance-*' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: file +User: paul +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +... diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..174deec --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig configuration +# http://editorconfig.org + +# Top-most EditorConfig file +root = true + +# UTF-8 charset, set indent to spaces with width of four, +# with no trailing whitespaces and a newline ending every file. +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{fl,in,sh,ttl,yml}] +indent_size = 2 +indent_style = space diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9dc5a4c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,32 @@ +.* export-ignore +.*/** export-ignore +appveyor.yml export-ignore +/scripts/appveyor/** export-ignore +/vst/external/VST_SDK/VST3_SDK/**/CMakeLists.txt export-ignore +/vst/external/VST_SDK/VST3_SDK/public.sdk/samples export-ignore +/vst/external/VST_SDK/VST3_SDK/public.sdk/source/vst/*wrapper/** export-ignore +/vst/external/VST_SDK/VST3_SDK/public.sdk/source/vst/interappaudio/** export-ignore +/vst/external/VST_SDK/VST3_SDK/public.sdk/source/vst/testsuite/** export-ignore +/vst/external/VST_SDK/VST3_SDK/public.sdk/source/vst/utility/test/** export-ignore +/editor/external/vstgui4/**/CMakeLists.txt export-ignore +/editor/external/vstgui4/vstgui/Documentation/** export-ignore +/editor/external/vstgui4/vstgui/doxygen/** export-ignore +/editor/external/vstgui4/vstgui/standalone/** export-ignore +/editor/external/vstgui4/vstgui/tests/** export-ignore +/editor/external/vstgui4/vstgui/tools/** export-ignore +/editor/external/vstgui4/vstgui/uidescription/** export-ignore +/editor/external/vstgui4/vstgui/uidescription/icontroller.h -export-ignore +/editor/external/vstgui4/vstgui/vstgui_standalone* export-ignore +/editor/external/vstgui4/vstgui/vstgui_uidescription* export-ignore +/external/st_audiofile/thirdparty/dr_libs/old/** export-ignore +/external/st_audiofile/thirdparty/dr_libs/tests/** export-ignore +/external/filesystem/test/** export-ignore +/external/abseil-cpp/conanfile.py export-ignore +/external/abseil-cpp/**/BUILD.bazel export-ignore +/external/abseil-cpp/ci/** export-ignore +/external/simde/docker/** export-ignore +/external/simde/test/** export-ignore +/external/simde/meson* export-ignore +/external/simde/*.py export-ignore +/external/simde/*.yml export-ignore +/external/simde/*.toml export-ignore diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..db193a9 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,482 @@ +name: build + +on: + push: + branches: + - 'develop' + - 'master' + tags: + - '[0-9]*' + - 'v[0-9]*' + pull_request: + branches: + - '*' + workflow_dispatch: +env: + BUILD_TYPE: Release + +jobs: + clang_tidy: + # if: ${{ false }} + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Set up dependencies + run: | + sudo apt-get update && \ + sudo apt-get install \ + clang-tidy \ + libsndfile1-dev + - name: Clang Tidy + working-directory: ${{runner.workspace}} + run: cd "$GITHUB_WORKSPACE" && scripts/run_clang_tidy.sh + + build_for_linux: + # if: ${{ false }} + runs-on: ubuntu-20.04 + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-linux" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Set up dependencies + run: | + sudo apt-get update && \ + sudo apt-get install \ + ninja-build \ + libjack-jackd2-dev \ + libsndfile1-dev \ + libcairo2-dev \ + libpango1.0-dev \ + libfontconfig1-dev \ + libx11-xcb-dev \ + libxcb-util-dev \ + libxcb-cursor-dev \ + libxcb-xkb-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + libxcb-keysyms1-dev + - name: Create Build Environment + shell: bash + working-directory: ${{runner.workspace}} + run: cmake -E make_directory build + - name: Configure CMake + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake "$GITHUB_WORKSPACE" -G Ninja \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DSFIZZ_JACK=ON \ + -DSFIZZ_VST=ON \ + -DSFIZZ_LV2_UI=ON \ + -DSFIZZ_PUREDATA=ON \ + -DSFIZZ_TESTS=ON \ + -DSFIZZ_SHARED=OFF \ + -DSFIZZ_STATIC_DEPENDENCIES=OFF \ + -DSFIZZ_LV2=ON \ + -DCMAKE_CXX_STANDARD=17 + - name: Build tests + if: ${{ false }} # DISABLED: Do this in library repo + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "$BUILD_TYPE" -j 2 --target sfizz_tests + - name: Run tests + if: ${{ false }} # DISABLED: Do this in library repo + shell: bash + run: ${{runner.workspace}}/build/tests/sfizz_tests + # Reenable with newer cmakes + # working-directory: ${{runner.workspace}}/build + # run: ctest -j 2 -C "$BUILD_TYPE" --output-on-failure + - name: Build all + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "$BUILD_TYPE" -j 2 + - name: Install + working-directory: ${{runner.workspace}}/build + shell: bash + run: | + DESTDIR="$(pwd)/$install_name" cmake --build . --config "$BUILD_TYPE" --target install + tar czvf "$install_name".tar.gz "$install_name" + - uses: actions/upload-artifact@v3 + with: + name: Linux tarball + path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz + + build_with_libsndfile: + if: ${{ false }} # DISABLED: Do this in library repo + runs-on: ubuntu-20.04 + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-linux" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Set up dependencies + run: | + sudo apt-get update && \ + sudo apt-get install \ + ninja-build \ + libsndfile1-dev + - name: Create Build Environment + shell: bash + working-directory: ${{runner.workspace}} + run: cmake -E make_directory build + - name: Configure CMake + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake "$GITHUB_WORKSPACE" -G Ninja \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DSFIZZ_JACK=OFF \ + -DSFIZZ_VST=OFF \ + -DSFIZZ_LV2_UI=OFF \ + -DSFIZZ_PUREDATA=OFF \ + -DSFIZZ_USE_SNDFILE=ON \ + -DSFIZZ_TESTS=ON \ + -DSFIZZ_SHARED=OFF \ + -DSFIZZ_STATIC_DEPENDENCIES=OFF \ + -DSFIZZ_LV2=OFF + - name: Build tests + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "$BUILD_TYPE" -j 2 --target sfizz_tests + - name: Run tests + shell: bash + run: ${{runner.workspace}}/build/tests/sfizz_tests + + build_for_macos: + # if: ${{ false }} + runs-on: macos-11 + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-macos" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Create Build Environment + shell: bash + working-directory: ${{runner.workspace}} + run: cmake -E make_directory build + - name: Configure CMake + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake "$GITHUB_WORKSPACE" \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DSFIZZ_AU=ON \ + -DSFIZZ_LV2=ON \ + -DSFIZZ_LV2_UI=ON \ + -DSFIZZ_VST=ON \ + -DSFIZZ_JACK=OFF \ + -DSFIZZ_TESTS=ON \ + -DSFIZZ_SHARED=OFF \ + -DSFIZZ_STATIC_DEPENDENCIES=OFF \ + -DCMAKE_CXX_STANDARD=17 + - name: Build tests + if: ${{ false }} # DISABLED: Do this in library repo + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + cmake --build . --config "$BUILD_TYPE" -j 2 --target sfizz_tests + - name: Run tests + if: ${{ false }} # DISABLED: Do this in library repo + shell: bash + run: ${{runner.workspace}}/build/tests/sfizz_tests + # Reenable with newer cmakes + # working-directory: ${{runner.workspace}}/build + # run: ctest -j 2 -C "$BUILD_TYPE" --output-on-failure + - name: Build plugins + shell: bash + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "$BUILD_TYPE" -j 2 + - name: Package OSX bundles + shell: bash + working-directory: ${{runner.workspace}} + run: $GITHUB_WORKSPACE/scripts/package-osx-bundles.sh + - name: Create OSX installer + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + productbuild \ + --distribution $GITHUB_WORKSPACE/scripts/plugin.pkg/package.xml \ + --identifier "sfz.tools.sfizz" \ + --package-path "${{runner.workspace}}/build" \ + --version 0 \ + ${{env.install_name}}.pkg + - uses: actions/upload-artifact@v3 + with: + name: macOS installer + path: | + ${{runner.workspace}}/build/${{env.install_name}}.pkg + + build_for_mod: + if: ${{ false }} # DISABLED: Old image which doesn't work with CMake > 3.5 + runs-on: ubuntu-20.04 + container: + image: jpcima/mod-plugin-builder + options: --user 0 + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-moddevices" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Fix up MOD environment + shell: bash + run: ln -sf /home/builder/mod-workdir ~/mod-workdir + - name: Create Build Environment + shell: bash + working-directory: ${{runner.workspace}} + run: mod-plugin-builder /usr/local/bin/cmake -E make_directory build + - name: Configure CMake + shell: bash + working-directory: ${{runner.workspace}}/build + run: | + mod-plugin-builder /usr/local/bin/cmake "$GITHUB_WORKSPACE" \ + -DPROJECT_SYSTEM_PROCESSOR=armv7-a \ + -DCMAKE_BUILD_TYPE=Release \ + -DSFIZZ_JACK=OFF \ + -DSFIZZ_VST=OFF \ + -DSFIZZ_LV2_UI=OFF + - name: Build + shell: bash + working-directory: ${{runner.workspace}}/build + run: mod-plugin-builder /usr/local/bin/cmake --build . --config "$BUILD_TYPE" -- -j 2 + - name: Install + working-directory: ${{runner.workspace}}/build + shell: bash + run: | + DESTDIR="$(pwd)/$install_name" mod-plugin-builder /usr/local/bin/cmake --build . --config "$BUILD_TYPE" --target install + tar czvf "$install_name".tar.gz "$install_name" + - uses: actions/upload-artifact@v3 + with: + name: MOD devices tarball + path: ${{runner.workspace}}/build/${{env.install_name}}.tar.gz + + build_for_windows: + # if: ${{ false }} + runs-on: windows-2019 + strategy: + matrix: + include: + - platform: x86 + pkg_platform: Win32 + release_arch: Win32 + bits: 32 + - platform: x64 + pkg_platform: Win64 + release_arch: x64 + bits: 64 + steps: + - name: Set install name + run: | + echo "install_ref=$(${Env:GITHUB_REF}.split('/')[-1])" >> "${Env:GITHUB_ENV}" + echo "install_name=sfizz-$(${Env:GITHUB_REF}.split('/')[-1])-win${{matrix.bits}}" >> "${Env:GITHUB_ENV}" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Create Build Environment + working-directory: ${{runner.workspace}} + run: cmake -E make_directory build + - name: Configure CMake + working-directory: ${{runner.workspace}}/build + run: | + cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${{matrix.release_arch}}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DCMAKE_CXX_STANDARD=17 -DSFIZZ_TESTS=ON -DSFIZZ_VST=ON -DSFIZZ_LV2=ON -DSFIZZ_PUREDATA=ON + - name: Build tests + if: ${{ false }} # DISABLED: Do this in library repo + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 --target sfizz_tests + - name: Test + if: ${{ false }} # DISABLED: Do this in library repo + run: ${{runner.workspace}}/build/tests/Release/sfizz_tests + - name: Build all + working-directory: ${{runner.workspace}}/build + run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2 + - name: Install pluginval + if: ${{ matrix.platform == 'x64' }} + run: | + Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Windows.zip -OutFile pluginval.zip + Expand-Archive pluginval.zip -DestinationPath pluginval + echo "$(Get-Location)\pluginval" | Out-File -FilePath ${Env:GITHUB_PATH} -Encoding utf8 -Append + pluginval\pluginval --version + - name: Validate VST3 + if: ${{ matrix.platform == 'x64' }} + working-directory: ${{runner.workspace}}/build + run: pluginval --validate-in-process --validate sfizz.vst3 + - name: Create installer + working-directory: ${{runner.workspace}}/build + run: iscc /O"." /F"${Env:install_name}" /dARCH="${{matrix.platform}}" innosetup.iss + - uses: actions/upload-artifact@v3 + with: + name: ${{matrix.pkg_platform}} installer + path: ${{runner.workspace}}/build/${{env.install_name}}.exe + + build_for_mingw: + if: ${{ false }} # DISABLED TEMPORARILY + runs-on: ubuntu-20.04 + strategy: + matrix: + include: + - platform: i686 + pkg_platform: Win32 + bits: 32 + - platform: x86_64 + pkg_platform: Win64 + bits: 64 + container: + image: archlinux + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}-mingw${{matrix.bits}}" >> "$GITHUB_ENV" + - name: Configure pacman repositories + shell: bash + run: | + cat >>/etc/pacman.conf <> "$GITHUB_ENV" + echo "install_name=sfizz-${GITHUB_REF##*/}" >> "$GITHUB_ENV" + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Set up dependencies + run: | + sudo apt-get update && \ + sudo apt-get install \ + python3-pip + sudo pip install git-archive-all + - name: Archive source code + shell: bash + run: | + cd "$GITHUB_WORKSPACE" && \ + git-archive-all --prefix="${install_name}/" -9 "${{runner.workspace}}/${install_name}.tar.gz" + - uses: actions/upload-artifact@v3 + with: + name: Source code tarball + path: ${{runner.workspace}}/${{env.install_name}}.tar.gz + + deploy: + runs-on: ubuntu-20.04 + if: startsWith(github.ref, 'refs/tags/') + needs: + # - build_for_linux + # - build_for_mod + # - build_for_mingw + - build_for_macos + - build_for_windows + - archive_source_code + steps: + - name: Set install name + run: | + echo "install_ref=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + - uses: actions/download-artifact@v3 + if: ${{ false }} # DISABLED: Use distro packages + with: + name: Linux tarball + - uses: actions/download-artifact@v3 + with: + name: macOS installer + - uses: actions/download-artifact@v3 + if: ${{ false }} # DISABLED: See comment above + with: + name: MOD devices tarball + - uses: actions/download-artifact@v3 + if: ${{ false }} # DISABLED: MinGW 32 build temporarily disabled + with: + name: Win32 MinGW tarball + - uses: actions/download-artifact@v3 + if: ${{ false }} # DISABLED: MinGW 64 build temporarily disabled + with: + name: Win64 MinGW tarball + - uses: actions/download-artifact@v3 + with: + name: Win32 installer + - uses: actions/download-artifact@v3 + with: + name: Win64 installer + - uses: actions/download-artifact@v3 + with: + name: Source code tarball + - name: Display file information + shell: bash + run: ls -lR + - uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + with: + tag_name: ${{env.install_ref}} + name: Release ${{env.install_ref}} + draft: false + prerelease: false + files: | + sfizz-${{env.install_ref}}-* + sfizz-${{env.install_ref}}.* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..647af10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +build*/* +docs +.vscode +perf.data +perf.data.old + +CMakeBuild/ +Makefile +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +compile_commands.json +*.a +*.user* +*.autosave +*.diff +/Doxyfile +.DS_Store +.cache/ +.cmake/ + +clients/sfizz_jack +clients/sfzprint + +/plugins/vst/download/ +/plugins/vst/external/vstsdk2.4/ +/plugins/vst/external/CoreAudioUtilityClasses/ + +/plugins/editor/external/fluentui-system-icons/ + +*.sublime-* +*.code-* +.kak.tags.namecache +.clangd diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c3e333 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,18 @@ +[submodule "plugins/vst/external/VST_SDK/VST3_SDK/base"] + path = plugins/vst/external/VST_SDK/VST3_SDK/base + url = https://github.com/steinbergmedia/vst3_base.git +[submodule "plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces"] + path = plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces + url = https://github.com/steinbergmedia/vst3_pluginterfaces.git +[submodule "plugins/vst/external/VST_SDK/VST3_SDK/public.sdk"] + path = plugins/vst/external/VST_SDK/VST3_SDK/public.sdk + url = https://github.com/steinbergmedia/vst3_public_sdk.git +[submodule "plugins/editor/external/vstgui4"] + path = plugins/editor/external/vstgui4 + url = https://github.com/sfztools/vstgui.git +[submodule "plugins/vst/external/sfzt_auwrapper"] + path = plugins/vst/external/sfzt_auwrapper + url = https://github.com/sfztools/sfzt_auwrapper.git +[submodule "library"] + path = library + url = https://github.com/sfztools/sfizz.git diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..0ef3a60 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,13 @@ +# Author and contributors + +Contributors to `sfizz`, in chronologic order: + +- Paul Ferrand (2019-2020) (maintainer) +- Andrea Zanellato (2019-2020) (devops, documentation and distribution) +- Alexander Mitchell (2020) +- Michael Willis (2020) +- Jean-Pierre Cimalando (2020) +- Tobiasz "unfa" Karoń (2020) +- Kinwie (2020) +- Atsushi Eno (2020) +- Dominique Würtz (2021) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e7552e2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,774 @@ +# Changelog + +The format is based on [Keep a Changelog][1]. + +This project tries to adhere to [Semantic Versioning][2]. + +## [Unreleased] + +## [1.2.1] - 2023-04-15 + +### Globally + +#### Added + +- Mac OS Installer (#93 #1087) +- This file from Release-Notes.md in wiki. + +#### Changed + +- Updated bundled dependencies and added more options to use those + preinstalled in system (#1104 #1117 #1143 #1145 #1149) +- CI: Various updates and fixes for GitHub Actions; removed Travis and Appveyor + +### Library + +#### Added + +- Add LFO frequency as an EG target (#1103) +- New `image_controls` opcode to add a background image on UI' Controls tab (#1156) + +#### Fixed + +- Disabled temporarily the embedded sample test +- Parse 0-valued note ons as note offs (#1072 #1073) +- Correct a bug with dynamic updates on offed EGs (#2 #1088) +- jack: Wait for CLI thread to finish on quit (#1097) +- Fix libsndfile build and add CI (#1112 #1090) +- Last keyswitches don't change the midi state (#1113 #1074) +- Add section suffix to man pages (#1124) + +### UI + +#### Added + +- HiDPI: Added a Zoom menu in Settings to apply an user' zoom ratio preference + (#1123 #1153) + +#### Changed + +- Colored logo button on mouse hover (#1151) +- Centered, full-size background images container (#1152) +- XP style tooltips (#1156) +- Window size has been changed to be compatible with ARIA banks + (775x335 for image backgrounds) (#1140 #1156) +- The CC knobs rotator and title label have a white color + on a semi-transparent background in the controls tab (#1156) +- The info tab becomes a dark and semi-transparent overlay dialog above the + Home tab; both will display the same background image when available (#1156) + +### Plugins + +#### Added + +- Handle LV2 scaleFactor supportedOption (#1153) + +#### Fixed + +- Added suffix "-multi" to LV2 and VST3. (#1084) +- Fix LV2 manifest when using PSA (#1114) +- Fix 2 bugs in LV2 and VST3 for loads and stores (#1115 #1098) + +## [1.2.0] - 2022-02-15 + +### Major improvements + +- The sfizz engine now handles multiple stereo outputs, + through the `output` opcode. The VST3 and LV2 plugins both have + a 16-out version (as 8 stereo outs) (#1002) +- The `emscripten` branch allows to run sfizz through WASM + for your web-oriented needs. An example with WebMidi + is available at https://sfz.tools/sfizz-webaudio/ + (requires a Chromium-based browser), + with source code at https://github.com/sfztools/sfizz-webaudio. +- sfizz can now parse embedded samples in base64 data (#1041) + +### Library + +#### Added + +- Added `sfizz_send_program_change` and its C++ counterpart. +- Added `sfizz_set_sustain_cancels_release` and its C++ counterpart. +- Support extended CCs 140 and 141 for "keydelta" (#1003) +- Added better support for files that are fully loaded in memory + and not simply preloaded (#1031) +- Added support for `loprog/hiprog` (#1058) + +#### Changed + +- The internal midi state is not reset when loading/reloading. + This means in particular that controls changed on an instrument + will be kept if you edit the underlying SFZ file (#1002) +- Updated dependent libraries (#1018) +- Negative values for lokey will not disable the region anymore (#1019) +- Choke groups now act through CC switches (#1024) +- `sw_last` is now properly affected by `octave_offset` and `note_offset` (#1039) +- sfizz is now clamping flex EG levels (#1037) + +#### Fixed + +- Corrected a bug when reloading some files (#1028) +- Corrected mistakes in the shelving filters (#1027) +- Properly read exponential notation in floats (#1036) +- Corrected a bug where long-tailed effects would be cut off (#1050) +- Corrected a bug in the band rejection filters (#1052). + The `apf_1p` filter still misbehaves and is disabled for now. + +#### Deprecated + +- `sfizz_enable_logging` and `sfizz_disable_logging` + (and their C++ counterparts) are deprecated, + replaced by `sfizz_get_callback_breakdown` which needs to be called + at the end of a callback by the host (#1034). + All file writing is handled by the host for logging. + +### UI + +#### Added + +- Added support in the plugin GUI/parameters for the "sustain cancels release" + behavior of LinuxSampler. + +#### Changed + +- Updated the GUI option panel for the plugins (#1022) + and some hints on the tab buttons (#1057) + +#### Fixed + +- Numpad input is now properly handled (#1053) + +### Plugins + +#### Added + +- In the LV2 plugin, plugin-side automation is now enabled by default + (which means all SFZ control changes are naturally bound + to incoming MIDI control changes and not controlled by a plugin parameter). + You can disable this at build time and show the relevant parameters + using `SFIZZ_LV2_PSA`. Too many hosts expect their plugins to respond + to midi messages for things like pedal and volume. (#1054) +- The LV2 plugin now has a string output parameter + with the CC used by the instrument (#1060) + +#### Fixed + +- Corrected a crash in some VST3 hosts (#1035) + +## [1.1.1] - 2021-11-08 [YANKED] + +### Fixed + +- Roll back VSTGUI to 4.10.2 to avoid corruption of the UI in some scenarios + +## [1.1.0] - 2021-10-23 + +### Library + +#### Added + +- Added support for `hint_sustain_cancels_release`, which when activated + reproduces a LinuxSampler behavior by sustaining already released notes (#898). + A GUI option should come later. +- Added support for `amp_veltrack_oncc`, `pitch_veltrack_oncc`, + and `fil_veltrack_oncc` (#938) +- Dynamic EGs are supported for flex EGs (#933 #932 #930) +- Support `_curvecc` for extended CCs (#993) +- Support `lorand` and `hirand` on CC-triggered regions (#997) +- The engine now parses `oscillator=auto` and only enables oscillators + on short samples with wavetable tags in the WAV metadata (#954) +- Added a polyphony option to `sfizz_render` (#992) +- Added a text interface to `sfizz_jack` (#973) + +#### Changed + +- Filter resonance can now be negative (#919) +- The JACK and PureData clients can use custom importers (#926) +- Polyphony groups can have negative indices (#934) +- Parse `lokey=-1` as `hikey=-1` (#941) +- Since the default build of the LV2 plugin is not statically linked against + libsndfile, the distribution license is changed to the ISC license (#943) + +#### Fixed + +- Fixed a bug which could prevent parameters to be automated in Ardour (#893) +- Fixed a bug where high definition CC, + crossfade or aftertouch events could be ignored (#855 #896) +- Resetting all controllers will now reset them to their default, + instead of 0 (#905) +- Fix `off_by` behavior with long-releasing notes (#972) +- Improved the preloading for monolithic wave files (#935) +- The DecentSampler importer now better translates the loop modes (#981) +- CC-triggered voices can be cut off by polyphony groups (#917) +- Allow building on MacOS 10.9 (#990) +- Corrected a bug where octave and note offset + were not reset upon loading a new file (#994) +- Corrected a bug where CC-triggered voices could generate note-offs + when cut by a polyphony group (#998) +- Added man pages for `sfizz_render` and `sfizz_jack`, plus jack client fix (#999) +- Various build system improvements for different platforms + (#957 #958 #964 #967 #968 #989 #978 #942 #921 #920 #902 #849 #850 #856 #1001) + +### UI + +#### Added + +- Added a GUI volume meter (#859) +- It is now possible to enter values for CCs on the plugin GUI + by double clicking on the CC knob (#945). + A high-resolution input option is also available on a right click. + +#### Fixed + +- Respect the editor set for sfz files on MacOS (#925) + +### Plugins + +#### Added + +- Added a Puredata plugin (#869 #873 #874) +- Implement support for VST3 keyswitch and midi note names (#877 #876) + +#### Fixed + +- Corrected a bug for the LV2 plugin when used in the Ardour DAW (#924) +- Even when plugin-side automation is disabled (through `SFIZZ_LV2_PSA`), + sustain and sostenuto CC events will still go through to the plugin to improve + the user experience on hosts that do not support automatic mapping of control + ports to midi events through the LV2 `midi:binding` extension (#963). +- Fixed a bug that could hang the background thread in the VST plugin + (#984 #865 #864) +- The current keyswitch is properly saved and restored with the plugin state (#961) +- Improved the X11 runloop (#986) +- Corrected a bug which prevented VST3 or LV2 plugins + to be loaded by the Ardour DAW (#995) + +## [1.0.0] - 2021-04-16 + +- SFZ v1 is virtually supported except for a handful of opcodes. + Please check https://sfz.tools/sfizz/development/status/ + for the up-to-date status of opcode support. +- It is now possible to build sfizz without relying on libsndfile, + using a set of libraries. This is now the default build mode. + Building with libsndfile can be enabled at configure time. +- The library and plugins can now load DecentSampler files, + and could accomodate other formats. +- CCs, keyswitch range, key ranges and active keyswitch + are now displayed in the editor for all plugins. + There has been a lot of UI work to make it more practical to use. +- There is an OSC interface in the library, which allows to have introspection + into the currently loaded file, the state of CCs/keyswitches, + and also set some parameters for loaded regions. + +### Library + +#### Added + +- Added documentation in the API calls to make clear + that sample-accurate triggering of voice requires + messages to be sent in order. The VST plugin in particular + did not respect this and has been updated. +- There are new HD (float) versions of API calls for note and CC events. +- Added an info panel in the plugin UI + (#793 #792 #791 #789 #788 #787 #782 #779 #773) +- Support DecentSampler and a generic importer for other formats (#725 #715 #680) +- Add high-definition versions of the API calls (#820) +- Support polyphonic aftertouch events, triggers and targets (#809 #764) +- Support monophonic aftertouch events, triggers and targets (#765 #630 ) +- It is now possible to change some parameters of the regions + after loading the file through the OSC interface (#761) +- Added modulation targets for amplitude, pan, width, position, pitch and volume (#760) +- Support CC modifiers for `end`, `loop_start` and `loop_end` (#752) +- Support CC modifiers for LFO targets (#753) +- Support SFZ v1 extended CCs (#747) +- Implement the sostenuto logic (#739) +- Added CC modulation for `depth` targets (#730) +- Implement v1 LFOs (#693) +- Support `count` and `loop_count` (#654) +- Support `delay_oncc` (#653) +- Support `sw_vel` (#650) +- Added windowed sinc interpolation for `sample_quality` from 3 to 10 (#609) +- Support `lfoN_beats` (#553) +- Add region and voice introspection to the OSC interface (#555) +- Add an alternative to libsndfile (#482 #534 #535 #536) +- Support disjoint keyswitch ranges, as well as `sw_lolast` and `sw_hilast` (#526) + +#### Changed + +- The ABI is broken, leading to a major version change. +- Thread-safety now has to be handled externally. + The API has been updated to provide a concurrency spec. +- The current version of the internal oversampling factor was too unstable + and has been disabled for the time being. The API and plugin parameters + are staying for compatibility. A more robust implementation should come soon. +- A new `bpmTempo` method has been added to pass tempo as beats-per-minute + rather than seconds per beat. + The previous version is deprecated. +- `aftertouch` is renamed as `channel_aftertouch` (for C) + and `channelAftertouch` (for C++) throughout the API + to be more consistent with `polyAftertouch`. +- The C++ standard is set to 14 if you build the UI on LV2, + or if you build the VST version. This will likely increase to 17 shortly + due to the dependency on VSTGUI. The ui-less LV2 plugin and the library + remain C++11 compatible. + Note that windows builds already use C++17 on all targets. (#795) +- The library uses an Hermite interpolation by default (#828 #829) +- Corrected a bug where regions with long release envelopes + would not stop on sample ending (#811) +- Update abseil and ghc::filesystem (#780), the tunings library from Surge + (#785), catch2 (#711) +- Improve the choking logic to match the spec and other implementations (#778) +- Refactored opcode reading and storing (#748 #727 #721 #722 #720 #700 #559) +- Filter/EQ parameters are clamped even with modulation (#701 #699) +- Delay envelope start by the voice delay (#655 #432) +- Allow parsing of some badly formed files files (#600) +- Match ARIA's LFOs (#613) +- Improve the Hermite interpolation (#597) +- Enable fast-math on MSVC (#567) + +#### Fixed + +- Fix an error where sample indices could wrap for massive pitch modulations + (#825 #751) +- Small files with only zeroes are considered as `*silence` (#831) +- Corrected a regression where sfizz would stop loading the SFZ file + if some sample files cannot be loaded (#806) +- Fix the parser to support sharp (#) symbols in file names (#746) +- Improved the ADSR transitions from decay to sustain, and from release to 0 (#734 #373) +- Reset the smoothers if the playhead moved (#731 #710 #382) +- Corrected a bug where the first sample was ignored on playback (#732 #394) +- If the sustain level is low enough the envelope is set to free-run (#723, #676) +- Fix loop crossfades when the loop starts early in the sample (#718) +- Fix random generators to match ARIA's behavior (#719) +- Fix corruption appearing in some sample libraries (#717 #695) +- Smoothers are now linear (#713 #714 #703) +- Fix a bug where the sample rate was not propagated properly to the flex EGs (#683) +- Fix `note_polyphony` choking (#634 #529) +- Respect the `end` opcode (#618) +- Fix allocations that may happen on the RT thread (#621) +- Fix the polyphony manager when the engine polyphony is changed + after a file is loaded (#619) +- Avoid reloading invalid files (#614) +- Update AtomicQueue to correct a bug (#583) +- Fix crashes when building with libsndfile (#542 #543) +- Properly pad and align the wav files upon reading (#538) +- Handle gracefully having a different `sw_default` in a region (#531) + +### UI + +#### Added + +- UI improvements and tweaks, adding themability in particular (#826 #824 #822 + #821 #819 #818 #816 #812 #799 #743 #691 #674 #673 #672 #670 #662 #661 #656) +- Show the current keyswitch on the editor and the virtual keybnoard (#665 #657) +- Added a button to reset the scala file to default (#660) +- The plugins now display an image through the `image` opcode (#770) + +#### Changed + +- Improve font handling (#669 #622) +- Improve file handling in the UI (#645 #659 #658) + and enable the use of a "default user directory" or environment variable. + This also allows the plugin to "find" missing sfz files + in a different environment (#644 #581 #572 #532) +- Update VST to 3.7.2 (#798) + +### Plugins + +#### Added + +- Added the option to build a VST2 version of sfizz + if you have the SDK available (#708) + +#### Changed + +- Order VST events (#810 #803) + +#### Fixed + +- Fix corruptions and crashes that happened in the LV2 plugin (#794 #668 #557) +- The internal controllers of sfizz are now presented as parameters + in the LV2 plugin. Experimental midi binding is also active on them. By default + the "direct" midi input is disabled and you need to map to the parameters. + A compile-time option is available to re-enable the midi input. (#776) +- Fix crashes in the VST plugin (#709 #702 #681 #625) +- Enable checking if file changed even when the transport is not running (#712 #689) +- The Mac builds are now signed (#550 #552) + +#### Removed + +- Remove automation from un-automatable parameters in the VST plugin (#814) + +## [0.5.1] - 2020-10-25 [YANKED] + +### Fixed + +- Corrected race conditions that appeared with the new thread and file pools + (#507 #508 #514 #521) +- Take the internal oversampling factor into account for loop points, + and solve an issue where loop points specified in sfz files + were not taken into account (#506) +- Fix an implementation error for the internal hash function + when applied on a single byte (#512) +- Knobs are linear in the AU plugin (#517) +- Fix a crash in VSTGUI (#520) +- Fix the resource path in the LV2 plugin under windows (#524) +- Add MacOS make install rules (#525) + +## [0.5.0] - 2020-10-15 + +### Major improvements + +#### Added + +- Added basic support for Flex EGs (#388) as modulation sources (targets to come) +- Added basic support for LFOs (#338) as modulation sources (targets to come) +- EGs and LFOs can now target EQs and filters (#424) +- A new GUI has been added and is common to the LV2 and VST plugin + (#397 #404 #419 #489 #492 #495 #496 #497) + still mostly work in progress, more to come! +- Provided build systems to use sfizz with the VCV-Rack SDK + and the DISTRHO Plugin Framework + +### Library + +#### Added + +- Added API support for setting the playback state, + time position and signature (#354) +- The API documentation on the sfizz's website has been streamlined alot ! +- Added support for `sustain_lo` (#327) +- Audio files are now read incrementally, improving the availability under load (#294) +- Added support for effect types `reverb`, `disto`, `gate` and `comp` +- Added support for `off_time` and complete support for `off_mode`. + The voice stealing logic was improved to take into account `polyphony`, + `note_polyphony` and `group_polyphony` properly (#349 #352 #393 #413 #414 #467). + Note that this support is also available for the engine polyphony. + In this case, some additional voice will take over for the release duration (#477). +- Support for `offset_cc` (#385) +- `sfizz_render` now supports a `--quality` switch, + which acts like the `sample_quality` opcode (#408) +- `pitch_keycenter=sample` is now taken into account (#362) +- Support `oscillator_detunecc` (#434) +- Support basic FM synthesis for oscillator regions (#436) +- Support `hint_ram_loading` for loading the whole samples in RAM (#353) +- Support for `loop_crossfade` (#464) +- Improve the filter shortcut path (#336) + +#### Changed + +- The voice stealing is now configurable using `hint_stealing`, + with possible values as `first`, `oldest` (default), and `envelope_and_age`. + The latter is the more CPU-consuming version which requires to follow + the envelope of each voice to kill low-volume ones preferably. + Note that the voice stealing continue to kill all voices + started on the same event by default (i.e. all layers of the same note). (#344 #384 #353) +- `sfizz` now internally uses a modulation matrix to link + all modulation sources (CC, LFOs, and EGs) and targets (#335 #351 #386) +- The wavetable quality has been improved (#347) +- Loading probable wavetable files, or wav files containing wavetable metadata + now sets `oscillator=on` on the region (#431) +- The default `sample_quality` was put back to 1 for live playing and 2 for freewheeling (#405) +- Improve the file loading logic to keep files in memory + for a short while in case they get reused (#425) +- Improved the release logic in many cases (#324 #414 #413) +- Set the level of the `*noise` generator to match ARIA's (#429) + +#### Fixed + +- CC 7, 10 and 11 are now linked by default to pan, volume and expression (#475) +- All phase-related opcodes in sfizz now use the 0->1 convention, + as does ARIA/Sforzando, instead of the 0->360 convention (#499) +- Fix an unwanted copy in the realtime thread (#334) +- Fix the default `ampeg_attack` and `ampeg_release` to avoid clicks (#437) +- Corrected a race condition in freewheeling mode (#500) +- Fixed a potential non-realtime operation in the realtime thread (#498) +- Fix a bug when using a larger internal oversampling for regions + with an `offset` value (#469) +- Fix an issue when loops occured more than once in a block (#462) +- Increase the range of the clamping on amplitude (#468) and pitch (#474) +- Fix CC modulations with their source depth set to 0 (#475) +- Fix an overshoot for crazily large cutoff values (#478); cutoffs are now clamped +- Fix the MIDNAM output for the case where extended CCs are used (#420) +- Fixed a bug where release voices where not ignored on self-mask search (#348) +- Fixed `amp_veltrack` behavior (#371) +- Fix the ADSRH envelope release rate (#376) +- Fixed an error for files where the loop spans the entire file (#378) +- Fixed `sustain_cc` behavior (#377) +- Match the default volumes with ARIA (#381) +- Properly set the `loop_mode` for release regions (#379) +- Regions with `end=0` are now properly disabled (#380) +- Fix `fil_random` to be bipolar (#452) +- The `sequence` order now properly starts at 1 (#451) +- Fix an issue on Flush to Zero on some ARM platforms (#455) +- Fix `pitch_veltrack` (#461) +- Opcode values now properly stop at the `<` character (#439) +- Fix various build errors and issues on all platforms + (#345 #401 #400 #399 #417 #447 #449 #443 #453 #456 #459 #471 #484 #487 #488 #491) +- Existing and known CC values are now correctly taken into account for modulations (#421) +- Fix various performance regressions and improved performance + especially on ARM builds (#410 #412 #415 #426) + +### UI + +#### Added + +- The file dialog initial directory is now the root of the current loaded file (#428) + +### Plugins + +#### Added + +- A new output port for active voices has been added in the LV2 plugin (#321) + +#### Fixed + +- Support for `atom:Blank` atoms in the LV2 plugin (#363) + +## [0.4.0] - 2020-07-24 + +### Major improvements + +#### Added + +- Added support for polynomial resamples and `sample_quality` opcodes (#238 #267). + The engine now defaults to a value of `2` for this opcode, + which is more intensive than the original linear interpolation resampler + but provides a better quality. Added support for better resampling algorithms + also in the wavetables via `oscillator_quality` (#287). +- Support `_curvecc` and `_stepcc` opcodes (#166 #155 #77) + as well as `_smoothcc` opcodes (#181 #48 #22 #153 #297 #285) +- Added support and API for Scala tuning files in the engine and the plugins + (#253 #268 #282) + +### Library + +#### Added + +- Added API calls to set `$variable` define values prior to loading an SFZ file + (#168 #119 #130) +- Added API calls to get key labels and cc labels + defined by `label_key` and `label_cc` (#174) +- Added an API call to load an sfz file as an `std::string` or `const char*` (#217) +- Added API calls for Scala files and tunings (#253) +- Added high-definition floating point CC API calls (#244) +- Added API calls to change the default resampling quality (#267 #238) +- Added support for unison oscillators (#161) +- Support for the `polyphony` opcode at all levels (#171 #275), + as well as `note_polyphony`. The `group=` polyphony is also more flexible + and can be defined anywhere. +- Added support for `offset_cc` (#170 #159) +- Added support for `direction=reverse` (#185 #179) +- Added support to label the keys using a `label_key` opcode. + This is not really standard yet, but it is now integrated in the LV2 plugin + to advertise the names in the MIDNAM file and possibly change their labels + in hosts that support it. (#174 #154) +- Added support for block comments `/* */` in the parser (#196 #195) +- Added a `sfizz_render` client in tree; you can build it with the make target + `sfizz_render` if the `SFIZZ_RENDER` CMake variable is set to `ON`. (#200 #201 #206) +- Add support to integrate sfizz in DPF plugins (#216) +- Added support for the `set_hdcc` opcodes and overall added the ability + to support floating-point CCs from the API (#233 #232 #244) +- Added support for FLAC loops (#242 #229) +- Add headers and group sources in the CMake project for integration with e.g. Qt (#312) +- Support flat notes parsed as string values (#291 #289) + +#### Fixed + +- Solved some issues with DSmolken's drumkits related to the ampeg envelope (#172) +- An exception problem was thrown if an sfz file was deleted (#182 #184) +- Improved the filter stability (#198 #199 #210) +- Handle `USE_LIBCPP` properly on configure (#203) +- Fix the handling of loop markers if sample `end=` is present (#202 #204) +- Handle note on with 0 velocity as note offs in the jack client (#208 #211) +- Solved an issue with super short files (#215) +- Fixed some parsing issues with `$variables` (#230) +- Process `$` expansions in `#include` (#247) +- Change the default build type to `RelWithDebInfo` (#249) +- Improve the note stealing algorithm (#214); + note that this is still very much a work in progress since many heuristics + are in play here. Feel free to report misbehavior + regarding note stealing as we improve this regularly. +- Corrected a bug with SFZ v1 `velcurve` (#263) +- Properly support the `off_by=-1` opcode to correctly reset the value. (#235) +- Ignore garbage values following e.g. a key number in opcode values + (as in `key=64Garbage` -> `key=64`) (#263) +- `ampeg_****_onccXX` modifiers now properly consider multiple CC modifiers + (#300 #167) +- Trigger on CC does not require disabling the key triggering through e.g. `key=-1` (#315) +- Improved handling of `release_key` (#298); still not perfect, if the region + spans multiple key and multiple notes happened with the pedal down, + only a single voice will start. +- Corrected a parsing issue when `$variables` were part of an opcode name (#328) +- Various other plumbing changes + +### Plugins + +#### Added + +- Added an AudioUnit target (#224) +- Support the `mapPath` feature of the LV2 specifications, + for tentatively better portability in plugin states (#303) +- New instances of the sfizz LV2 plugin will now load a default + `*sine` instrument (#283) + +#### Fixed + +- Properly bundle the `dylib` for macOS (#188) +- Corrected a stack smashing bug in the LV2 plugin (#226) +- Properly advertise the VST plugin parameters (#241) +- Corrected some errors with null-terminated atoms in the LV2 plugin (#269) +- Properly read the LV2 option list until the end (#323, by @atsushieno) + +## [0.3.2] - 2020-04-03 + +### Added + +- Added an experimental support for `make uninstall` (#118, #120) +- Add the autopan (#105), width, rectifier, gain, limiter (#131), + and string resonator (#143) effects +- Curves are now registered within the synth but cannot be referenced yet (#96) +- Added a "panic button" API that kills voices (#122) +- Added support for more generators using wavetables (#61) +- Added support for the `oscillator` opcode, to create generators from files (#128) +- Added support for `note_polyphony`, `polyphony`, and `note_selfmask` (#142) +- Added support for `pitch_cc` and `tune_cc` modifiers (#142) + +### Changed + +- sfizz now builds down to gcc-4.9 with stricter C++11 compliance. + The main release builds use C++17 mode on newer compilers (#111, #110) +- Upstream libraries updates (abseil, filesystem and atomic_queue) (#121) +- The logging API can be used to set a log filename (a6cbb48) +- Reworked the parser; the new one is more efficient, + and can indicate error/warning ranges (#130) +- The VST plugin now reloads the file automatically, like the LV2 plugin (#139) +- The max number of CCs was increased to 512, + to accomodate some libraries that use cc300 modifiers. +- The engine uses floating point values internally for midi events (#137); + this prepares it for high-resolution midi down the line. +- The modifier support was overhauled; + all regions can now have multiple CCs modifying the same target (#142). +- Improved performance of the amplitude stage gain of the rendering process (#145) +- The VST3 are now a submodule; more architecture targets have been added + (#158, #147, patch proposed by @hexdump0815) + +### Fixed + +- Corrected a bug where the VST plugin got recreated needlessly in some hosts (#122) +- Corrected a potential overflow for CC names (930bfdf) +- Generators using wavetables are now correctly tuned (#126) +- The stereo panning stage of the process was corrected; + width is now set to 100% by default as it should, + and panning is properly applied (1faa7f, b55171, #133) +- Corrected errors in the performance report script related to display values + (file names and histogram range) +- Fixes some realtime synchronization issues in the VST (#140) +- Corrected bugs and differences with Cakewalk/ARIA in the ADSR envelope (#136, #129) + +## [0.3.1] - 2020-03-14 + +### Added + +- Added a VST3 plug-in front-end to the library. It is still quite experimental + and suffers from problems that stem from the VST3 SDK itself. (#99) +- Added effect buses and processing. There is a "lofi" effect available for now, + as well as the same filters and EQs you can apply on the regions. + More will come soon! (#84) +- Added a script to parse and render the timings. + This can help tracking performance issues and regressions. (#89) + +### Fixed + +- Various fixups, performance improvements, and CI updates. + +## [0.3.0] - 2020-02-29 + +### Added + +- Added filter and EQ handling (the `filN_...` and `eqN_...` opcodes). + There are also no limits to the amount of filters and EQs you can slap + on each region beyond your CPU. Most if not all of the relevant filter types + from the SFZ v2 spec are supported. +- Added a new command-line option for the JACK client + to set the client's name (#75, #76). +- Added initial MIDNAM support (#79). + The MIDNAM shows the named CCs in the SFZ file for now. +- Added fine timings within the callbacks for performance improvements + and regression testing (#65). +- Added a crude `*noise` generator. This generator is a bit expensive + for what it does but it's mostly useful to test the filters. + +### Changed + +- Reworked the parsing code for faster dispatching and better handling + of complex opcodes with multiple parameters in their opcode name (#40). +- Reworked the panning and stereo image process. + The new process uses tabulated functions and avoid expensive calls + to compute sine and cosine functions (#47, #56). + +### Fixed + +- Corrected a bug with Ardour where saving a session with no file loaded + would crash on reopening. +- Corrected a bug where voices triggered on key off would never end + and fill up the polyphony (#63). +- Improved and completed CI on all platforms. + +## [v0.2.0] - 2020-01-30 + +First version not depending on SFZero / JUCE, +becoming a library to be used in other projects, plus a LV2 plugin. + +### Added + +- Added an LV2 plugin version. +- Added support for pitch bends (#6) + as well as pitch-bend activation for regions (`lobend` and `hibend` opcodes). +- Added dynamic updates for the current modifiers + (panning, stereo image, volume and amplitude mainly) (#19, #28) +- Added timing for callbacks and file loading times. +- Added a windows build process for both the shared library and the LV2. + `sfizz` now builds on all major platforms. + +### Changed + +- The parser now falls back to case-insensitive search if it doesn't find + the sample file in its current path (#28), + so that the behavior of SFZ libraries on case-sensitive filesystems + will match Windows and macOS default case-insensitive filesystems. +- The file now reload automatically on file change, + and you can force a reload if necessary (#17). + +### Fixed + +- Corrected a bug where memory would be read past the end of the file in memory, + generating artifacts. +- Corrected a bug where the real-time queue handling background loading + of the voices would fail spuriously. +- Corrected a bug where in the LV2 plugin the unknown opcode list was truncated (#18). +- The JACK client will warn you instead of crashing + if you do not give it a file to load (#27). + +## [v0.1.0] - 2019-05-30 + +- Initial release, based on SFZero and JUCE. + + +[1]: https://keepachangelog.com/en/1.0.0/ +[2]: https://semver.org/spec/v2.0.0.html + +[Unreleased]: https://github.com/sfztools/sfizz/compare/1.2.1...HEAD +[1.2.1]: https://github.com/sfztools/sfizz/compare/1.2.0...1.2.1 +[1.2.0]: https://github.com/sfztools/sfizz/compare/1.1.1...1.2.0 +[1.1.1]: https://github.com/sfztools/sfizz/compare/1.1.0...1.1.1 +[1.1.0]: https://github.com/sfztools/sfizz/compare/1.0.0...1.1.0 +[1.0.0]: https://github.com/sfztools/sfizz/compare/0.5.1...1.0.0 +[0.5.1]: https://github.com/sfztools/sfizz/compare/0.5.0...0.5.1 +[0.5.0]: https://github.com/sfztools/sfizz/compare/0.4.0...0.5.0 +[0.4.0]: https://github.com/sfztools/sfizz/compare/0.3.2...0.4.0 +[0.3.2]: https://github.com/sfztools/sfizz/compare/0.3.1...0.3.2 +[0.3.1]: https://github.com/sfztools/sfizz/compare/0.3.0...0.3.1 +[0.3.0]: https://github.com/sfztools/sfizz/compare/v0.2.0...0.3.0 +[v0.2.0]: https://github.com/sfztools/sfizz/commits/v0.2.0 +[v0.1.0]: https://github.com/sfztools/sfizz-juce/releases/tag/v0.1.0-beta.2 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..13ae0f6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +if (WIN32) + cmake_minimum_required (VERSION 3.15) + cmake_policy(SET CMP0091 NEW) +else() + # FIXME: The old JPC fork of mod-plugin-builder image used in CI requires 3.5. + # The current upstream one needs a different configuration. + # Minimum required to use SFIZZ_USE_SYSTEM_ABSEIL is 3.11, + # see library/cmake/SfizzDeps.cmake at line 70. + cmake_minimum_required(VERSION 3.11) +endif() + +project (sfizz VERSION 1.2.2 LANGUAGES CXX C) +set (PROJECT_DESCRIPTION + "SFZ based sampler, providing AU, LV2, PureData and VST plugins." +) +# External configuration CMake scripts +set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};" + "${CMAKE_CURRENT_SOURCE_DIR}/library/cmake;" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" +) +include(BuildType) +include(OptionEx) + +option_ex(SFIZZ_AU "Enable AU plug-in build" APPLE) +option_ex(SFIZZ_LV2 "Enable LV2 plug-in build" ON) +option_ex(SFIZZ_LV2_UI "Enable LV2 plug-in user interface" ON) +option_ex(SFIZZ_PUREDATA "Enable Puredata plug-in build" OFF) +option_ex(SFIZZ_USE_SYSTEM_LV2 "Use LV2 headers preinstalled on system" OFF) +option_ex(SFIZZ_USE_SYSTEM_VST3SDK "Use VST3SDK source files preinstalled on system" OFF) +option_ex(SFIZZ_VST2 "Enable VST2 plug-in build (unsupported)" OFF) +option_ex(SFIZZ_VST "Enable VST plug-in build" ON) + +set(MIDI_CC_MAX 512 CACHE STRING "Maximum amount of Control Change Messages") + +include(SfizzConfig) # Re-used for this project +include(BundleDylibs) + +add_subdirectory(library) +add_subdirectory(plugins) + +# Windows installer +if(WIN32) + include(VSTConfig) + configure_file(${PROJECT_SOURCE_DIR}/scripts/innosetup.iss.in + ${PROJECT_BINARY_DIR}/innosetup.iss @ONLY) +endif() + +# Put it at the end so that the vst/lv2 directories are registered +if (NOT MSVC) + include(PluginsUninstall) +endif() + +show_build_info_if_needed() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5f3823e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,223 @@ +# Contributing to sfizz + +Thanks for considering contributing! +There is actually many things anyone can do, programming-related or music-related. + +## Using the software for your projects + +One of the best and probably simplest thing to do is simply to use sfizz in a number of situations. +There is only so much we can test as far as sfz libraries go, or differents hosts for the plugin versions. +If during the course of your usage you find out that sfizz does not behave as it should with respect to what the SFZ spec dictates, or what other players do, please file in a bug reports. +If there is some feature that would be fantastic for you, feel free to post a feature requests in the issues. +This will help us track and remember everything. +Refer to (#filing-bugs-and-feature-requests) for more precision. + +### Filing bugs and feature requests + +When filing a bug, try to be as precise as possible. +Obviously bugs that are easily reproducible are easier to solve. + +- If sfizz fails to play a library correctly, please give us a link to the library and ideally an example of what you except. It helps **tremendously** if you can provide a *minimal* example in the form of a simple custom sfz library that pinpoints the problem exactly, along with instruction or midi files to render. +- If the plugins crashes, please try to reproduce and detail everything you did up to the crash, as well as the host and sfz library. It can be really helpful to provide session files, again with a minimal example. +- If the behavior of sfizz is clumsy and annoying, and the user experience feels subpar, please tell us and try to propose some solutions on how it could be improved. As developers, we are viewing and using sfizz in specific ways which may actually be quite far from what users would like, but we're all ears :) + +For feature requests, provide details about the expected behavior and possibly example sfz files that will benefit from the new feature. +Note that we aim at implementing the sfz spec as a whole, and possibly some extensions by e.g. ARIA so normally all sfz features should come at some point. + +### Using the C or C++ library + +Sfizz is liberally licensed and you can freely use it in any project under the term of its license and its dependencies. +We provide a somewhat simple C and C++ API that covers our use cases within plugins mostly, and should be useful. +The API is documented over at the [api] page. +You can find example of using the C API in the `lv2` directory which contains sfizz's LV2 plugin. +You can find example of using the C++ API in the `vst3` directory which contains sfizz's VST3 plugin. +In both cases sfizz is built in the plugins as a static library. +The project [sfizz-render] illustrates how one might use sfizz as a shared library through a `make`- and `pkg-config`-based process. +Other projects that bundle or use sfizz are: + +- (https://github.com/sfztools/beatbox): a live drum machine + +If you use the library, maybe you will have specific needs and requests that could be useful to you. +Be sure to post an issue for this so we can improve the API, especially because we have to queue API changes so it stays a bit stable over time. + +## Developing for sfizz + +We welcome all code contributions to sfizz, whatever your coding level. +If there is a specific feature you would like, and you are willing to put in the time (and have some form of initial background in modern C++ code), we can mentor you and provide breadcrumbs through the codebase. +Sfizz is still very much alpha, and we're refactoring large parts of the code on a regular basis. +We try to keep the public API documented, and most of the internal APIs are also documented, but this can be in flux from time to time. +If you want to add things we encourage you to ask around through an issue, on Discord, IRC, or by email, so that we may be able to guide you and discuss about how and what to implement. + +### Handling the repository and its submodules + +Sfizz uses a number of submodules, namely abseil and the VST sdk. +These submodules are purely consumed by sfizz, and the maintainers will manage updating them when needed. +As such, they should *not* pose too much issues. +They are all pinned to specific branches or commits in their original repository. + +To clone the sfizz repository, the recommended way is to do +```bash +git clone https://github.com/sfztools/sfizz.git --recursive +# Other possibility +git clone https://github.com/sfztools/sfizz.git +cd sfizz +git submodule update --init +``` + +If after some time you happen to see something like this: +``` +❯ git status +On branch develop +Your branch is up to date with 'origin/develop'. + +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git checkout -- ..." to discard changes in working directory) + + modified: external/abseil-cpp (new commits) + +no changes added to commit (use "git add" and/or "git commit -a") +``` +Then it means that the abseil submodule *on your disk* differs from the one expected by sfizz. +This usually happens if i) you modified one of abseil's files, in which case the repository will be dirty, or ii) sfizz updated abseil's and pinned it to another commit. +Assuming you were not adding relevant changes to abseil in order to push them upstream, you can usually resolve this issue through +```bash +git submodule update +``` +**All changes in the submodule will be lost**. +If you were in fact modifying abseil for some reason, then consider either pushing your changes upstream or find an alternative. +Same goes for the VST SDK. + +### Building sfizz from source + +For details on the dependencies and platform-specific information for building sfizz, please refer to the [build] page. +Note that for development you will probably want all dependencies, which means using debian's lingo: + +- `cmake` +- `g++` or `clang++` +- `clang-format` +- `libjack-jackd2-dev` +- `libsndfile-dev` + +We also recommend building [benchmark] from source and installing it as a static library. +You will also want to configure using the `-DSFIZZ_TESTS=ON` flag and `-DSFIZZ_BENCHMARKS=ON` flag to be able to run the tests and possibly the micro-benchmarks. + +### Forking and creating pull requests + +We do development on the git `develop` branch, and distribute using the `master` branch. +We mostly avoid directly committing to the `develop` branch and go through pull requests and issues for development-related discussion and choices. +Try to have public and private discussion about development, and if such discussion happen on IRC or Discord, try to sum them up in a pull request or an issue that we can then refer to. +Obviously, light technical questions can be asked freely in private, but the goal is to open the discussion for everyone to see. + +To start contributing, we encourage you to fork the main sfizz repository on your personal account and clone it locally on your machine +In the clone of your fork, and add the `sfztools` repository as a remote as follows +```bash +# Clone your own fork in a specific directory +git clone https://github.com/MY_GITHUB_NAME/sfizz.git sfizz-fork +cd sfizz-fork +# Add the upstream sfztools remote +git remote add upstream https://github.com/sfztools/sfizz.git +``` +Once done, the contribution process is as follows: + +1. Fetch the latest changes from the `upstream` remote, in particular the `develop` branch. +2. Checkout the `upstream/develop` branch, and immediately checkout to a new local branch to start adding changes +3. Add commits to your local branch +4. Push the local branch to your own fork +5. Go back to Github web interface and create a pull request + +In command language, this translates to +```bash +# Fetch the latest status from upstrea +git fetch upstream +# Checkout the upstream develop branch +git checkout upstream/develop +# Create a new local branch +git checkout -b my-new-contribution +# Do things as usual for git: git add, git commit +# ... +# Once ready, push the branch to your fork. Github will tell you where to go for a pull request from there. +git push --set-upstream origin my-new-contribution +``` + +### Adding files to the build system + +We use CMake for the build system. +For the main sfizz library, the main file is `src/CMakeLists.txt`. +In particular if you add a new `.cpp` file to sfizz, you should add it to the `SFIZZ_SOURCES` list in this file so it gets compiled and linked with the rest of the library. + +### Coding style and guidelines + +We have continuous integration targets down to gcc 4.9, so the code has to be fully C++11 compliant. +Sadly this means some bonuses from C++14 are not available, although `absl` compensates many things from a standard library point of view. +All modern C++ guidelines apply, as well as rules related to audio programming: + +- Raw pointers are non-owning observers; use `unique_ptr` and `shared_ptr` for owning references, and `absl::optional` for nullable/optional/maybe values returned from functions. +- Use move semantics when appropriate. +- Try to use [RAII] wrappers and write them if necessary. +- Avoid allocating memory, calling functions that throw, using system calls, or any launching any blocking operation in the audio thread; for sfizz, that means anything that is called at some point from `Synth::renderBlock` and `Synth::noteOn`, `noteOff`, ... and all midi events. +- We have a helper class that helps us to track allocation in the form of the `sfz::Buffer` class and the `sfz::AudioBuffer` class, so use it for heap memory in lieu of naked `new` or `unique_ptr` when relevant. +- Most things in the [CppCoreGuidelines] are good to take, although be careful as many proposed things may break the *no allocation* rule. + +We also have a `clang-tidy` CI step that may annoy you from time to time. + +We have a `clang-format` coding style in that is loosely based on the WebKit style. +We encourage you to use it, as well as `git-clang-format` to format your PR. +If possible, find a plugin for your text editor than handles `.editorconfig` files. Most editors have one. +Some notes : + +- Class and type names start by a capital letter and follow the `PascalCase`, function names and variables in the C++ part follow the `camelCase`, and function names and variables in the C part follows a `snake_case` to blend with the surroundings. +- Try to keep names meaningful, and code clear if possible. +- Comment your classes APIs, in a terse and clear way. Comments inside the code are usually not needed *if the code is written clearly with meaningful names*, but if you feel that it's needed please do. +- You can use `_` in front of private variables, but we do not have a consistent naming for these yet. + +All these rules are subject to change upon discussion and integration. + +### Testing your changes + +To build and run the tests, you need to have configured sfizz with the `-DSFIZZ_TESTS=ON` flag. +You can then use the `make sfizz_tests` command from your build directory. +The tests are located in `tests/sfizz_tests` from the same build directory. + +Note that these tests mostly cover the parsing and observable invariants of sfizz, as well as all helper methods as much as possible. +The processing and rendering is usually harder to test as it produces alot of data. +We are in the process of making a testing platform using rendered wave files, but it is not yet here. +If you write anything testable however, we encourage you to write the tests. +We use the [Catch] library for testing, and you can check out the files in `tests/` for examples on how to add some. + +To actually try your changes, you can use the `clients/sfizz_jack` client to instantiate a JACK application, or use `jalv` for a similar purpose by launching from your build directory +```bash +env LV2_PATH=$PWD jalv.gtk3 http://sfztools.github.io/sfizz +``` +Similarly, you can launch e.g. Ardour with suitable reference to your version of the sfizz LV2 plugin with +```bash +env LV2_PATH=$PWD Ardour6 +``` + +### Running micro-benchmarks + +For micro-optimization purposes we have a number of benchmarks written in `benchmarks/`, build against the [benchmark] library. +If you wonder about the performance of something, feel free to write some too and add them to the build system. +We tend to name the benchmarks with the `bm_` prefix. +It can be as simple as adding a line +```cmake +sfizz_add_benchmark(bm_wavfile BM_wavfile.cpp) +``` +to the `benchmarks/CMakeLists.txt` file but you might need to link to other libraries depending on what you are benchmarking. +The `CMakeLists.txt` file contains example that you can copy and try. + +### Running in-use benchmarks + +We have logging facilities for the processing inside of sfizz, that we use to track how the additions impact the rendering speed. +For now the usage is a bit hand-made, as it requires the [sfizz-render] program and manually pre-loading the development `.so` library. +The logger outputs CSV files for the rendering and file-loading processes. +The file `scripts/performance_report.py` is made to process and produce a nice interactive report from this data. + +[build]: https://sfz.tools/sfizz/development/build/ +[benchmark]: https://github.com/google/benchmark +[sfizz-render]: https://github.com/sfztools/sfizz-render +[GOVERNANCE]: GOVERNANCE.md +[api]: https://sfz.tools/sfizz/api/ +[RAII]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-resource +[CppCoreGuidelines]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines +[Catch]: https://github.com/catchorg/Catch2 diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..f80ae43 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +open_collective: sfztools diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..d809b00 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,56 @@ +## Leadership + +The current maintainers of sfizz and the sfz tools organization are : + +- Paul "@paulfd" Ferrand +- Jean-Pierre "@jpcima" Cimalando +- Andrea "@redtide" Zanellato + +1. Sfizz does not follow a so-called "Benevolent Dictatorship for Life" model. +Given the current size of the project, we believe an horizontal organization model to be the most relevant. +2. Decisions regarding the technical part or the organizational part of the project are taken unanimously between the maintainers, where anyone can obviously abstain on any decision. +3. We use Git as a global supporting platform for all decisions regarding the project, in the form of either pull requests or issues. +This guarantees that a trace is kept for any contribution or change, and that users may interject and discuss on the subject at hand. + +## Donations and financial contributions + +In most jurisdictions, handling money requires bank accounts and identification as a physical person, or be incorporated as a non-profit or for-profit organization. +We wish to: + +- keep things simple; +- support the horizontal governance model we chose; +- facilitate a transparent handling of the finances of the project from the maintainers; +- abide by international laws for the transfer and handling of money in support of a project. + +Considering these constraints, we chose to host the SFZ tools organization within the [SFZTools Open Collective]. +The Open Collective fiscal host holds the money of unincorporated partnerships such as SFZ tools, providing legal channels and accounting, as well as tools to support a transparent budget management by members of the collective. + +We apply the following ruleset to handle the project's financial resources: + +1. As everything in SFZ tools, usage of the project's financial resources is subject to unanimous approval by all maintainers. +2. The money is primarily directed towards expenses to support hosting the project---namely the website, and continuous testing and integration platforms. +3. At any time, a maintainer may submit an expense request regarding the development of sfizz, for example but not limited to software resources, commercial SFZ libraries, or hardware devices. +4. Every 3 months, during the months of March, June, September and December, the maintainers will gather in a budget meeting. The goal of the budget meeting decide in goodwill how to handle the money left over between contributors to the project. +5. The partition should take into account individual technical and organizational contributions. +The partition may also include other project that sfizz uses or wish to support. +6. Any contributor may waive its own share to benefit either the project---pooling back resources for the next round---or the other contributors. +7. Failure to reach a unanimous agreement will see the donations pooled back until the next budget meeting. +8. The budget and expenses are kept public so all donators can keep track of how their donations are used. + +Note that the Open Collective platform charges a 10% administrative fee on all donations; subsequent fees may be charged by credit card or banks as the money is moved from the collective to individual accounts. + +## Changes to the governance model + +1. This document is subject to changes and version control. +It is the role of maintainers to best adapt the governance model to the evolution of the project. +2. Change to the governance model are mapped to changes to this document through the same decision process, e.g. a pull request with approval from all maintainers. +3. Maintainers can be invited from regular contributors at any point in time; maintainers wishing to leave the governance of the project may inform the other maintainers. +In both cases, updates to this document reflect the evolution of the governance team and rules. + +## License for the GOVERNANCE.md file + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided that this notice +is preserved. This file is offered as-is, without any warranty. + +[Open-source Open Collective]: https://opencollective.com/sfztools diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..28b1f2f --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2021-2023, sfizz contributors (detailed in AUTHORS.md) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfbe897 --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# sfizz + +![build](https://github.com/sfztools/sfizz/actions/workflows/build.yml/badge.svg) + +[![Discord Badge Image]](https://discord.gg/3ArE9Mw) +[![SFZv1 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=1) +[![SFZv2 Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=2) +[![ARIA Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=aria) +[![Cakewalk Status Image]](https://sfz.tools/sfizz/development/status/opcodes/?v=cakewalk) + +SFZ parser and synth c++ library, providing AU / LV2 / VST3 plugins +and JACK standalone client, please check [our website] for more details. + +![Screenshot](screenshot.png) + +## Using sfizz + +Sfizz can be used most easily within an LV2 host such as [Carla] or [Ardour]. +It can also be integrated as a library within your own program; check out our [API] bindings for C and C++. +Our [releases] are an immediate way to get a working library or LV2/VST plugin for Windows and Mac. +Linux builds are available over at [OBS]. +On any operating system, you might prefer to [build from source]! + +## Contributing to sfizz + +There is actually many things anyone can do, programming-related or music-related. +Please check out the [CONTRIBUTING](CONTRIBUTING.md) document for information about filing bug reports or feature requests, and helping the development of sfizz + +## Donating to sfizz + +Sfizz and the work in the SFZ tools organization is purely driven by hobbyists who choose to use their free time to benefit this project. +We firmly believe in the honesty and goodwill of users as a whole, and we want to promote a healthy relationship to software and to the cost of producing quality software. + +No financial returns is explicitely required from using sfizz in any shape. +However, if you feel that sfizz produces value for you or your products, and if you find that your financial situation allows for it, we put together ways to donate to the project. +You are never compelled to do so, the [CONTRIBUTING](CONTRIBUTING.md) file contains different ways to contribute. + +In all of sfizz's governance model, we strive to live in the open. +Finances are no different, and we put in place a process so that the use of donations is as transparent as possible through our [Open Collective]. +We invite you to check out the [GOVERNANCE](GOVERNANCE.md) file to see how the organization is governed and how are donations handled. + +## Dependencies and licenses + +The sfizz library has the option to be compiled against either the `dr_libs` +audio libraries, which is now the default option, or `libsndfile`. + +- [dr_libs] is licensed under the MIT No Attribution license +- [libsndfile] is licensed under the GNU Lesser General Public License v2.1 + +The sfizz library makes primary use of: + +- [Abseil], licensed under the Apache License 2.0 +- [atomic_queue] by Maxim Egorushkin, licensed under the MIT license +- [filesystem] by Steffen Schümann, licensed under the BSD 3-Clause license +- [hiir] by Laurent de Soras, licensed under the WTFPL v2 license +- [KISS FFT] by Mark Borgerding, licensed under the BSD 3-Clause license +- [Surge tuning] by Paul Walker, licensed under the MIT license +- [pugixml] by Arseny Kapoulkine, licensed under the MIT license +- [cephes] by Stephen Moshier, licensed under the BSD 3-Clause license +- [cpuid] by Steinwurf ApS, licensed under the BSD 3-Clause license +- [faust-libraries] by GRAME, Julius O. Smith III and Eric Tarr, licensed under the STK-4.3 license and a permissive variant of the LGPL license + +The sfizz library also uses in some subprojects: + +- [Catch2], licensed under the Boost Software License 1.0 +- [benchmark], licensed under the Apache License 2.0 +- [LV2], licensed under the ISC license +- [JACK], licensed under the GNU Lesser General Public License v2.1 +- [cxxopts] by Jarryd Beck, licensed under the MIT license +- [fmidi] by Jean Pierre Cimalando, licensed under the Boost Software License 1.0 +- [libsamplerate], licensed under the BSD 2-Clause license +- [GLSL-Color-Spaces] by tobspr, licensed under the MIT license +- [stb_image] by Sean Barrett, licensed as public domain or MIT license + +[Abseil]: https://abseil.io/ +[atomic_queue]: https://github.com/max0x7ba/atomic_queue +[benchmark]: https://github.com/google/benchmark +[Catch2]: https://github.com/catchorg/Catch2 +[filesystem]: https://github.com/gulrak/filesystem +[Surge tuning]: https://surge-synth-team.org/tuning-library/ +[pugixml]: https://pugixml.org/ +[cephes]: https://www.netlib.org/cephes/ +[cpuid]: https://github.com/steinwurf/cpuid +[dr_libs]: https://github.com/mackron/dr_libs +[faust-libraries]: https://github.com/grame-cncm/faustlibraries +[hiir]: http://ldesoras.free.fr/prod.html#src_hiir +[KISS FFT]: http://kissfft.sourceforge.net/ +[JACK]: https://github.com/jackaudio/jack2 +[cxxopts]: https://github.com/jarro2783/cxxopts +[fmidi]: https://github.com/jpcima/fmidi +[libsamplerate]: http://www.mega-nerd.com/SRC/ +[libsndfile]: http://www.mega-nerd.com/libsndfile/ +[LV2]: https://lv2plug.in/ +[GLSL-Color-Spaces]: https://github.com/tobspr/GLSL-Color-Spaces +[stb_image]: https://github.com/nothings/stb +[our website]: https://sfz.tools/sfizz +[releases]: https://github.com/sfztools/sfizz/releases +[Carla]: https://kx.studio/Applications:Carla +[Ardour]: https://ardour.org/ +[API]: https://sfz.tools/sfizz/api/ +[Open Collective]: https://opencollective.com/sfztools +[build from source]: https://sfz.tools/sfizz/development/build/ +[AppVeyor Build Status]: https://img.shields.io/appveyor/ci/sfztools/sfizz.svg?label=Windows&style=popout&logo=appveyor +[Travis Build Status]: https://img.shields.io/travis/com/sfztools/sfizz.svg?label=Linux&style=popout&logo=travis +[Discord Badge Image]: https://img.shields.io/discord/587748534321807416?label=discord&logo=discord +[OBS]: https://software.opensuse.org//download.html?project=home%3Asfztools%3Asfizz&package=sfizz +[SFZv1 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz1.svg +[SFZv2 Status Image]: https://sfz.tools/assets/img/sfizz/badge_sfz2.svg +[ARIA Status Image]: https://sfz.tools/assets/img/sfizz/badge_aria.svg +[Cakewalk Status Image]: https://sfz.tools/assets/img/sfizz/badge_cakewalk.svg diff --git a/cmake/BundleDylibs.cmake b/cmake/BundleDylibs.cmake new file mode 100644 index 0000000..f7c0628 --- /dev/null +++ b/cmake/BundleDylibs.cmake @@ -0,0 +1,32 @@ +# Dylib bundler for macOS +# Requires the external program "dylibbundler" + +if(APPLE) + find_program(DYLIBBUNDLER_PROGRAM "dylibbundler") + if(NOT DYLIBBUNDLER_PROGRAM) + message(WARNING "The installation helper \"dylibbundler\" is not available.") + endif() +endif() + +function(bundle_dylibs NAME PATH) + if(NOT APPLE OR NOT DYLIBBUNDLER_PROGRAM) + return() + endif() + + set(_relative_libdir "../Frameworks") + + get_filename_component(_dir "${PATH}" DIRECTORY) + set(_dir "${_dir}/${_relative_libdir}") + + set(_script "${CMAKE_CURRENT_BINARY_DIR}/_bundle-dylibs.${NAME}.cmake") + + file(WRITE "${_script}" +"execute_process(COMMAND \"${DYLIBBUNDLER_PROGRAM}\" + \"-cd\" \"-of\" \"-b\" + \"-x\" \"\$ENV{DESTDIR}${PATH}\" + \"-d\" \"\$ENV{DESTDIR}${_dir}\" + \"-p\" \"@loader_path/${_relative_libdir}/\") +") + + install(SCRIPT "${_script}" ${ARGN}) +endfunction() diff --git a/cmake/LV2Config.cmake b/cmake/LV2Config.cmake new file mode 100644 index 0000000..31e88a9 --- /dev/null +++ b/cmake/LV2Config.cmake @@ -0,0 +1,126 @@ +# This option is for MIDI CC support in absence of host midi:binding support +option(SFIZZ_LV2_PSA "Enable plugin-side MIDI automations" ON) + +# Configuration for this plugin +# TODO: generate version from git +set(LV2PLUGIN_VERSION_MINOR 10) +set(LV2PLUGIN_VERSION_MICRO 3) +set(LV2PLUGIN_NAME "sfizz") +set(LV2PLUGIN_COMMENT "SFZ sampler") +set(LV2PLUGIN_URI "http://sfztools.github.io/sfizz") +set(LV2PLUGIN_REPOSITORY SFIZZ_REPOSITORY) +set(LV2PLUGIN_AUTHOR "SFZTools") +set(LV2PLUGIN_EMAIL "paul@ferrand.cc") +if(SFIZZ_USE_VCPKG) + set(LV2PLUGIN_SPDX_LICENSE_ID "LGPL-3.0-only") +else() + set(LV2PLUGIN_SPDX_LICENSE_ID "ISC") +endif() + +if(SFIZZ_LV2_UI) + set(LV2PLUGIN_IF_ENABLE_UI "") +else() + set(LV2PLUGIN_IF_ENABLE_UI "#") +endif() + +if(WIN32) + set(LV2_UI_TYPE "WindowsUI") +elseif(APPLE) + set(LV2_UI_TYPE "CocoaUI") +elseif(HAIKU) + set(LV2_UI_TYPE "BeUI") +else() + set(LV2_UI_TYPE "X11UI") +endif() + +if(APPLE) + set(LV2PLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Audio/Plug-Ins/LV2" CACHE STRING + "Install destination for LV2 bundle [default: $ENV{HOME}/Library/Audio/Plug-Ins/LV2]") +elseif(MSVC) + set(LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lv2" CACHE STRING + "Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lv2]") +else() + set(LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/lv2" CACHE STRING + "Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/lv2]") +endif() + +include(StringUtility) + +function(sfizz_lv2_generate_controllers_ttl FILE) + file(WRITE "${FILE}" "# LV2 parameters for SFZ controllers +@prefix atom: . +@prefix lv2: . +@prefix midi: . +@prefix patch: . +@prefix rdfs: . +@prefix sfizz: <${LV2PLUGIN_URI}#> . +") + math(EXPR _j "${MIDI_CC_MAX}-1") + foreach(_i RANGE "${_j}") + if(_i LESS 128 AND SFIZZ_LV2_PSA) + continue() # Don't generate automation parameters for CCs with plugin-side automation + endif() + + string_left_pad(_i "${_i}" 3 0) + file(APPEND "${FILE}" " +sfizz:cc${_i} + a lv2:Parameter ; + rdfs:label \"Controller ${_i}\" ; + rdfs:range atom:Float ; + lv2:minimum 0.0 ; + lv2:maximum 1.0") + + if(_i LESS 128 AND NOT SFIZZ_LV2_PSA) + math(EXPR _digit1 "${_i}>>4") + math(EXPR _digit2 "${_i}&15") + string(SUBSTRING "0123456789ABCDEF" "${_digit1}" 1 _digit1) + string(SUBSTRING "0123456789ABCDEF" "${_digit2}" 1 _digit2) + file(APPEND "${FILE}" " ; + midi:binding \"B0${_digit1}${_digit2}00\"^^midi:MidiEvent . +") + else() + file(APPEND "${FILE}" " . +") + endif() + endforeach() + + file(APPEND "${FILE}" " +<${LV2PLUGIN_URI}> + a lv2:Plugin ; +") + + file(APPEND "${FILE}" " patch:readable") + if(NOT SFIZZ_LV2_PSA) + file(APPEND "${FILE}" " sfizz:cc000") + foreach(_i RANGE 1 "${_j}") + string_left_pad(_i "${_i}" 3 0) + file(APPEND "${FILE}" ", sfizz:cc${_i}") + endforeach() + else() + file(APPEND "${FILE}" " sfizz:cc128") + foreach(_i RANGE 129 "${_j}") + string_left_pad(_i "${_i}" 3 0) + file(APPEND "${FILE}" ", sfizz:cc${_i}") + endforeach() + endif() + + file(APPEND "${FILE}" " ; +") + + file(APPEND "${FILE}" " patch:writable") + if(NOT SFIZZ_LV2_PSA) + file(APPEND "${FILE}" " sfizz:cc000") + foreach(_i RANGE 1 "${_j}") + string_left_pad(_i "${_i}" 3 0) + file(APPEND "${FILE}" ", sfizz:cc${_i}") + endforeach() + else() + file(APPEND "${FILE}" " sfizz:cc128") + foreach(_i RANGE 129 "${_j}") + string_left_pad(_i "${_i}" 3 0) + file(APPEND "${FILE}" ", sfizz:cc${_i}") + endforeach() + endif() + file(APPEND "${FILE}" " . +") +endfunction() diff --git a/cmake/MakeUninstall.cmake.in b/cmake/MakeUninstall.cmake.in new file mode 100644 index 0000000..c2d34d4 --- /dev/null +++ b/cmake/MakeUninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/cmake/PluginsUninstall.cmake b/cmake/PluginsUninstall.cmake new file mode 100644 index 0000000..6286832 --- /dev/null +++ b/cmake/PluginsUninstall.cmake @@ -0,0 +1,19 @@ +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/MakeUninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/MakeUninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/MakeUninstall.cmake) + + if(SFIZZ_LV2 AND LV2PLUGIN_INSTALL_DIR) + add_custom_command(TARGET uninstall + COMMAND rm -rv "${LV2PLUGIN_INSTALL_DIR}/${PROJECT_NAME}.lv2") + endif() + + if(SFIZZ_VST AND VSTPLUGIN_INSTALL_DIR) + add_custom_command(TARGET uninstall + COMMAND rm -rv "${VSTPLUGIN_INSTALL_DIR}/${PROJECT_NAME}.vst3") + endif() +endif() diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake new file mode 100644 index 0000000..11eab2f --- /dev/null +++ b/cmake/PuredataConfig.cmake @@ -0,0 +1,70 @@ +find_path(PD_INCLUDE_BASEDIR "m_pd.h" PATH_SUFFIXES "pd") +set(PD_IMP_DEF "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/bin/pd.def") + +if(PD_INCLUDE_BASEDIR) + message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") +else() + message(STATUS "Puredata headers not found, using our own") + set(PD_INCLUDE_BASEDIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include") +endif() + +if(WIN32) + set(PUREDATA_SUFFIX ".dll") +elseif(APPLE) + set(PUREDATA_SUFFIX ".pd_darwin") +else() + set(PUREDATA_SUFFIX ".pd_linux") +endif() + +if(APPLE) + set(PDPLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Pd" CACHE STRING + "Install destination for Puredata bundle [default: $ENV{HOME}/Library/Pd]") +elseif(MSVC) + set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/Pd/extra" CACHE STRING + "Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/Pd/extra]") +else() + set(PDPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/pd/extra" CACHE STRING + "Install destination for Puredata bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/pd/extra]") +endif() + +if(WIN32) + add_library(pdex-implib STATIC IMPORTED) + if(MSVC) + add_custom_command( + OUTPUT "pd.lib" + COMMAND "lib" "/out:pd.lib" "/def:${PD_IMP_DEF}" "/machine:${MSVC_C_ARCHITECTURE_ID}" + DEPENDS "${PD_IMP_DEF}") + add_custom_target(pdex-implib-generated + DEPENDS "pd.lib") + set_target_properties(pdex-implib PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/pd.lib") + else() + find_program(PD_DLLTOOL_PROGRAM "dlltool") + if(NOT PD_DLLTOOL_PROGRAM) + message(FATAL_ERROR "Cannot find dlltool") + endif() + add_custom_command( + OUTPUT "libpd.dll.a" + COMMAND "${PD_DLLTOOL_PROGRAM}" "-l" "libpd.dll.a" "-d" "${PD_IMP_DEF}" + DEPENDS "${PD_IMP_DEF}") + add_custom_target(pdex-implib-generated + DEPENDS "libpd.dll.a") + set_target_properties(pdex-implib PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libpd.dll.a") + endif() + add_dependencies(pdex-implib pdex-implib-generated) +endif() + +function(add_pd_external TARGET) + add_library("${TARGET}" MODULE ${ARGN}) + target_include_directories("${TARGET}" PRIVATE "${PD_INCLUDE_BASEDIR}") + set_target_properties("${TARGET}" PROPERTIES + PREFIX "" + SUFFIX "${PUREDATA_SUFFIX}") + if(APPLE) + set_property(TARGET "${TARGET}" APPEND_STRING + PROPERTY LINK_FLAGS " -Wl,-undefined,suppress,-flat_namespace") + elseif(WIN32) + target_link_libraries("${TARGET}" PRIVATE pdex-implib) + endif() +endfunction() diff --git a/cmake/StringUtility.cmake b/cmake/StringUtility.cmake new file mode 100644 index 0000000..00c5ba5 --- /dev/null +++ b/cmake/StringUtility.cmake @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-2-Clause + +function(string_left_pad VAR INPUT LENGTH FILLCHAR) + set(_output "${INPUT}") + string(LENGTH "${_output}" _length) + while(_length LESS "${LENGTH}") + set(_output "${FILLCHAR}${_output}") + string(LENGTH "${_output}" _length) + endwhile() + set("${VAR}" "${_output}" PARENT_SCOPE) +endfunction() diff --git a/cmake/VSTConfig.cmake b/cmake/VSTConfig.cmake new file mode 100644 index 0000000..e445873 --- /dev/null +++ b/cmake/VSTConfig.cmake @@ -0,0 +1,48 @@ +set(VSTPLUGIN_NAME "sfizz") +set(VSTPLUGIN_VENDOR "SFZTools") +set(VSTPLUGIN_URL "http://sfztools.github.io/sfizz") +set(VSTPLUGIN_EMAIL "paul@ferrand.cc") + +if(APPLE) + set(VSTPLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Audio/Plug-Ins/VST3" CACHE STRING + "Install destination for VST bundle [default: $ENV{HOME}/Library/Audio/Plug-Ins/VST3]") + set(AUPLUGIN_INSTALL_DIR "$ENV{HOME}/Library/Audio/Plug-Ins/Components" CACHE STRING + "Install destination for AudioUnit bundle [default: $ENV{HOME}/Library/Audio/Plug-Ins/Components]") +elseif(MSVC) + set(VSTPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/vst3" CACHE STRING + "Install destination for VST bundle [default: ${CMAKE_INSTALL_PREFIX}/vst3]") +else() + set(VSTPLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/vst3" CACHE STRING + "Install destination for VST bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/vst3]") +endif() + +if(NOT VST3_SYSTEM_PROCESSOR) + set(VST3_SYSTEM_PROCESSOR "${PROJECT_SYSTEM_PROCESSOR}") +endif() + +message(STATUS "The system architecture is: ${VST3_SYSTEM_PROCESSOR}") + +# --- VST3 Bundle architecture --- +if(NOT VST3_PACKAGE_ARCHITECTURE) + if(APPLE) + # VST3 packages are universal on Apple, architecture string not needed + else() + if(VST3_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64|AMD64|x64|X64)$") + set(VST3_PACKAGE_ARCHITECTURE "x86_64") + elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(i.86|x86|X86)$") + if(WIN32) + set(VST3_PACKAGE_ARCHITECTURE "x86") + else() + set(VST3_PACKAGE_ARCHITECTURE "i386") + endif() + elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(armv[3-8][a-z]*)$") + set(VST3_PACKAGE_ARCHITECTURE "${VST3_SYSTEM_PROCESSOR}") + elseif(VST3_SYSTEM_PROCESSOR MATCHES "^(aarch64)$") + set(VST3_PACKAGE_ARCHITECTURE "aarch64") + else() + message(FATAL_ERROR "We don't know this architecture for VST3: ${VST3_SYSTEM_PROCESSOR}.") + endif() + endif() +endif() + +message(STATUS "The VST3 architecture is deduced as: ${VST3_PACKAGE_ARCHITECTURE}") diff --git a/library b/library new file mode 160000 index 0000000..37bf8fe --- /dev/null +++ b/library @@ -0,0 +1 @@ +Subproject commit 37bf8fe7baf38e24fd9b16686a1030a5230f20f9 diff --git a/mac/dmg-back.png b/mac/dmg-back.png new file mode 100644 index 0000000000000000000000000000000000000000..bb1ebf5d93fb3c7203ad705972a92cca7bd533b3 GIT binary patch literal 18921 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 literal 0 HcmV?d00001 diff --git a/mac/dmg-back.svg b/mac/dmg-back.svg new file mode 100644 index 0000000..1b8e61a --- /dev/null +++ b/mac/dmg-back.svg @@ -0,0 +1,251 @@ + + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/mac/dmg-back@2x.png b/mac/dmg-back@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a9d91d753d3fa1f61af2628b25f0845679a1188b GIT binary patch literal 40969 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 z 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 hD*XRMN}sQ9&I!V=#{zPQep{|74dVSE4p literal 0 HcmV?d00001 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000..27af2ff --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,60 @@ +add_library(plugins-common STATIC EXCLUDE_FROM_ALL + "common/plugin/RMSFollower.h" + "common/plugin/MessageUtils.h" + "common/plugin/MessageUtils.cpp" + "common/plugin/InstrumentDescription.h" + "common/plugin/InstrumentDescription.cpp" + "common/plugin/NativeHelpers.h" + "common/plugin/NativeHelpers.cpp" + "common/plugin/SfizzFileScan.h" + "common/plugin/SfizzFileScan.cpp" + "common/plugin/FileTrie.h" + "common/plugin/FileTrie.cpp" + "common/plugin/SfizzForeignPaths.h" + "common/plugin/SfizzForeignPaths.cpp" + "common/plugin/SfizzSettings.h" + "common/plugin/SfizzSettings.cpp") +if(APPLE) + target_sources(plugins-common PRIVATE + "common/plugin/SfizzForeignPaths.mm" + "common/plugin/SfizzSettings.mm" + "common/plugin/NativeHelpers.mm") + set_property( + SOURCE "common/plugin/SfizzForeignPaths.mm" + "common/plugin/SfizzSettings.mm" + "common/plugin/NativeHelpers.mm" + APPEND_STRING + PROPERTY COMPILE_FLAGS " -fobjc-arc") +endif() +target_include_directories(plugins-common PUBLIC "common") +target_link_libraries(plugins-common + PUBLIC sfizz::spin_mutex + PUBLIC sfizz::simde sfizz::filesystem absl::strings + PRIVATE sfizz::pugixml + PRIVATE sfizz::internal sfizz::sfizz) +add_library(sfizz::plugins-common ALIAS plugins-common) + +# Link system dependencies +if(WIN32) +elseif(ANDROID) +elseif(APPLE) + target_link_libraries(plugins-common PRIVATE + "${APPLE_FOUNDATION_LIBRARY}") +else() +endif() + +if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) + add_subdirectory(editor) +endif() + +if(SFIZZ_LV2) + add_subdirectory(lv2) +endif() + +if((SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) AND NOT (PROJECT_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc).*")) + add_subdirectory(vst) +endif() + +if(SFIZZ_PUREDATA) + add_subdirectory(puredata) +endif() diff --git a/plugins/common/plugin/FileTrie.cpp b/plugins/common/plugin/FileTrie.cpp new file mode 100644 index 0000000..014ad62 --- /dev/null +++ b/plugins/common/plugin/FileTrie.cpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "FileTrie.h" +#include +#include +#include + +constexpr size_t FileTrie::npos; + +fs::path FileTrie::at(size_t index) const +{ + if (index >= entries_.size()) + throw std::out_of_range("FileTrie::at"); + return pathFromEntry(index); +} + +fs::path FileTrie::operator[](size_t index) const +{ + assert(index < entries_.size()); + return pathFromEntry(index); +} + +fs::path FileTrie::pathFromEntry(size_t index) const +{ + const Entry* currentEntry = &entries_[index]; + fs::path path = fs::u8path(currentEntry->name); + + size_t currentIndex; + while ((currentIndex = currentEntry->parent) != npos) { + currentEntry = &entries_[currentIndex]; + path = fs::u8path(currentEntry->name) / path; + } + + return path; +} + +std::ostream& operator<<(std::ostream& os, const FileTrie& trie) +{ + os << '{' << '\n'; + for (size_t i = 0, n = trie.size(); i < n; ++i) + os << '\t' << i << ':' << ' ' << trie[i] << ',' << '\n'; + os << '}'; + return os; +} + +//------------------------------------------------------------------------------ +FileTrieBuilder::FileTrieBuilder(size_t initialCapacity) +{ + FileTrie& trie = trie_; + trie.entries_.reserve(initialCapacity); +} + +FileTrie&& FileTrieBuilder::build() +{ + FileTrie& trie = trie_; + trie.entries_.shrink_to_fit(); + return std::move(trie); +} + +size_t FileTrieBuilder::addFile(const fs::path& path) +{ + if (path.empty()) + return FileTrie::npos; + + size_t dirIndex = ensureDirectory(path.parent_path()); + + FileTrie& trie = trie_; + FileTrie::Entry ent; + ent.parent = dirIndex; + ent.name = (--path.end())->u8string(); + + size_t fileIndex = trie.entries_.size(); + trie.entries_.push_back(std::move(ent)); + + return fileIndex; +} + +size_t FileTrieBuilder::ensureDirectory(const fs::path& dirPath) +{ + if (dirPath.empty()) + return FileTrie::npos; + + const fs::path::string_type& dirNat = dirPath.native(); + auto it = directories_.find(dirNat); + if (it != directories_.end()) + return it->second; + + FileTrie& trie = trie_; + FileTrie::Entry ent; + ent.parent = FileTrie::npos; + ent.name = (--dirPath.end())->u8string(); + if (dirPath.has_parent_path()) { + fs::path parentPath = dirPath.parent_path(); + if (parentPath != dirPath) + ent.parent = ensureDirectory(parentPath); + } + + size_t dirIndex = trie.entries_.size(); + trie.entries_.push_back(std::move(ent)); + + directories_[dirNat] = dirIndex; + + return dirIndex; +} diff --git a/plugins/common/plugin/FileTrie.h b/plugins/common/plugin/FileTrie.h new file mode 100644 index 0000000..63bf32d --- /dev/null +++ b/plugins/common/plugin/FileTrie.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#include +#include +#include +#include + +class FileTrie { +public: + static constexpr size_t npos = ~size_t(0); + + size_t size() const noexcept { return entries_.size(); } + fs::path at(size_t index) const; + fs::path operator[](size_t index) const; + void clear() noexcept { entries_.clear(); } + +private: + fs::path pathFromEntry(size_t index) const; + +private: + struct Entry { + size_t parent = npos; + std::string name; + }; + std::vector entries_; + + friend class FileTrieBuilder; +}; + +std::ostream& operator<<(std::ostream& os, const FileTrie& trie); + +//------------------------------------------------------------------------------ +class FileTrieBuilder { +public: + explicit FileTrieBuilder(size_t initialCapacity = 8192); + FileTrie&& build(); + size_t addFile(const fs::path& path); + +private: + size_t ensureDirectory(const fs::path& dirPath); + +private: + FileTrie trie_; + std::unordered_map directories_; +}; diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp new file mode 100644 index 0000000..af8955a --- /dev/null +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "InstrumentDescription.h" +#include "MessageUtils.h" +#include "sfizz.hpp" +#include +#include +#include +#include +#include +#include + +template +static void bufferedStrCat(std::string* buffer, const Args&... args) +{ + buffer->clear(); + absl::StrAppend(buffer, args...); +} + +std::string getDescriptionBlob(sfizz_synth_t* handle) +{ + std::string blob; + blob.reserve(128 * 1024); + + std::vector msgbuf; + msgbuf.resize(1024); + + std::string pathbuf; + pathbuf.reserve(256); + + struct ClientData { + sfz::Sfizz* synth = nullptr; + sfz::Client* client = nullptr; + std::string* blob = nullptr; + std::vector* msgbuf = nullptr; + std::string* pathbuf = nullptr; + }; + + sfz::Sfizz synth(handle); + ClientData cdata; + sfz::ClientPtr client = synth.createClient(&cdata); + cdata.synth = &synth; + cdata.client = client.get(); + cdata.blob = &blob; + cdata.msgbuf = &msgbuf; + cdata.pathbuf = &pathbuf; + + synth.setReceiveCallback(*client, [](void* data, int, const char* path, const char* sig, const sfizz_arg_t* args) { + ClientData& cdata = *reinterpret_cast(data); + unsigned indices[8]; + + /// + uint32_t msglen = sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); + if (msglen > cdata.msgbuf->size()) { + cdata.msgbuf->resize(msglen); + sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); + } + cdata.blob->append(cdata.msgbuf->data(), msglen); + + /// + if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { + if (bits.test(key)) { + bufferedStrCat(cdata.pathbuf, "/key", key, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { + if (bits.test(key)) { + bufferedStrCat(cdata.pathbuf, "/sw/last/", key, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned cc = 0; cc < sfz::config::numCCs && cc < bits.bit_size(); ++cc) { + if (bits.test(cc)) { + bufferedStrCat(cdata.pathbuf, "/cc", cc, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + bufferedStrCat(cdata.pathbuf, "/cc", cc, "/default"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + bufferedStrCat(cdata.pathbuf, "/cc", cc, "/value"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + }); + + synth.sendMessage(*client, 0, "/num_regions", "", nullptr); + synth.sendMessage(*client, 0, "/num_groups", "", nullptr); + synth.sendMessage(*client, 0, "/num_masters", "", nullptr); + synth.sendMessage(*client, 0, "/num_curves", "", nullptr); + synth.sendMessage(*client, 0, "/num_samples", "", nullptr); + synth.sendMessage(*client, 0, "/root_path", "", nullptr); + synth.sendMessage(*client, 0, "/image", "", nullptr); + synth.sendMessage(*client, 0, "/image_controls", "", nullptr); + synth.sendMessage(*client, 0, "/key/slots", "", nullptr); + synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); + synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); + synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr); + + blob.shrink_to_fit(); + return blob; +} + +InstrumentDescription parseDescriptionBlob(absl::string_view blob) +{ + InstrumentDescription desc; + + const uint8_t* src = reinterpret_cast(blob.data()); + uint32_t srcSize = blob.size(); + char buffer[1024]; + + const char* path; + const char* sig; + const sfizz_arg_t* args; + + int32_t byteCount; + while ((byteCount = sfizz_extract_message(src, srcSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + unsigned indices[8]; + + /// + auto copyArgToBitSpan = [](const sfizz_arg_t& arg, BitSpan bits) + { + size_t size = std::min(bits.byte_size(), arg.b->size); + memcpy(bits.data(), arg.b->data, size); + }; + + // + if (Messages::matchOSC("/num_regions", path, indices) && !strcmp(sig, "i")) + desc.numRegions = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_groups", path, indices) && !strcmp(sig, "i")) + desc.numGroups = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_masters", path, indices) && !strcmp(sig, "i")) + desc.numMasters = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_curves", path, indices) && !strcmp(sig, "i")) + desc.numCurves = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_samples", path, indices) && !strcmp(sig, "i")) + desc.numSamples = uint32_t(args[0].i); + else if (Messages::matchOSC("/root_path", path, indices) && !strcmp(sig, "s")) + desc.rootPath = args[0].s; + else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) + desc.image = args[0].s; + else if (Messages::matchOSC("/image_controls", path, indices) && !strcmp(sig, "s")) + desc.image_controls = args[0].s; + else if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.keyUsed.span()); + else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.keyswitchUsed.span()); + else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.ccUsed.span()); + else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span()); + else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s")) + desc.keyLabel[indices[0]] = args[0].s; + else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) + desc.keyswitchLabel[indices[0]] = args[0].s; + else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) + desc.ccLabel[indices[0]] = args[0].s; + else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) + desc.ccDefault[indices[0]] = args[0].f; + else if (Messages::matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) + desc.ccValue[indices[0]] = args[0].f; + + src += byteCount; + srcSize -= byteCount; + } + + return desc; +} + +std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc) +{ + os << "instrument:\n"; + + os << " regions: " << desc.numRegions << "\n"; + os << " groups: " << desc.numGroups << "\n"; + os << " masters: " << desc.numMasters << "\n"; + os << " curves: " << desc.numCurves << "\n"; + os << " samples: " << desc.numSamples << "\n"; + + os << " root_path: " << desc.rootPath << "\n"; + os << " image: " << desc.image << "\n"; + os << " image_controls: " << desc.image_controls << "\n"; + + os << " keys:\n"; + for (unsigned i = 0; i < 128; ++i) { + if (desc.keyUsed.test(i)) { + os << " - number: " << i << "\n"; + if (!desc.keyLabel[i].empty()) + os << " label: " << desc.keyLabel[i].c_str() << "\n"; + } + } + + os << " keyswitches:\n"; + for (unsigned i = 0; i < 128; ++i) { + if (desc.keyswitchUsed.test(i)) { + os << " - number: " << i << "\n"; + if (!desc.keyswitchLabel[i].empty()) + os << " label: " << desc.keyswitchLabel[i].c_str() << "\n"; + } + } + + os << " cc:\n"; + for (unsigned i = 0; i < sfz::config::numCCs; ++i) { + if (desc.ccUsed.test(i)) { + os << " - number: " << i << "\n"; + os << " default: " << desc.ccDefault[i] << "\n"; + if (!desc.ccLabel[i].empty()) + os << " label: " << desc.ccLabel[i].c_str() << "\n"; + } + } + + return os; +} diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h new file mode 100644 index 0000000..2435706 --- /dev/null +++ b/plugins/common/plugin/InstrumentDescription.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "sfizz/Config.h" +#include "sfizz/utility/bit_array/BitArray.h" +#include +#include +#include +#include +struct sfizz_synth_t; + +/** + * @brief Description of user-interactible elements of the SFZ instrument + */ +struct InstrumentDescription { + uint32_t numRegions {}; + uint32_t numGroups {}; + uint32_t numMasters {}; + uint32_t numCurves {}; + uint32_t numSamples {}; + std::string rootPath; + std::string image; + std::string image_controls; + BitArray<128> keyUsed {}; + BitArray<128> keyswitchUsed {}; + BitArray<128> sustainOrSostenuto {}; + BitArray ccUsed {}; + std::array keyLabel {}; + std::array keyswitchLabel {}; + std::array ccLabel {}; + std::array ccDefault {}; + std::array ccValue {}; +}; + +/** + * @brief Produce a description of the currently loaded instrument in the synth, + * in the form of a concatenation of OSC messages. + * + * This form is a message transmissible over binary channels. + */ +std::string getDescriptionBlob(sfizz_synth_t* handle); + +/** + * @brief Extract the information from the OSC blob and rearrange it in a + * structured form. + */ +InstrumentDescription parseDescriptionBlob(absl::string_view blob); + +/** + * @brief Display the description in human-readable format. + */ +std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc); diff --git a/plugins/common/plugin/MessageUtils.cpp b/plugins/common/plugin/MessageUtils.cpp new file mode 100644 index 0000000..84e1b5f --- /dev/null +++ b/plugins/common/plugin/MessageUtils.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "MessageUtils.h" +#include +#include +#include + +namespace Messages { + +bool matchOSC(const char* pattern, const char* path, unsigned* indices) +{ + unsigned nthIndex = 0; + + while (const char *endp = std::strchr(pattern, '&')) { + size_t length = endp - pattern; + if (std::strncmp(pattern, path, length)) + return false; + pattern += length; + path += length; + + length = 0; + while (absl::ascii_isdigit(path[length])) + ++length; + + if (!absl::SimpleAtoi(absl::string_view(path, length), &indices[nthIndex++])) + return false; + + pattern += 1; + path += length; + } + + return !std::strcmp(path, pattern); +} + +} // namespace Messages diff --git a/plugins/common/plugin/MessageUtils.h b/plugins/common/plugin/MessageUtils.h new file mode 100644 index 0000000..0faf54a --- /dev/null +++ b/plugins/common/plugin/MessageUtils.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once + +namespace Messages { + +/** + * Simple matcher for message handling in O(N) + * @param[in] pattern Pattern to match, where '&' characters match positive integer numbers + * @param[in] path Path to match against the pattern + * @param[out] indices Table which received the indices, with size >= the number of '&' in the pattern + */ +bool matchOSC(const char* pattern, const char* path, unsigned* indices); + +} // namespace Messages diff --git a/plugins/common/plugin/NativeHelpers.cpp b/plugins/common/plugin/NativeHelpers.cpp new file mode 100644 index 0000000..8f7f0ae --- /dev/null +++ b/plugins/common/plugin/NativeHelpers.cpp @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "NativeHelpers.h" +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include + +const fs::path& getUserDocumentsDirectory() +{ + static const fs::path directory = []() -> fs::path { + std::unique_ptr path(new WCHAR[32768]); + if (SHGetFolderPathW(nullptr, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path.get()) != S_OK) + throw std::runtime_error("Cannot get the document directory."); + return fs::path(path.get()); + }(); + return directory; +} + +wchar_t *stringToWideChar(const char *str, int strCch) +{ + unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); + if (strSize == 0) + return {}; + std::unique_ptr strW(new wchar_t[strSize]); + if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) + return {}; + return strW.release(); +} + +char* stringToUTF8(const wchar_t *strW, int strWCch) +{ + unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); + if (strSize == 0) + return {}; + std::unique_ptr str(new char[strSize]); + if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) + return {}; + return str.release(); +} +#elif defined(__APPLE__) + // implemented in NativeHelpers.mm +#else +const fs::path& getUserDocumentsDirectory() +{ + static const fs::path directory = []() -> fs::path { + for (const XdgUserDirsEntry& ent : + parseXdgUserDirs(getXdgConfigHome() / "user-dirs.dirs")) { + if (ent.name == "XDG_DOCUMENTS_DIR") + return ent.value; + } + return getUserHomeDirectory() / "Documents"; + }(); + return directory; +} + +const fs::path& getUserHomeDirectory() +{ + static const fs::path directory = []() -> fs::path { + const char* home = getenv("HOME"); + if (home && home[0] == '/') + return fs::u8path(home); + else + throw std::runtime_error("Cannot get the home directory."); + }(); + return directory; +} + +const fs::path& getXdgConfigHome() +{ + static const fs::path directory = []() -> fs::path { + const char* config = getenv("XDG_CONFIG_HOME"); + if (config && config[0] == '/') + return fs::u8path(config); + else + return getUserHomeDirectory() / ".config"; + + }(); + return directory; +} + +std::vector parseXdgUserDirs(const fs::path& userDirsPath) +{ + // from user-dirs.dirs(5) + // This file contains lines of the form `XDG_NAME_DIR=VALUE` + // VALUE must be of the form "$HOME/Path" or "/Path". + // Lines beginning with a # character are ignored. + + std::vector ents; + const fs::path& home = getUserHomeDirectory(); + + fs::ifstream in(userDirsPath); + std::string lineBuf; + + lineBuf.reserve(256); + while (std::getline(in, lineBuf)) { + absl::string_view line(lineBuf); + + line = absl::StripLeadingAsciiWhitespace(line); + if (line.empty() || line.front() == '#') + continue; + + size_t pos = line.find('='); + if (pos == line.npos) + continue; + + XdgUserDirsEntry ent; + ent.name = std::string(line.substr(0, pos)); + + absl::string_view rawValue = line.substr(pos + 1); + + rawValue = absl::StripTrailingAsciiWhitespace(rawValue); + + if (rawValue.size() < 2 || rawValue.front() != '"' || rawValue.back() != '"') + continue; + + rawValue.remove_prefix(1); + rawValue.remove_suffix(1); + + if (!rawValue.empty() && rawValue.front() == '/') + ent.value = fs::u8path(rawValue.begin(), rawValue.end()); + else if (absl::StartsWith(rawValue, "$HOME")) { + absl::string_view part = rawValue.substr(5); + ent.value = home / fs::u8path(part.begin(), part.end()).relative_path(); + } + else + continue; + + ents.push_back(std::move(ent)); + } + + return ents; +} +#endif diff --git a/plugins/common/plugin/NativeHelpers.h b/plugins/common/plugin/NativeHelpers.h new file mode 100644 index 0000000..0b534a1 --- /dev/null +++ b/plugins/common/plugin/NativeHelpers.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#if defined(_WIN32) +#include +#endif + +const fs::path& getUserDocumentsDirectory(); + +#if !defined(_WIN32) && !defined(__APPLE__) +const fs::path& getUserHomeDirectory(); +const fs::path& getXdgConfigHome(); +struct XdgUserDirsEntry { std::string name; fs::path value; }; +std::vector parseXdgUserDirs(const fs::path& userDirsPath); +#endif + +#if defined(_WIN32) +wchar_t *stringToWideChar(const char *str, int strCch = -1); +char* stringToUTF8(const wchar_t *strW, int strWCch = -1); +#endif diff --git a/plugins/common/plugin/NativeHelpers.mm b/plugins/common/plugin/NativeHelpers.mm new file mode 100644 index 0000000..8f2bfd3 --- /dev/null +++ b/plugins/common/plugin/NativeHelpers.mm @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "NativeHelpers.h" +#import +#include +#if !__has_feature(objc_arc) +#error This source file requires ARC +#endif + +const fs::path& getUserDocumentsDirectory() +{ + static const fs::path directory = []() -> fs::path { + NSFileManager* fm = [NSFileManager defaultManager]; + NSArray* urls = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; + for (NSUInteger i = 0, n = [urls count]; i < n; ++i) { + NSURL *url = [urls objectAtIndex:i]; + if ([url isFileURL]) + return fs::path([url path].UTF8String); + } + throw std::runtime_error("Cannot get the document directory."); + }(); + return directory; +} diff --git a/plugins/common/plugin/RMSFollower.h b/plugins/common/plugin/RMSFollower.h new file mode 100644 index 0000000..10d2f02 --- /dev/null +++ b/plugins/common/plugin/RMSFollower.h @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +class RMSFollower { +public: + RMSFollower() + { + setNumOutputs(numOutputs_); + updatePole(); + } + + ~RMSFollower() + { + freeAlignedMemory(); + } + + void clear() + { + for (int c = 0; c < numOutputs_; c += 4) { + simde__m128* mem = (simde__m128*) (mem_ + c); + *mem = simde_mm_setzero_ps(); + } + } + + void setNumOutputs(int numOutputs) + { + freeAlignedMemory(); + numOutputs_ = numOutputs; + memSize_ = numOutputs % 4 == 0 ? numOutputs * sizeof(float) : (numOutputs / 4 + 1) * 4 * sizeof(float); +#ifdef _WIN32 + mem_ = (float *)_aligned_malloc(memSize_, 4 * sizeof(float)); +#elif __APPLE__ + mem_ = (float *)malloc(memSize_); // Should be 16 aligned +#else + mem_ = (float *)aligned_alloc(4 * sizeof(float), memSize_); +#endif + clear(); + } + + void init(float sampleRate) + { + sampleRate_ = sampleRate; + updatePole(); + } + + void setT60(float t60) + { + t60_ = t60; + updatePole(); + } + + void process(const float** blocks, size_t numFrames, size_t numChannels) + { + assert(numChannels <= static_cast(numOutputs_)); + const auto numSafeChannels = (numChannels / 4) * 4; + for (size_t c = 0; c < numSafeChannels; c += 4) { + simde__m128* mem = (simde__m128*) (mem_ + c); + process4(mem, &blocks[c], numFrames); + } + + simde__m128* mem = (simde__m128*) (mem_ + numSafeChannels); + const auto remainingChannels = numChannels - numSafeChannels; + if (remainingChannels >= 4) + process4(mem, &blocks[numSafeChannels], numFrames); + else if (remainingChannels == 3) + process3(mem, &blocks[numSafeChannels], numFrames); + else if (remainingChannels == 2) + process2(mem, &blocks[numSafeChannels], numFrames); + else if (remainingChannels == 1) + process1(mem, &blocks[numSafeChannels], numFrames); + } + + void getMS(float* ms, size_t numChannels) const + { + assert(numChannels <= static_cast(numOutputs_)); + const auto numSafeChannels = (numChannels / 4) * 4; + for (size_t c = 0; c < numSafeChannels; c += 4) { + simde__m128* mem = (simde__m128*) (mem_ + c); + simde_mm_store_ps(ms + c, *mem); + } + + simde__m128* mem = (simde__m128*) (mem_ + numSafeChannels); + float* temp = (float*)&temp; + simde_mm_store_ps(temp, *mem); + for (size_t c = numSafeChannels, t = 0; c < numChannels; c++, t++) + ms[c] = temp[t]; + } + + void getRMS(float* rms, size_t numChannels) const + { + assert(numChannels <= static_cast(numOutputs_)); + const auto numSafeChannels = (numChannels / 4) * 4; + for (size_t c = 0; c < numSafeChannels; c += 4) { + simde__m128* mem = (simde__m128*) (mem_ + c); + simde_mm_store_ps(rms + c, simde_mm_sqrt_ps(*mem)); + } + + simde__m128* mem = (simde__m128*) (mem_ + numSafeChannels); + float* temp = (float*)&temp_; + simde_mm_store_ps(temp, simde_mm_sqrt_ps(*mem)); + for (size_t c = numSafeChannels, t = 0; c < numChannels; c++, t++) + rms[c] = temp[t]; + } + +private: + void freeAlignedMemory() + { + if (mem_) +#ifdef _WIN32 + _aligned_free(mem_); +#else + free(mem_); +#endif + } + + void updatePole() + { + pole_ = std::exp(float(-2.0 * M_PI) / (t60_ * sampleRate_)); + } + + void process1(simde__m128* mem, const float** blocks, size_t numFrames) + { + simde__m128 input; + const simde__m128 pole = simde_mm_load1_ps(&pole_); + for (size_t i = 0; i < numFrames; ++i) { + input = simde_mm_setr_ps( + blocks[0][i], 0.0f, 0.0f, 0.0f); + input = simde_mm_mul_ps(input, input); + *mem = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(*mem, input))); + } + } + + void process2(simde__m128* mem, const float** blocks, size_t numFrames) + { + simde__m128 input; + const simde__m128 pole = simde_mm_load1_ps(&pole_); + for (size_t i = 0; i < numFrames; ++i) { + input = simde_mm_setr_ps( + blocks[0][i], blocks[1][i], 0.0f, 0.0f); + input = simde_mm_mul_ps(input, input); + *mem = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(*mem, input))); + } + } + + void process3(simde__m128* mem, const float** blocks, size_t numFrames) + { + simde__m128 input; + const simde__m128 pole = simde_mm_load1_ps(&pole_); + for (size_t i = 0; i < numFrames; ++i) { + input = simde_mm_setr_ps( + blocks[0][i], blocks[1][i], blocks[2][i], 0.0f); + input = simde_mm_mul_ps(input, input); + *mem = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(*mem, input))); + } + } + + void process4(simde__m128* mem, const float** blocks, size_t numFrames) + { + simde__m128 input; + const simde__m128 pole = simde_mm_load1_ps(&pole_); + for (size_t i = 0; i < numFrames; ++i) { + input = simde_mm_setr_ps( + blocks[0][i], blocks[1][i], blocks[2][i], blocks[3][i]); + input = simde_mm_mul_ps(input, input); + *mem = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(*mem, input))); + } + } + +private: + float* mem_ { nullptr }; + simde__m128 temp_; + float pole_ {}; + float t60_ = 300e-3; + float sampleRate_ = 44100; + int numOutputs_ = 2; + int memSize_ = 4; +}; diff --git a/plugins/common/plugin/SfizzFileScan.cpp b/plugins/common/plugin/SfizzFileScan.cpp new file mode 100644 index 0000000..66fbe72 --- /dev/null +++ b/plugins/common/plugin/SfizzFileScan.cpp @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzFileScan.h" +#include "SfizzForeignPaths.h" +#include "SfizzSettings.h" +#include "NativeHelpers.h" +#include +#include +#include +#if defined(_WIN32) +#include +#endif + +// wait at least this much before refreshing the file rescan +// it permits to not repeat the operation many times if many searches are +// requested at once, eg. on session loading with multiple plugin instances +static const std::chrono::seconds expiration_time { 10 }; + +SfzFileScan& SfzFileScan::getInstance() +{ + static SfzFileScan instance; + return instance; +} + +bool SfzFileScan::locateRealFile(const fs::path& pathOrig, fs::path& pathFound) +{ + if (pathOrig.empty()) + return false; + + std::unique_lock lock { mutex }; + refreshScan(); + + auto it = file_index_.find(keyOf(pathOrig.filename())); + if (it == file_index_.end()) + return false; + + const std::list& candidateIndices = it->second; + std::vector candidates; + candidates.reserve(candidateIndices.size()); + for (const size_t index : candidateIndices) + candidates.push_back(file_trie_[index]); + + lock.unlock(); + + pathFound = electBestMatch(pathOrig, candidates); + return true; +} + +bool SfzFileScan::isExpired() const +{ + return !completion_time_ || + (clock::now() - *completion_time_) > expiration_time; +} + +void SfzFileScan::refreshScan(bool force) +{ + if (!force && !isExpired()) + return; + + file_trie_.clear(); + file_index_.clear(); + + FileTrieBuilder builder; + + for (const fs::path& dirPath : SfizzPaths::getSfzSearchPaths()) { + std::error_code ec; + const fs::directory_options dirOpts = + fs::directory_options::skip_permission_denied; + + for (fs::recursive_directory_iterator it(dirPath, dirOpts, ec); + !ec && it != fs::recursive_directory_iterator(); + it.increment(ec)) + { + const fs::directory_entry& ent = *it; + const fs::path& filePath = ent.path(); + std::error_code ec; + if (ent.is_regular_file(ec) /*&& pathIsSfz(filePath)*/) { + size_t fileIndex = builder.addFile(filePath); + file_index_[keyOf(filePath.filename())].push_back(fileIndex); + } + } + } + + file_trie_ = builder.build(); + completion_time_ = clock::now(); +} + +std::string SfzFileScan::keyOf(const fs::path& path) +{ + std::string key = path.u8string(); + absl::AsciiStrToLower(&key); + return key; +} + +namespace SfzFileScanImpl { +template +bool asciiCaseEqual(const std::basic_string& a, const std::basic_string& b) +{ + const size_t n = a.size(); + if (n != b.size()) + return false; + + auto lower = [](T c) -> T { + return (c >= T('A') && c <= T('Z')) ? (c - T('A') + T('a')) : c; + }; + + for (size_t i = 0; i < n; ++i) + if (lower(a[i]) != lower(b[i])) + return false; + + return true; +} +} // namespace SfzFileScanImpl + +bool SfzFileScan::pathIsSfz(const fs::path& path) +{ + const fs::path::string_type& str = path.native(); + using char_type = fs::path::value_type; + const size_t n = str.size(); + return n > 4 && + str[n - 4] == char_type('.') && + (str[n - 3] == char_type('s') || str[n - 3] == char_type('S')) && + (str[n - 2] == char_type('f') || str[n - 2] == char_type('F')) && + (str[n - 1] == char_type('z') || str[n - 1] == char_type('Z')); +} + +const fs::path& SfzFileScan::electBestMatch(const fs::path& path, absl::Span candidates) +{ + if (candidates.empty()) + return path; + + if (candidates.size() == 1) + return candidates.front(); + + struct Score { + size_t components = 0; + size_t exact = 0; + bool operator<(const Score& other) const noexcept + { + return (components != other.components) ? + (components < other.components) : (exact < other.exact); + } + }; + + std::vector scores; + scores.reserve(candidates.size()); + + for (size_t i = 0, n = candidates.size(); i < n; ++i) { + scores.emplace_back(); + Score& score = scores.back(); + + const fs::path& p1 = path; + const fs::path& p2 = candidates[i]; + auto it1 = p1.end(); + auto it2 = p2.end(); + + bool matching = true; + while (matching && it1-- != p1.begin() && it2-- != p2.begin()) { + const fs::path& c1 = *it1; + const fs::path& c2 = *it2; + if (c1 == c2) { + score.components += 1; + score.exact += 1; + } + else if (SfzFileScanImpl::asciiCaseEqual(c1.native(), c2.native())) + score.components += 1; + else + matching = false; + } + } + + size_t best = 0; + for (size_t i = 1, n = scores.size(); i < n; ++i) { + if (scores[best] < scores[i]) + best = i; + } + + return candidates[best]; +} + +//------------------------------------------------------------------------------ + +namespace SfizzPaths { + +std::vector getSfzSearchPaths() +{ + std::vector paths; + paths.reserve(8); + auto addPath = [&paths](const fs::path& newPath) { + if (absl::c_find(paths, newPath) == paths.end()) + paths.push_back(newPath); + }; + + absl::optional configDefaultPath = getSfzConfigDefaultPath(); + fs::path fallbackDefaultPath = getSfzFallbackDefaultPath(); + + if (configDefaultPath) + addPath(*configDefaultPath); + addPath(fallbackDefaultPath); + + for (const fs::path& path : getEnvironmentSfzPaths()) + addPath(path); + + for (const fs::path& foreign : { + getAriaPathSetting("user_files_dir"), + getAriaPathSetting("Converted_path") }) + if (!foreign.empty() && foreign.is_absolute()) + addPath(foreign); + + paths.shrink_to_fit(); + return paths; +} + +absl::optional getSfzConfigDefaultPath() +{ + SfizzSettings settings; + fs::path path = fs::u8path(settings.load_or("user_files_dir", {})); + if (path.empty() || !path.is_absolute()) + return {}; + return path; +} + +void setSfzConfigDefaultPath(const fs::path& path) +{ + if (path.empty() || !path.is_absolute()) + return; + SfizzSettings settings; + settings.store("user_files_dir", path.u8string()); +} + +fs::path getSfzFallbackDefaultPath() +{ + return getUserDocumentsDirectory() / "SFZ instruments"; +} + +std::vector getEnvironmentSfzPaths() +{ + std::vector paths; + +#if defined(_WIN32) + std::unique_ptr buf; + + DWORD bufsize = GetEnvironmentVariableW(L"SFZ_PATH", nullptr, 0); + if (bufsize == 0) + return {}; + + buf.reset(new WCHAR[bufsize]); + if (GetEnvironmentVariableW(L"SFZ_PATH", buf.get(), bufsize) != bufsize - 1) + return {}; + + paths.reserve(8); + + const WCHAR* env = buf.get(); + while (*env) { + const WCHAR* endp; + for (endp = env; *endp && *endp != L';'; ++endp); + fs::path path = fs::path(env, endp); + if (!path.empty() && path.is_absolute()) + paths.push_back(std::move(path)); + env = *endp ? (endp + 1) : endp; + } +#else + const char* env = getenv("SFZ_PATH"); + if (!env) + return {}; + + paths.reserve(8); + + while (*env) { + const char* endp; + for (endp = env; *endp != ':' && *endp != '\0'; ++endp); + fs::path path = fs::u8path(env, endp); + if (!path.empty() && path.is_absolute()) + paths.push_back(std::move(path)); + env = *endp ? (endp + 1) : endp; + } +#endif + + return paths; +} + +} // namespace SfizzPaths diff --git a/plugins/common/plugin/SfizzFileScan.h b/plugins/common/plugin/SfizzFileScan.h new file mode 100644 index 0000000..9a6b0a0 --- /dev/null +++ b/plugins/common/plugin/SfizzFileScan.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "FileTrie.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SfzFileScan { +public: + static SfzFileScan& getInstance(); + bool locateRealFile(const fs::path& pathOrig, fs::path& pathFound); + +private: + typedef std::chrono::steady_clock clock; + std::mutex mutex; + absl::optional completion_time_; + FileTrie file_trie_; + std::unordered_map> file_index_; + + bool isExpired() const; + void refreshScan(bool force = false); + static std::string keyOf(const fs::path& path); + static bool pathIsSfz(const fs::path& path); + static const fs::path& electBestMatch(const fs::path& path, absl::Span candidates); +}; + +namespace SfizzPaths { +std::vector getSfzSearchPaths(); +absl::optional getSfzConfigDefaultPath(); +void setSfzConfigDefaultPath(const fs::path& path); +fs::path getSfzFallbackDefaultPath(); +std::vector getEnvironmentSfzPaths(); +} // namespace SfizzPaths diff --git a/plugins/common/plugin/SfizzForeignPaths.cpp b/plugins/common/plugin/SfizzForeignPaths.cpp new file mode 100644 index 0000000..61fa50f --- /dev/null +++ b/plugins/common/plugin/SfizzForeignPaths.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzForeignPaths.h" + +namespace SfizzPaths { + +#if defined(_WIN32) +#include + +fs::path getAriaPathSetting(const char* name) +{ + std::unique_ptr nameW; + unsigned nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0); + if (nameSize == 0) + return {}; + nameW.reset(new WCHAR[nameSize]); + if (MultiByteToWideChar(CP_UTF8, 0, name, -1, nameW.get(), nameSize) == 0) + return {}; + + const WCHAR ariaKeyPath[] = L"Software\\Plogue Art et Technologie, Inc\\Aria"; + + HKEY key = nullptr; + LSTATUS status = RegOpenKeyExW(HKEY_CURRENT_USER, ariaKeyPath, 0, KEY_QUERY_VALUE, &key); + if (status != ERROR_SUCCESS) + return {}; + + DWORD valueSize = 32768 * sizeof(WCHAR); + std::unique_ptr valueW(new WCHAR[(valueSize / sizeof(WCHAR)) + 1]()); + DWORD valueType; + status = RegQueryValueExW( + key, nameW.get(), nullptr, + &valueType, reinterpret_cast(valueW.get()), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return {}; + + return fs::path(valueW.get()); +} +#elif defined(__APPLE__) + // implementation in SfizzForeignPaths.mm +#else +fs::path getAriaPathSetting(const char* name) +{ + (void)name; + return {}; +} +#endif + +} // namespace SfizzPaths diff --git a/plugins/common/plugin/SfizzForeignPaths.h b/plugins/common/plugin/SfizzForeignPaths.h new file mode 100644 index 0000000..02f79b4 --- /dev/null +++ b/plugins/common/plugin/SfizzForeignPaths.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +namespace SfizzPaths { +fs::path getAriaPathSetting(const char* name); +} // namespace SfizzPaths diff --git a/plugins/common/plugin/SfizzForeignPaths.mm b/plugins/common/plugin/SfizzForeignPaths.mm new file mode 100644 index 0000000..074a3ec --- /dev/null +++ b/plugins/common/plugin/SfizzForeignPaths.mm @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzForeignPaths.h" + +#if defined(__APPLE__) +#import +#if !__has_feature(objc_arc) +#error This source file requires ARC +#endif + +namespace SfizzPaths { + +fs::path getAriaPathSetting(const char* name) +{ + NSUserDefaults* ud = [[NSUserDefaults alloc] initWithSuiteName:@"com.plogue.aria"]; + NSString* value = [ud stringForKey:[NSString stringWithUTF8String:name]]; + if (!value) + return {}; + return fs::path(value.UTF8String); +} + +} // namespace SfizzPaths +#endif diff --git a/plugins/common/plugin/SfizzSettings.cpp b/plugins/common/plugin/SfizzSettings.cpp new file mode 100644 index 0000000..091ad6f --- /dev/null +++ b/plugins/common/plugin/SfizzSettings.cpp @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzSettings.h" +#include "NativeHelpers.h" +#include +#include + +std::string SfizzSettings::load_or(const char* key, absl::string_view defaultValue) +{ + absl::optional optValue = load(key); + return optValue ? *optValue : std::string(defaultValue); +} + +#if defined(_WIN32) +#include + +static HKEY openRegistryKey() +{ + LSTATUS status; + HKEY root = HKEY_CURRENT_USER; + HKEY parent = root; + HKEY key = nullptr; + for (const WCHAR* component : {L"Software", L"SFZTools", L"sfizz"}) { + status = RegCreateKeyExW( + parent, component, 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &key, nullptr); + if (parent != root) + RegCloseKey(parent); + if (status != ERROR_SUCCESS) + return nullptr; + parent = key; + } + return key; +} + +absl::optional SfizzSettings::load(const char* name) +{ + std::unique_ptr nameW { stringToWideChar(name) }; + if (!nameW) + return {}; + + HKEY key = openRegistryKey(); + if (!key) + return {}; + + WCHAR valueW[32768]; + DWORD valueSize = sizeof(valueW); + DWORD valueType; + LSTATUS status = RegQueryValueExW( + key, nameW.get(), nullptr, + &valueType, reinterpret_cast(valueW), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return {}; + + std::unique_ptr value { stringToUTF8(valueW) }; + if (!value) + return {}; + + return std::string(value.get()); +} + +bool SfizzSettings::store(const char* name, absl::string_view value) +{ + std::unique_ptr nameW { stringToWideChar(name) }; + std::unique_ptr valueW { stringToWideChar(std::string(value).c_str()) }; + if (!nameW || !valueW) + return false; + + HKEY key = openRegistryKey(); + if (!key) + return {}; + + LSTATUS status = RegSetValueExW( + key, nameW.get(), 0, RRF_RT_REG_SZ, + reinterpret_cast(valueW.get()), + (wcslen(valueW.get()) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + + return status == ERROR_SUCCESS; +} +#elif defined(__APPLE__) + // implementation in SfizzSettings.mm +#else +#include +#include + +static const fs::path getSettingsPath() +{ + const fs::path dirPath = getXdgConfigHome() / "SFZTools" / "sfizz"; + std::error_code ec; + fs::create_directories(dirPath, ec); + if (ec) + return {}; + return dirPath / "settings.xml"; +} + +absl::optional SfizzSettings::load(const char* key) +{ + const fs::path path = getSettingsPath(); + if (path.empty()) + return {}; + + pugi::xml_document doc; + if (!doc.load_file(path.c_str())) + return {}; + + pugi::xml_node root = doc.child("properties"); + if (!root) + return {}; + + pugi::xml_node entry = root.find_child_by_attribute("entry", "key", key); + if (!entry) + return {}; + + return std::string(entry.text().get()); +} + +bool SfizzSettings::store(const char* key, absl::string_view value) +{ + const fs::path path = getSettingsPath(); + if (path.empty()) + return false; + + pugi::xml_document doc; + doc.load_file(path.c_str()); + + pugi::xml_node root = doc.child("properties"); + if (!root) + root = doc.append_child("properties"); + + pugi::xml_node entry = root.find_child_by_attribute("entry", "key", key); + if (!entry) { + entry = root.append_child("entry"); + entry.append_attribute("key").set_value(key); + } + entry.text().set(std::string(value).c_str()); + + return doc.save_file(path.c_str()); +} +#endif diff --git a/plugins/common/plugin/SfizzSettings.h b/plugins/common/plugin/SfizzSettings.h new file mode 100644 index 0000000..2fcc292 --- /dev/null +++ b/plugins/common/plugin/SfizzSettings.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +class SfizzSettings { +public: + absl::optional load(const char* key); + std::string load_or(const char* key, absl::string_view defaultValue); + bool store(const char* key, absl::string_view value); +}; diff --git a/plugins/common/plugin/SfizzSettings.mm b/plugins/common/plugin/SfizzSettings.mm new file mode 100644 index 0000000..9ddbaeb --- /dev/null +++ b/plugins/common/plugin/SfizzSettings.mm @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzSettings.h" + +#if defined(__APPLE__) +#import +#if !__has_feature(objc_arc) +#error This source file requires ARC +#endif + +static NSUserDefaults* getUserDefaults() +{ + return [[NSUserDefaults alloc] initWithSuiteName:@"tools.sfz.sfizz"];; +} + +absl::optional SfizzSettings::load(const char* key) +{ + NSUserDefaults* ud = getUserDefaults(); + NSString* value = [ud stringForKey:[NSString stringWithUTF8String:key]]; + if (!value) + return {}; + return std::string(value.UTF8String); +} + +bool SfizzSettings::store(const char* key, absl::string_view value) +{ + NSUserDefaults* ud = getUserDefaults(); + NSString* object = + [[NSString alloc] initWithBytes:value.data() + length:(NSUInteger)value.size() encoding:NSUTF8StringEncoding]; + [ud setObject:object forKey:[NSString stringWithUTF8String:key]]; + return true; +} +#endif diff --git a/plugins/editor/CMakeLists.txt b/plugins/editor/CMakeLists.txt new file mode 100644 index 0000000..1c63a6d --- /dev/null +++ b/plugins/editor/CMakeLists.txt @@ -0,0 +1,158 @@ +if(SFIZZ_USE_SYSTEM_VST3SDK) + find_package(PkgConfig REQUIRED) + pkg_check_modules(VST3SDK "vst3sdk" REQUIRED) + set(VSTGUI_BASEDIR "${VST3SDK_INCLUDE_DIRS}/vstgui4") +else() + set(VSTGUI_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/vstgui4") +endif() + +include("cmake/Vstgui.cmake") + +set(EDITOR_RESOURCES + logo.png + logo_orange.png + logo_text.png + logo_text_white.png + logo_text_shaded.png + logo_text@2x.png + logo_text_white@2x.png + logo_text_shaded@2x.png + background.png + background@2x.png + background_button_about.png + background_button_about@2x.png + icon_white.png + icon_white@2x.png + knob48.png + knob48@2x.png + Fonts/sfizz-fluentui-system-r20.ttf + Fonts/sfizz-fluentui-system-f20.ttf + Fonts/sfizz-misc-icons.ttf + Fonts/Roboto-Regular.ttf + Themes/Default/theme.xml + Themes/Dark/theme.xml + PARENT_SCOPE +) +set(UI_RESOURCES + resources/Themes/Default/theme.xml + resources/Themes/Dark/theme.xml +) +function(copy_editor_resources TARGET SOURCE_DIR DESTINATION_DIR) + set(_deps) + foreach(res ${EDITOR_RESOURCES}) + get_filename_component(_dir "${res}" DIRECTORY) + file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}") + add_custom_command( + OUTPUT "${DESTINATION_DIR}/${res}" + COMMAND "${CMAKE_COMMAND}" "-E" "copy" + "${SOURCE_DIR}/${res}" "${DESTINATION_DIR}/${res}" + DEPENDS "${SOURCE_DIR}/${res}") + list(APPEND _deps "${DESTINATION_DIR}/${res}") + endforeach() + add_custom_target("${TARGET}_editor_resources" DEPENDS ${_deps}) + add_dependencies("${TARGET}" "${TARGET}_editor_resources") +endfunction() + +set(UI_FILES layout/main.fl layout/about.fl) +source_group("Editor UI" FILES ${UI_FILES}) +source_group("Editor Resources" FILES ${UI_RESOURCES}) + +# editor +add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL + src/editor/EditIds.h + src/editor/EditIds.cpp + src/editor/Editor.h + src/editor/Editor.cpp + src/editor/EditorLibs.h + src/editor/EditorLibs.cpp + src/editor/EditorController.h + src/editor/GUIComponents.h + src/editor/GUIComponents.cpp + src/editor/GUIDefs.h + src/editor/GUIDefs.cpp + src/editor/GUIHelpers.h + src/editor/GUIHelpers.cpp + src/editor/GUIPiano.h + src/editor/GUIPiano.cpp + src/editor/DlgAbout.h + src/editor/DlgAbout.cpp + src/editor/Theme.h + src/editor/Theme.cpp + src/editor/ColorHelpers.h + src/editor/ColorHelpers.cpp + src/editor/ImageHelpers.h + src/editor/ImageHelpers.cpp + src/editor/NativeHelpers.h + src/editor/NativeHelpers.cpp + src/editor/VSTGUIHelpers.h + src/editor/VSTGUIHelpers.cpp + src/editor/layout/main.hpp + src/editor/layout/about.hpp + src/editor/utility/vstgui_after.h + src/editor/utility/vstgui_before.h + ${UI_FILES} + ${UI_RESOURCES} +) +add_library(sfizz::editor ALIAS sfizz_editor) +target_include_directories(sfizz_editor PUBLIC "src") +target_link_libraries(sfizz_editor PUBLIC sfizz::messaging sfizz::plugins-common) +target_link_libraries(sfizz_editor PRIVATE sfizz::vstgui) +target_compile_definitions(sfizz_editor PRIVATE + "SFIZZ_VERSION=\"${CMAKE_PROJECT_VERSION}\"") +if(APPLE) + find_library(APPLE_APPKIT_LIBRARY "AppKit") + find_library(APPLE_CORESERVICES_LIBRARY "CoreServices") + find_library(APPLE_FOUNDATION_LIBRARY "Foundation") + target_sources(sfizz_editor PRIVATE + src/editor/NativeHelpers.mm + src/editor/VSTGUIHelpers.mm) + target_link_libraries(sfizz_editor PRIVATE + "${APPLE_APPKIT_LIBRARY}" + "${APPLE_CORESERVICES_LIBRARY}" + "${APPLE_FOUNDATION_LIBRARY}") + target_compile_options(sfizz_editor PRIVATE "-fobjc-arc") +endif() + +# dependencies +add_library(sfizz_colorspaces INTERFACE) +add_library(sfizz::colorspaces ALIAS sfizz_colorspaces) +target_include_directories(sfizz_colorspaces INTERFACE "external/color-spaces") + +add_library(sfizz_stb_image INTERFACE) +add_library(sfizz::stb_image ALIAS sfizz_stb_image) +target_include_directories(sfizz_stb_image INTERFACE "external/stb_image") + +if(WIN32) + # +elseif(APPLE) + # +else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(sfizz-gio "gio-2.0" REQUIRED) + target_include_directories(sfizz_editor PRIVATE ${sfizz-gio_INCLUDE_DIRS}) + target_link_libraries(sfizz_editor PRIVATE ${sfizz-gio_LIBRARIES}) +endif() +target_link_libraries(sfizz_editor PRIVATE sfizz::colorspaces sfizz::stb_image + sfizz::bit_array sfizz::filesystem sfizz::pugixml) + +# layout tool +if(NOT CMAKE_CROSSCOMPILING) + add_executable(layout-maker + "tools/layout-maker/sources/layout.h" + "tools/layout-maker/sources/reader.cpp" + "tools/layout-maker/sources/reader.h" + "tools/layout-maker/sources/main.cpp") + target_link_libraries(layout-maker PRIVATE absl::strings) + + foreach(_layout main about) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/${_layout}.hpp" + COMMAND "$" + "${CMAKE_CURRENT_SOURCE_DIR}/layout/${_layout}.fl" + > "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/${_layout}.hpp" + DEPENDS layout-maker "${CMAKE_CURRENT_SOURCE_DIR}/layout/${_layout}.fl") + endforeach() +endif() + +# Git build identifier +target_link_libraries(sfizz_editor PRIVATE sfizz-git-build-id) diff --git a/plugins/editor/cmake/Vstgui.cmake b/plugins/editor/cmake/Vstgui.cmake new file mode 100644 index 0000000..77c2d58 --- /dev/null +++ b/plugins/editor/cmake/Vstgui.cmake @@ -0,0 +1,261 @@ +# Find vstgui version +FILE(READ ${VSTGUI_BASEDIR}/vstgui/lib/vstguibase.h VSTGUIBASE_HEADER) +string(REGEX MATCH "\#define VSTGUI_VERSION_MAJOR ([0-9]*)" _ ${VSTGUIBASE_HEADER}) +set(VSTGUI_VERSION_MAJOR ${CMAKE_MATCH_1}) +string(REGEX MATCH "\#define VSTGUI_VERSION_MINOR ([0-9]*)" _ ${VSTGUIBASE_HEADER}) +set(VSTGUI_VERSION_MINOR ${CMAKE_MATCH_1}) +set(VSTGUI_VERSION "${VSTGUI_VERSION_MAJOR}.${VSTGUI_VERSION_MINOR}") + +add_library(sfizz_vstgui STATIC EXCLUDE_FROM_ALL + "${VSTGUI_BASEDIR}/vstgui/lib/animation/animations.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/animation/animator.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/animation/timingfunctions.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cautoanimation.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cbuttons.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/ccolorchooser.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/ccontrol.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cfontchooser.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cknob.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/clistcontrol.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cmoviebitmap.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cmoviebutton.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/coptionmenu.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cparamdisplay.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cscrollbar.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/csearchtextedit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/csegmentbutton.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cslider.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cspecialdigit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/csplashscreen.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cstringlist.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cswitch.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/ctextedit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/ctextlabel.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cvumeter.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/controls/cxypad.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/platformfactory.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/common/fileresourceinputstream.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/common/genericoptionmenu.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/common/generictextedit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cbitmap.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cbitmapfilter.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/ccolor.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cdatabrowser.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cdrawcontext.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cdrawmethods.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cdropsource.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cfileselector.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cfont.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cframe.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cgradientview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cgraphicspath.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/clayeredviewcontainer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/clinestyle.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/coffscreencontext.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/copenglview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cpoint.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/crect.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/crowcolumnview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cscrollview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cshadowviewcontainer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/csplitview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cstring.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/ctabview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/ctooltipsupport.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cviewcontainer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/cvstguitimer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/genericstringlistdatabrowsersource.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/pixelbuffer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/vstguidebug.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/vstguiinit.cpp") + +if(${VSTGUI_VERSION} VERSION_GREATER "4.10") + target_sources(sfizz_vstgui PRIVATE + ${VSTGUI_BASEDIR}/vstgui/lib/cgradient.cpp + ${VSTGUI_BASEDIR}/vstgui/lib/events.cpp) +endif() + +add_library(sfizz::vstgui ALIAS sfizz_vstgui) + +if(WIN32) + target_sources(sfizz_vstgui PRIVATE + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dbitmap.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dfont.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32datapackage.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32dragging.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32factory.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32frame.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32openglview.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32optionmenu.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32resourcestream.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32support.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/win32textedit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/winfileselector.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/winstring.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/wintimer.cpp") + if(${VSTGUI_VERSION} VERSION_GREATER "4.10") + target_sources(sfizz_vstgui PRIVATE + ${VSTGUI_BASEDIR}/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp) + endif() +elseif(APPLE) + target_sources(sfizz_vstgui PRIVATE + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewframe.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewoptionmenu.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/carbon/hiviewtextedit.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/autoreleasepool.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoaopenglview.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewframe.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cocoa/nsviewoptionmenu.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/caviewlayer.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cfontmac.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cgbitmap.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/cgdrawcontext.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macclipboard.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macfactory.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macfileselector.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macglobals.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/macstring.mm" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/mactimer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/mac/quartzgraphicspath.cpp") +else() + target_sources(sfizz_vstgui PRIVATE + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairobitmap.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairocontext.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairofont.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairogradient.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/cairopath.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/linuxfactory.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/linuxstring.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11dragging.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11fileselector.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11frame.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11platform.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11timer.cpp" + "${VSTGUI_BASEDIR}/vstgui/lib/platform/linux/x11utils.cpp") +endif() + +target_include_directories(sfizz_vstgui PUBLIC "${VSTGUI_BASEDIR}") + +if(WIN32) + if (NOT MSVC) + # autolinked on MSVC with pragmas + target_link_libraries(sfizz_vstgui PRIVATE + "opengl32" + "d2d1" + "dwrite" + "dwmapi" + "windowscodecs" + "shlwapi") + endif() +elseif(APPLE) + target_link_libraries(sfizz_vstgui PRIVATE + "${APPLE_COREFOUNDATION_LIBRARY}" + "${APPLE_FOUNDATION_LIBRARY}" + "${APPLE_COCOA_LIBRARY}" + "${APPLE_OPENGL_LIBRARY}" + "${APPLE_ACCELERATE_LIBRARY}" + "${APPLE_QUARTZCORE_LIBRARY}" + "${APPLE_CARBON_LIBRARY}" + "${APPLE_AUDIOTOOLBOX_LIBRARY}" + "${APPLE_COREAUDIO_LIBRARY}" + "${APPLE_COREMIDI_LIBRARY}") +else() + find_package(X11 REQUIRED) + find_package(Freetype REQUIRED) + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBXCB REQUIRED xcb) + pkg_check_modules(LIBXCB_UTIL REQUIRED xcb-util) + pkg_check_modules(LIBXCB_CURSOR REQUIRED xcb-cursor) + pkg_check_modules(LIBXCB_KEYSYMS REQUIRED xcb-keysyms) + pkg_check_modules(LIBXCB_XKB REQUIRED xcb-xkb) + pkg_check_modules(LIBXKB_COMMON REQUIRED xkbcommon) + pkg_check_modules(LIBXKB_COMMON_X11 REQUIRED xkbcommon-x11) + pkg_check_modules(CAIRO REQUIRED cairo) + pkg_check_modules(PANGO REQUIRED pangocairo pangoft2) + pkg_check_modules(FONTCONFIG REQUIRED fontconfig) + pkg_check_modules(GLIB REQUIRED glib-2.0) + target_include_directories(sfizz_vstgui PRIVATE + ${X11_INCLUDE_DIRS} + ${FREETYPE_INCLUDE_DIRS} + ${LIBXCB_INCLUDE_DIRS} + ${LIBXCB_UTIL_INCLUDE_DIRS} + ${LIBXCB_CURSOR_INCLUDE_DIRS} + ${LIBXCB_KEYSYMS_INCLUDE_DIRS} + ${LIBXCB_XKB_INCLUDE_DIRS} + ${LIBXKB_COMMON_INCLUDE_DIRS} + ${LIBXKB_COMMON_X11_INCLUDE_DIRS} + ${CAIRO_INCLUDE_DIRS} + ${PANGO_INCLUDE_DIRS} + ${FONTCONFIG_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS}) + target_link_libraries(sfizz_vstgui PRIVATE + ${X11_LIBRARIES} + ${FREETYPE_LIBRARIES} + ${LIBXCB_LIBRARIES} + ${LIBXCB_UTIL_LIBRARIES} + ${LIBXCB_CURSOR_LIBRARIES} + ${LIBXCB_KEYSYMS_LIBRARIES} + ${LIBXCB_XKB_LIBRARIES} + ${LIBXKB_COMMON_LIBRARIES} + ${LIBXKB_COMMON_X11_LIBRARIES} + ${CAIRO_LIBRARIES} + ${PANGO_LIBRARIES} + ${FONTCONFIG_LIBRARIES} + ${GLIB_LIBRARIES}) + find_library(DL_LIBRARY "dl") + if(DL_LIBRARY) + target_link_libraries(sfizz_vstgui PRIVATE "${DL_LIBRARY}") + endif() +endif() + +if(${CMAKE_BUILD_TYPE} MATCHES "Debug") + target_compile_definitions(sfizz_vstgui PUBLIC "DEVELOPMENT") +endif() + +if(${CMAKE_BUILD_TYPE} MATCHES "Release") + target_compile_definitions(sfizz_vstgui PUBLIC "RELEASE") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # Windows 10 RS2 DDI for custom fonts + target_compile_definitions(sfizz_vstgui PRIVATE "NTDDI_VERSION=0x0A000003") + # disable custom fonts if dwrite3 API is unavailable in MinGW + if(MINGW) + check_cxx_source_compiles(" +#include +#include +HRESULT FeatureCheck(IDWriteFontSet* self, const WCHAR* name, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STRETCH stretch, DWRITE_FONT_STYLE style, IDWriteFontSet** fontset) +{ + return self->GetMatchingFonts(name, weight, stretch, style, fontset); +} +int main() +{ + return 0; +}" SFIZZ_MINGW_SUPPORTS_DWRITE3) + if(NOT SFIZZ_MINGW_SUPPORTS_DWRITE3) + message(WARNING "This version of MinGW does not support DirectWrite 3. Custom font support is disabled.") + target_compile_definitions(sfizz_vstgui PRIVATE "VSTGUI_WIN32_CUSTOMFONT_SUPPORT=0") + endif() + endif() +endif() + +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + gw_target_warn(sfizz_vstgui PRIVATE + "-Wno-deprecated-copy" + "-Wno-deprecated-declarations" + "-Wno-extra" + "-Wno-ignored-qualifiers" + "-Wno-multichar" + "-Wno-reorder" + "-Wno-sign-compare" + "-Wno-unknown-pragmas" + "-Wno-unused-function" + "-Wno-unused-parameter" + "-Wno-unused-variable") +endif() diff --git a/plugins/editor/external/color-spaces/ColorSpaces.h b/plugins/editor/external/color-spaces/ColorSpaces.h new file mode 100644 index 0000000..06af231 --- /dev/null +++ b/plugins/editor/external/color-spaces/ColorSpaces.h @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +/* +GLSL Color Space Utility Functions +(c) 2015 tobspr + +Porting a subset to C++ +(c) 2020 Jean Pierre Cimalando + +------------------------------------------------------------------------------- + +The MIT License (MIT) + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------- + +Most formulars / matrices are from: +https://en.wikipedia.org/wiki/SRGB + +Some are from: +http://www.chilliant.com/rgb2hsv.html +https://www.fourcc.org/fccyvrgb.php +*/ + +#pragma once +#include +#include +#include + +namespace ColorSpaces { + +template using vec = std::array; +using vec3 = vec<3>; +using vec4 = vec<4>; + +template +T clamp(T x, T lo, T hi) +{ + return std::max(lo, std::min(hi, x)); +} + +template +vec saturate(vec x) +{ + for (std::size_t i = 0; i < N; ++i) + x[i] = clamp(x[i], 0.0f, 1.0f); + return x; +} + +float dot(vec3 a, vec3 b) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +// Constants +static constexpr float HCV_EPSILON = 1e-10; +static constexpr float HCY_EPSILON = 1e-10; + +// Converts a value from linear RGB to HCV (Hue, Chroma, Value) +vec3 rgb_to_hcv(vec3 rgb) +{ + // Based on work by Sam Hocevar and Emil Persson + vec4 P = (rgb[1] < rgb[2]) ? vec4{{rgb[2], rgb[1], -1.0, 2.0/3.0}} : vec4{{rgb[1], rgb[2], 0.0, -1.0/3.0}}; + vec4 Q = (rgb[0] < P[0]) ? vec4{{P[0], P[1], P[3], rgb[0]}} : vec4{{rgb[0], P[1], P[2], P[0]}}; + float C = Q[0] - std::min(Q[3], Q[1]); + float H = std::abs((Q[3] - Q[1]) / (6 * C + HCV_EPSILON) + Q[2]); + return vec3{{H, C, Q[0]}}; +} + +// Converts from pure Hue to linear RGB +vec3 hue_to_rgb(float hue) +{ + float R = std::fabs(hue * 6 - 3) - 1; + float G = 2 - std::fabs(hue * 6 - 2); + float B = 2 - std::fabs(hue * 6 - 4); + return saturate(vec3{{R,G,B}}); +} + +// Converts from HCY to linear RGB +vec3 hcy_to_rgb(vec3 hcy) +{ + const vec3 HCYwts{{0.299, 0.587, 0.114}}; + vec3 RGB = hue_to_rgb(hcy[0]); + float Z = dot(RGB, HCYwts); + if (hcy[2] < Z) { + hcy[1] *= hcy[2] / Z; + } else if (Z < 1) { + hcy[1] *= (1 - hcy[2]) / (1 - Z); + } + return vec3{{(RGB[0] - Z) * hcy[1] + hcy[2], + (RGB[1] - Z) * hcy[1] + hcy[2], + (RGB[2] - Z) * hcy[1] + hcy[2]}}; +} + +// Converts from rgb to hcy (Hue, Chroma, Luminance) +vec3 rgb_to_hcy(vec3 rgb) +{ + const vec3 HCYwts = vec3{{0.299, 0.587, 0.114}}; + // Corrected by David Schaeffer + vec3 HCV = rgb_to_hcv(rgb); + float Y = dot(rgb, HCYwts); + float Z = dot(hue_to_rgb(HCV[0]), HCYwts); + if (Y < Z) { + HCV[1] *= Z / (HCY_EPSILON + Y); + } else { + HCV[1] *= (1 - Z) / (HCY_EPSILON + 1 - Y); + } + return vec3{{HCV[0], HCV[1], Y}}; +} + +} // namespace ColorSpaces diff --git a/plugins/editor/external/color-spaces/LICENSE b/plugins/editor/external/color-spaces/LICENSE new file mode 100644 index 0000000..20efd1b --- /dev/null +++ b/plugins/editor/external/color-spaces/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/plugins/editor/external/stb_image/LICENSE b/plugins/editor/external/stb_image/LICENSE new file mode 100644 index 0000000..a77ae91 --- /dev/null +++ b/plugins/editor/external/stb_image/LICENSE @@ -0,0 +1,37 @@ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/editor/external/stb_image/stb_image.h b/plugins/editor/external/stb_image/stb_image.h new file mode 100644 index 0000000..d60371b --- /dev/null +++ b/plugins/editor/external/stb_image/stb_image.h @@ -0,0 +1,7897 @@ +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/plugins/editor/external/vstgui4 b/plugins/editor/external/vstgui4 new file mode 160000 index 0000000..0db8738 --- /dev/null +++ b/plugins/editor/external/vstgui4 @@ -0,0 +1 @@ +Subproject commit 0db8738ae73ff76842eb0bfe90cdf2039690a5b5 diff --git a/plugins/editor/layout/about.fl b/plugins/editor/layout/about.fl new file mode 100644 index 0000000..4394192 --- /dev/null +++ b/plugins/editor/layout/about.fl @@ -0,0 +1,68 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0305 +header_name {.h} +code_name {.cxx} +widget_class aboutView { + label {About sfizz} open + xywh {527 165 800 475} type Double visible +} { + Fl_Box {} { + image {../resources/logo_orange.png} xywh {200 0 400 180} + class Logo + } + Fl_Box {} { + xywh {260 180 280 80} + class InfoBox + } + Fl_Box {} { + xywh {200 260 400 100} + class DescriptionBox + } + Fl_Box lblHover_ { + xywh {460 435 315 25} labelsize 12 hide + class HoverBox + } + Fl_Button {} { + comment {tag=kTagButtonSfztools} + xywh {460 405 40 40} labelsize 30 + class ButtonSfztools + } + Fl_Button {} { + comment {tag=kTagButtonGithub} + xywh {530 405 40 40} labelsize 30 + class ButtonGithub + } + Fl_Button {} { + comment {tag=kTagButtonDiscord} + xywh {600 405 40 40} labelsize 30 + class ButtonDiscord + } + Fl_Button {} { + comment {tag=kTagButtonOpencollective} + xywh {670 405 40 40} labelsize 30 + class ButtonOpencollective + } + Fl_Button {} { + comment {tag=kTagButtonSfzformat} + xywh {740 405 40 40} labelsize 30 + class ButtonSfzformat + } + Fl_Box {} { + label {Format +Host +Processor +Address +System} + xywh {15 375 90 90} align 20 + class SysInfoKeyBox + } + Fl_Box lblSysInfoValue_ { + label {%PluginFormat% +%HostProgram% +%HostCPU% +%HostBits% bits +%HostOS%} selected + xywh {105 375 345 90} align 20 + class SysInfoValueBox + } +} diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl new file mode 100644 index 0000000..be2497a --- /dev/null +++ b/plugins/editor/layout/main.fl @@ -0,0 +1,464 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0304 +header_name {.h} +code_name {.cxx} +widget_class mainView {open + xywh {624 564 775 515} type Double + class LogicalGroup visible +} { + Fl_Box imageContainer_ { + image {../resources/background.png} xywh {0 110 775 335} + class Background + } + Fl_Group {} { + comment {palette=invertedPalette} open + xywh {0 0 775 110} + class LogicalGroup + } { + Fl_Group {} { + xywh {5 4 175 101} box ROUNDED_BOX align 0 + class RoundedGroup + } { + Fl_Box {} { + comment {tag=kTagAbout} + image {../resources/logo_text_shaded.png} xywh {32 9 120 60} + class AboutButton + } + Fl_Button {panelButtons_[kPanelInfo]} { + comment {tag=kTagFirstChangePanel+kPanelInfo} + xywh {56 73 32 32} labelsize 30 + class InfoButton + } + Fl_Button {panelButtons_[kPanelControls]} { + comment {tag=kTagFirstChangePanel+kPanelControls} + xywh {97 73 32 32} labelsize 30 + class CCButton + } + Fl_Button {panelButtons_[kPanelSettings]} { + comment {tag=kTagFirstChangePanel+kPanelSettings} + xywh {137 73 32 32} labelsize 30 + class SettingsButton + } + Fl_Button {panelButtons_[kPanelGeneral]} { + comment {tag=kTagFirstChangePanel+kPanelGeneral} + xywh {16 73 32 32} labelsize 30 + class HomeButton + } + } + Fl_Group {} {open + xywh {185 5 365 100} box ROUNDED_BOX + class RoundedGroup + } { + Fl_Box {} { + label {Separator 1} + xywh {195 40 345 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Box {} { + label {Separator 2} + xywh {195 75 345 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Box sfzFileLabel_ { + label {DefaultInstrument.sfz} + comment {tag=kTagLoadSfzFile} + xywh {195 10 270 30} labelsize 20 align 20 + class ClickableLabel + } + Fl_Box keyswitchLabel_ { + xywh {255 45 285 30} labelsize 20 align 20 + class Label + } + Fl_Box keyswitchBadge_ { + xywh {195 47 60 26} box THIN_UP_BOX labelsize 20 + class Badge + } + Fl_Box keyswitchInactiveLabel_ { + label {No key switch} + xywh {195 45 345 30} labelsize 20 align 20 hide + class InactiveLabel + } + Fl_Box {} { + label {Voices:} + xywh {195 78 60 25} align 24 + class Label + } + Fl_Button {} { + comment {tag=kTagPreviousSfzFile} + xywh {465 15 25 25} labelsize 24 + class PreviousFileButton + } + Fl_Button {} { + comment {tag=kTagNextSfzFile} + xywh {490 15 25 25} labelsize 24 + class NextFileButton + } + Fl_Button fileOperationsMenu_ { + comment {tag=kTagFileOperations} + xywh {515 15 25 25} labelsize 24 + class ChevronDropDown + } + Fl_Box infoVoicesLabel_ { + xywh {255 78 40 25} align 16 + class Label + } + Fl_Box {} { + label {Max:} + xywh {315 78 40 25} align 24 + class Label + } + Fl_Box numVoicesLabel_ { + xywh {355 78 35 25} align 16 + class Label + } + Fl_Box {} { + label {Memory:} + xywh {430 78 60 25} align 24 + class Label + } + Fl_Box memoryLabel_ { + xywh {490 78 50 25} align 16 + class Label + } + Fl_Button numVoicesSlider_ { + comment {tag=kTagSetNumVoices} + xywh {390 82 20 20} labelsize 16 + class ChevronValueDropDown + } + } + Fl_Group {} { + xywh {555 5 215 100} box ROUNDED_BOX + class RoundedGroup + } { + Fl_Dial {} { + xywh {587 20 48 48} value 0.5 hide + class Knob48 + } + Fl_Box {} { + label Center + xywh {582 70 60 5} hide + class ValueLabel + } + Fl_Box volumeCCKnob_ { + label Volume + comment {tag=kTagSetCCVolume} + xywh {560 10 70 90} box BORDER_BOX align 17 + class KnobCCBox + } + Fl_Box panCCKnob_ { + label Pan + comment {tag=kTagSetCCPan} + xywh {635 10 70 90} box BORDER_BOX align 17 + class KnobCCBox + } + Fl_Box {meters_[0]} { + xywh {710 10 23 90} box BORDER_BOX + class VMeter + } + Fl_Box {meters_[1]} { + xywh {740 10 23 90} box BORDER_BOX + class VMeter + } + } + } + Fl_Group {subPanels_[kPanelInfo]} { + xywh {0 110 775 335} hide + class LogicalGroup + } { + Fl_Group {} { + xywh {0 110 775 335} box BORDER_BOX + class SquaredTransparentGroup + } { + Fl_Box {} { + label {Curves:} + xywh {20 120 60 25} align 20 + class InfoLabel + } + Fl_Box {} { + label {Masters:} + xywh {20 145 60 25} align 20 + class InfoLabel + } + Fl_Box {} { + label {Groups:} + xywh {20 170 60 25} align 20 + class InfoLabel + } + Fl_Box {} { + label {Regions:} + xywh {20 195 60 25} align 20 + class InfoLabel + } + Fl_Box {} { + label {Samples:} + xywh {20 220 60 25} align 20 + class InfoLabel + } + Fl_Box infoCurvesLabel_ { + label 0 + xywh {120 120 40 25} align 16 + class InfoLabel + } + Fl_Box infoMastersLabel_ { + label 0 + xywh {120 145 40 25} align 16 + class InfoLabel + } + Fl_Box infoGroupsLabel_ { + label 0 + xywh {120 170 40 25} align 16 + class InfoLabel + } + Fl_Box infoRegionsLabel_ { + label 0 + xywh {120 195 40 25} align 16 + class InfoLabel + } + Fl_Box infoSamplesLabel_ { + label 0 + xywh {120 220 40 25} align 16 + class InfoLabel + } + } + } + Fl_Group {subPanels_[kPanelControls]} { + xywh {0 110 775 335} hide + class LogicalGroup + } { + Fl_Group controlsPanel_ { + xywh {0 110 775 335} box BORDER_BOX + class ControlsPanel + } {} + } + Fl_Group {subPanels_[kPanelSettings]} { + xywh {0 110 775 335} + class LogicalGroup + } { + Fl_Group {} {open + xywh {0 110 775 335} box BORDER_BOX + class SquaredGroup + } { + Fl_Spinner oversamplingSlider_ { + comment {tag=kTagSetOversampling} + xywh {180 195 70 25} + class ValueMenu + } + Fl_Box {} { + label Oversampling + xywh {10 195 85 25} align 20 + class Label + } + Fl_Box {} { + label {Preload size} + xywh {10 170 75 25} align 20 + class Label + } + Fl_Spinner preloadSizeSlider_ { + comment {tag=kTagSetPreloadSize} + xywh {180 170 70 25} + class ValueMenu + } + Fl_Spinner sampleQualitySlider_ { + comment {tag=kTagSetSampleQuality} + xywh {430 145 80 25} + class ValueMenu + } + Fl_Box {} { + label Sample + xywh {270 145 50 25} align 20 + class Label + } + Fl_Box {} { + label Oscillator + xywh {270 195 60 25} align 20 + class Label + } + Fl_Spinner oscillatorQualitySlider_ { + comment {tag=kTagSetOscillatorQuality} + xywh {430 195 80 25} + class ValueMenu + } + Fl_Spinner themeMenu_ { + comment {tag=kTagThemeMenu} + xywh {665 145 100 25} + class OptionMenu + } + Fl_Box {} { + label Theme + xywh {525 145 45 25} align 20 + class Label + } + Fl_Box {} { + label {User SFZ folder} + xywh {525 170 95 25} align 20 + class Label + } + Fl_Button userFilesDirButton_ { + label DefaultPath + comment {tag=kTagChooseUserFilesDir} + xywh {665 170 100 25} + class ValueButton + } + Fl_Box {} { + label {Root key} + xywh {525 310 60 25} align 20 + class Label + } + Fl_Box {} { + label Frequency + xywh {525 335 70 25} align 20 + class Label + } + Fl_Dial stretchedTuningSlider_ { + comment {tag=kTagSetStretchedTuning} + xywh {700 360 48 48} value 0.5 + class StyledKnob + } + Fl_Box {} { + label Stretch + xywh {525 370 50 25} align 20 + class Label + } + Fl_Box {} { + label {Scala file} + xywh {525 285 60 25} align 20 + class Label + } + Fl_Button scalaFileButton_ { + label DefaultScale + comment {tag=kTagLoadScalaFile} + xywh {624 285 100 25} + class ValueButton + } + Fl_Spinner scalaRootKeySlider_ { + comment {tag=kTagSetScalaRootKey} + xywh {685 310 35 25} + class ValueMenu + } + Fl_Spinner scalaRootOctaveSlider_ { + comment {tag=kTagSetScalaRootKey} + xywh {720 310 30 25} + class ValueMenu + } + Fl_Button scalaResetButton_ { + comment {tag=kTagResetScalaFile} + xywh {725 285 25 25} + class ResetSomethingButton + } + Fl_Box {} { + label {when freewheeling} + xywh {270 170 145 25} align 20 + class Label + } + Fl_Spinner freewheelingSampleQualitySlider_ { + comment {tag=kTagSetFreewheelingSampleQuality} + xywh {430 170 80 25} + class ValueMenu + } + Fl_Box {} { + label {Sustain cancels release} + xywh {10 220 145 25} align 20 + class Label + } + Fl_Check_Button sustainCancelsReleaseCheckbox_ { + comment {tag=kTagSetSustainCancelsRelease} + xywh {180 220 25 25} down_box DOWN_BOX + class Checkbox + } + Fl_Box sfizzVersionLabel_ { + label SFIZZ_VERSION + xywh {10 145 225 20} + class Label + } + Fl_Box {} { + label {Rendering quality} + xywh {270 115 110 25} align 20 + class Label + } + Fl_Box {} { + label {Separator 2} + xywh {10 135 240 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Box {} { + label {Engine options} + xywh {10 115 95 25} align 20 + class Label + } + Fl_Box {} { + label Other + xywh {525 115 40 25} align 20 + class Label + } + Fl_Box {} { + label Tuning + xywh {525 255 225 25} align 20 + class Label + } + Fl_Box {} { + label {Separator Rendering} + xywh {270 135 239 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Box {} { + label {Separator Other} + xywh {525 135 240 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Box {} { + label {Separator Tuning} + xywh {525 275 225 5} box BORDER_BOX labeltype NO_LABEL + class HLine + } + Fl_Button tuningFrequencyDropdown_ { + comment {tag=kTagSetTuningFrequency} + xywh {725 335 25 25} labelsize 24 + class ChevronValueDropDown + } + Fl_Text_Editor tuningFrequencyEdit_ { + comment {tag=kTagSetTuningFrequency} + xywh {665 335 60 25} + class TextEdit + } + Fl_Box {} { + label {when freewheeling} + xywh {270 220 145 25} align 20 + class Label + } + Fl_Spinner freewheelingOscillatorQualitySlider_ { + comment {tag=kTagSetFreewheelingOscillatorQuality} + xywh {430 220 80 25} + class ValueMenu + } + Fl_Box {} { + label Zoom + xywh {525 195 95 25} align 20 + class Label + } + Fl_Spinner zoomMenu_ { + comment {tag=kTagZoomMenu} + xywh {665 195 100 25} + class OptionMenu + } + Fl_Button defaultZoomButton_ { + label {Set Default} + comment {tag=kTagSetDefaultZoom} + xywh {665 220 100 25} + class ValueButton + } + } + } + Fl_Box piano_ { + xywh {0 445 775 70} labelsize 16 + class Piano + } + Fl_Group {subPanels_[kPanelGeneral]} { + xywh {0 110 775 335} hide + class LogicalGroup + } {} + Fl_Box lblHover_ { + comment {palette=invertedPalette} + xywh {5 105 170 25} hide + class HoverBox + } +} diff --git a/plugins/editor/resources/Fonts/Roboto-Regular.ttf b/plugins/editor/resources/Fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2b6392ffe8712b9c5450733320cd220d6c0f4bce GIT binary patch literal 171272 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 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/Fonts/sfizz-fluentui-system-f20.ttf b/plugins/editor/resources/Fonts/sfizz-fluentui-system-f20.ttf new file mode 100644 index 0000000000000000000000000000000000000000..691a6dacc596e615c065d6ed11b518774a6d4e10 GIT binary patch literal 229796 zcmdqK37k~b(KlRm&h5T^yL)<{joGJXWd>%S0S1_1SOnQdKmxJ|I7U=dRD{u>f{LON zg9Z`zF>ygeMMcGkMvV(eG(lq`k3mHw2ICT z`>9i>s!p9ccZi87jaHDR&M{-AOzUZ7&=DSzTME+P7QRtJi z=65ePuHoYlKM3gy&z^tQsbl+%RHQo>5viLNpL70_OYdJiob*(KXy8R8eFJ=R+6|li}+o{@V zpdDi9LS9QQw9BJv5wh61^9m{_U{I0EveVg@(BVhw{Ql@JTCs=rE$G{ZBU*s=Ag&*v z8_*#@-PB24fKEJ3CJ(M{_FI=7*F~!k(go- z^4Cf2G{XK#Ju~f+1mlAFzj77sf|U;TqLfv%0->q$WE7_WSxXM*pO(67lhNV#30 zrVilfM(X6V`D{E(=pwYb0>2_axBcwAJ0mCklzvqJy7BDvcWUx3VG<+13d|pA#l84V z!u=XM=e?k%RLj5X-hwtxqS^MmaB$)DaWdNGU{Yp|jD~})BGftw&)pcQE?Q%^dj#m| zCcCB!>H1^PuQM_q&wLt*Q3P*vLprob*ko_g=d-t%&Kd zTOt^jbb>``pZx5;fd0BMucV$eb_(fRs^ku|WnwaC2gYd$-C@VdZ0W%qb?}h73Y~T$ z{WAQ{Laj13&de3OI6ZOFI`zx!aB#MsM%txyP$gyHX}w)%2aUnKp0*})OBvUKHjW3) zPD-|aIMPnQvyAg9^h|yq0R*3uk-HnEIT*Yb&z->R$@opMOO$>(^FmrxZ{tnEoHh!! zQ_q4qDdq4_%Br9%Z5k3v6wbKA{;dKR6YB4=-)ihKgg%_o1hZ4=Va&MHzmx40XWM0u zLoEmJ)(wuh6 zI5^`kyt|WzBV`*E;wkkmc_c>alU7W#pDMu3KEnG)HYb+w^K1@OVW*W=IKPwa)J|(9 ze~ND&`<{xIess`$@S3~rc1T&PFfzLlDziu0;?EXf`U6=>qYx%|6_|iL zOX+m`E^jj5oKUCT&e+L#PoaD0?>rBqD!rAllzVD?MkJr6@*}(lB(IZ@>*yt(w-F*b zQZ5;uXK}9wWf&CMGh)w_J+t?nfn$%d!#+IXXKs}_srRI>7Hx%T=)Lq_q*T!WN+8rC42AOd-vW4 z_CB%qslAC0sy?Xw;EWHx-q*5kmgIo~rwbMCChG3}R~RoWEG#I@mw>{e-eA$x>XUB| z;r^ToscZDv#!1G~R2t(s*BBz#EpCrlVXimdM!57(p-(PylL`IGA}{&K58XLHK?+fr zA{3<|MpV8B_ot}ZzevY1} z7w8xCBE3wnfTn&)zoK8$tMnTE20H6bTlf1Ny-B~Pw`dpr7rjk?#7ujK-lg|2Z12-v z$i02^C+L)aru~@lf1!`*6Z(`sqrZZ${f$1SFX&79JN<+HNeAg)^cDS^zNSNzpl_f& zGb`3?u#4SnvWG49vXA|o#sLm-m?IqHbk5*R&f;wD$2pwKd7RG$T*yUS%yBN^QZD0i zuH^n)#nn832XZ|(a3eQyGq>;{9?V0ym51^$9?ost&Lg;kNAf5h&13jD9?RqScplFa zcp^{Y$=t~&@D!fP)A&T5!87?Jp2f3y4$tM2c^=Q_1$+v3@u~cMK8+Xh>3jyC$&2_b zXg%lfVm_D8-a`q&o}Wcd@J9^xAPr*C-?AO{1g5u-_7^%y?h^UJ9b^Irad_wk?jL;f@G=a2X={4syRpYdP$ z0RN4@;4k^_{15&oALM`WSNw1Onh$Y;zfq)EDW#R6T*|FXtb#1%RX*icX)2(Cn0sLr zQBf6B=_*5Isw|bQ`l%e1tMXL7Do};0NENHNDp94XOqHt&RjK-`DpjopsDY|R)u{&6 zq?%QW8l(oRA*xjkRm0S9)u!6j2-TrRs!?jR8l#R=W7Rlyyc(}2sEKNlnyfn232KU( zs-~$E)pYeeHABr*C#hL#j+(1ZR`b++wLqPsy40!a`|32cP@S&MP-m(|>MV7(I!7&5 z=c@D6`D%%}KrL0vRJXcNU8H`XmaB`^CF)YOLakJnsms+BYL&WDU8Pp5tJO8?TD3;~ zP+h04S2w61t99x|wO-w%HmIA`E$UWvyShW&se061>L==_>TY$9x>wz&Hmdv8CiQ^Y ztR7SksfX1T^@w^@J*Kv*$JG<+NwrNqrGBQKR@>Dx>RI)3^_+TM{Xy+g|E1nme^k5G zJL+Bap4y|{S9{e5YM=U(`cVB@?N=YEzo?JZC+bu6nfj|bpgvb$s4vyu)j!lf)j{Y;j= zZqx00gznHI^(Z}BkI~2Jv3i_7UXRzE`UE{iPu0`(iF&&Jo}Q^s(zEnzJx9;gC+m57 zzFweD(WmO~>(lf?eY!qFpQ#t=v-H{e9KBedtIyNt>m~XEy;LvL-TFd(k^X^Rt}oV? z=u7nqy;5JMFV|P-Rr*SOm0qo{*4OB3^&0&{eVzW1UaPOyH|QVhb^1oVUf-lQ=$rK| z`c{3LzFps;@6C-N8hXO(;M~udXs)YZ`Kd$hxHcyh<;Q*rnlW1 z>Wv1Y(P%Q7jTU2&F_;EGOBqem>3h(TmfD)eg>(`9fG(y>u-;usm(k^P1vI%U>4#Wn z{g~E4!@G$#(0#NS>+P554;#yotB-GyE+7oOki3{JE-C z^{P?LRzFf})oto6^*5cX^K^wCu4m{j{g8f7|HE(_ej{uY7==Xa1|-!0hDHIIf8@VZ ztU9b(V zfQ=B~Lwdgl2F7-x0IXnv4MaiM7DFg6^fgiVMWP67fzeLb_+}HO!;Y7Z@-vVx;}fFH zX#kX)H43nqC>wQVF99H5zZrmiL^&ugw~#0gW#sK7%13ws$}dFPLc|rJ{-T9|H;IZ- zZhR=EhzDm^DA9eP>o2Uw9RiWJKMMMJ-H()z#i_-x6 ziE03~+lcDc08man%BV-$2IOf#cw+^io2Uu-nh@8#1b{ZTp!~rhzyYEmV*veaW z=u;QUJQZ#DKHg7@0X7pY#Ph;Wh)zd2XP}%j@O>cO?oa^AK5q-r`HP8`%pke|buUFbmVQaJ4B_2)?(QbKu#@N_ly}jaL_b(fv>feT zjx-lSsL_g{wT8q5bA0+xQ>iqFeqIElnZbTXD zX8?8)-GuO)Q14Bj5N#MsbaO4>Hlkay07!ES+Ih>@M7N?nw=M_V4M6#~4Fb#td`)!w zVxl`ri0)hnKpXG!0Co`l1nGXdk?3x~J*$cCod(!Ubl*CnjmW$4Hlq8{&P`*99*6-} z5N)mipq&pQ%|qQp4=*IzvW@7GgG7&RAbJe-JcjzVqTP=p?c<1h0{wh47qFjb8`3{j z4S1F4XIBtCjr32W&h1%%eMHZo&S#$^`uPN~9NeFOk?0pF=S8G{3F%+jL-g`SqE~SL zWjE2U(9U0>J-=Q;^eWz8yBqK&(Qk3ziTmrd0F?8_45Hr^0_FjB5WU$5=pp(&^8J1v z(I2J(5dIdz-`Yy_Uqb;Kh~7s2w^tMWaSL_~koO(HyJLyo1MJyI^nQ$JFWUS8+O}^Y z(VuYt5Mdvp>_1ln(7r#fA=)p1dOq4i^p{b9bwnTI{_#PgPjdnLi9UOi=&uuq4)_76 z`)_v>eU3Dr?BJ>Ec-c)Lr5yF2!?c4{_OM;_}_Z6{xQgbyOZ8?vFb9qr9qGz#8J}YQQJN0~Qkx z%mpkbu0`59q-|V8+`ON-1!WAX0PG|l{3`K~p~ORxei+gZ13rc!d^qxfpK=@8)E)z% zoggs*h4&W2Jxs`0O}mQg?P-H#K*lzJhlHfksmOm?A@M@gaXQkTISPPs7NPt_2Z_(x4ETij zY@|D90$?5S;uQdtb1up|4{bQV7VskRlHJ4?>?B@_xMjWk}o0v%2+-Ku$uVd z5CCZ}!TTj!h%e0r;J#uB-~jQ;{lu4d6JLRHR-ufm8i`lW20TZ6^*q2q;%jif7Vtxq zaUJsg$OAy0wOIhP`T7{3hxmpS#6KqDb$-B50OD>$S?iJSrfI|*mJ{Ec3qbfS&k^5x z1>jBM+cps2j`-WrmOJJFz9zmCee6N_PYMA`h<`c(u#@<1wEdn@fZfFRb_39d`%uot zMa1_b@1}O*2WkOpi8qf0p!^3>FHA@L5aJ&$1gs$5avSj@sP8cYu#ot1+#g37PvHIp z>UgpOunSw8oy1R}{GTE3(?f~3ZzX=_Zoom}XS)E~h<}cJ&!Mj8HWNROu;=#>|6&l} zRpJ+s{v|&EZFqSC@hc_7J4O-zauM;bUL^i?F7d0lzlJ)0gEYTwC*D~I*h&04^1snT z{JTcL4&pas0Ob3+w+qiOdhy$+>yKT;yQdMqQwvxN_>%bD6~yng6N3-( z`v}{+n;3kM_mvR;se<@J5AmN7zaMe?_Yi-CxW6<4@ci)_;!n`VPtea#*Aah)=g-it zzoMSMqTU1D#DALrKs}!${0sEqi-W{pzDWG{dBp!1OZ?9)0P-IM{3`^Q2G~jb)gt1r zhXQURJ~Rq|G7=kzziEdaKLfCb6fXh1NlHxvYyf;sNtpKQbCj-ss?N(6)ps9Ar)CiDvGqR zyGf-F1so)mf#=Lo0F;}#l~fiWyPZ@&gykS@?ix~g8%cqOs{FNpJ){cfkt!+ypzLBi z$5)doA;4{}s%asqW|Y~2vRY8*Afy?zkJJ#<*^2O1gbi&ZH4J%%O#mQn z_&mTaQf=r%8_H-$SsjB&jYOTJLVy=Zjb1})4C*}&~w3^gp)Nw*50BNVj0H|jg(x149)O6H69pT^mlGKb1q-HJwY$tUR?kC|6 zom4?5RkIO4=QdJvXOKF%9e_5>TT5y_;^yxnwE*v@ApDe_q`G#HIu+?o{hHMG*O5AH z0;z?FTZp*R(T+2a=M0p6Ci-$F(kwz%S6@Nun#H89MLX9Z?uU(} zuA2e)gw$F;sq5#Fx?vFjd4G)ik9Uw-_bMsqr)qr`shd#WO{nAM5UE=x0PuY4I#RbG z{PqgKbENJ7+==?{+)1hjY3?GxascWF|5ZPY0alQ@yOGpA(@5Qm`+aEB#u8HZdjOx1 z+O&t%1IWJ_^*?xk)I?8HqJW^X1l6oBZpFkZ?wgZrU8`3@n z_}Nxc+YJE9dj|QQolWZJqewkh0oX?BdE|Ma8nBtvFOcsgl>gG#q+UkaSCDtdbEJNW zI(|if8Ki#QN$NL%-=gf-LxAn1ez%$w_^Emm?fE_O{GprFTc~GOJ76cNw~_Xb(*Wq# z?u7uPgHEa5*-h$QqdfTS_eSBzic7( z@pGg;?IQJA7OB4q+)e7hSOCKRhW7jo<$VtL0`PYOaFEnL_mlcp1z-iKuTa-l+e!U< z6aZy@y@AxB<)jiwo4A71H)}}KSimQw`2cCPnzSAS*iYKk2o(Hf z3es5nYyULTXnd6RT>1L+vbPTxs7<5kjGS){W|NcUS#I(HQ5 zya}Z9YXQhpu#R*g(ie3Co&%u%IO5_<04TRK23SZM{7;vCLb`l5U?1s<)qvfkD^XA7 z7SjFE#{RbfkiV(|un=;g9}#_`($Pzk20p@6GxSl@pik=`al3X8eN-zv(j!`eT@w7L z>4uzn6b7PC>l?xi;qr!Xyx$w!?zlsxANsf-zmL)sVfPHS+np#ej`!lS#^(Brn5zgE zcNK?Y?&7jx98Z^fQ#dZs{76SlO-D`Q3I5U_iTF1~vLY3LO?XkYh^XoK9$phak;uy~ zlPk|f071|1pi^MweU~5z*bHFvL&|IAXmdtp4mW2MaD!8Aaaj#(cUd|2tJD$yqChkn zm@?Yk+l}%H6)(TR@nq> zVVBje*N1(o@1d8m02dGB#^GF_;c}bqAiMLqxuvnCxuvN>W(u2YDKs|q)EM?}*t2`F$+1{wz z@-l1fiDm}fX+dvUBg#PHfCt-7R=7TvRS@tk@db{TqTiCstXV#E6gbCDD#mvscKN8Z z$sWK~ZjwRHOJUm{Tt08N4ApLoRPV$3Bil$J)+c-Xky-Bdf|<6-7yghKk3XoDm{Zl*k;URN zMa7vJ?w|@_Z?TO{H=5Z9`pvXEV4_{pK5fr(K_X`Li>9Z(-fjAPt+k<8zx$R4D&y%c z|Aob^Y&1pPM&?I>cR4#=5etp@MvW3uIkX*DHQH@fxLEl|WH)%4dm^4-X+~>*fB%wh z!^jCm+@9Kru0mCKoUb}D5~OAGfx|qS@NPfc!HV46e9_lQlJQ%w!-X*$50R+SH@c=ezwOx0)f>^AaZ| z>s1~zF=osu$re3fw~zl;TeZL)^1Bahl_U5PREbu}X zW@Z(3(Z|t=z(s>p3|!=4s6b_c2pK>@gbOqubDP36L0>KQ9O(OAnh{jsnPqLNIm4Bi zkL~Ch3_cJz;AQuq;|I-Icteej1P<*t-F~mT#<-r}^aj%`J<=QWd-b7>_6;uxQajUv zy2FZr)5z`6X&shMi&^&dzLMHnb*B+1>xsFdv1qy9c=U0PFYGr?nt<)EkiUMIi~V6ATQf~92oU;FNel|CS8_VKaR2-Snf<~YyeAK&n)ceu#ovoF;t+KVX zGBLKYwJSRw&*p7%-I^Kh3}+s7IdIgw{i+tFvZJUk86L%18j;YDVGkmG`Na@gUp7ahT+`OWDCv|^C}k^ z+0u|1Pj6^BilsU4;QvBNNTWL4m90v(R=N`_s)p;V1c!d#6=fpc!fEf(y z0`ML%vDqU>zHc)y2zi9Aw^Fj1+fY;^|NpBudcWsNXU^atis#8{qmU zR|R|){64&R>5@kVZ^LsRT`~$9fHqQ^WWi3iWuPZ0M=Zbt+c4A=a={hGw8Oa#LCSG)&c_{88J9IDatilMuwl0bsW}`b38kp!n?u-^& zLlNa9qq#h(3pJOSK&nWF^tzbnyv60kO=XR3f^8u|TP(Un|CM=J1$Km+=@ucd>2E5jDLOiUXLUkx)t&B;n_cdKi)Ah?9tb&_SiIOU{2rHy zH#c*@@1)aSm)l_W`dIC(Df$7*jMv9e#nhsjnxd(_uRkcN`A4TV4Z}pyZgz)U12Lrs zLgaQTSDM#t;1Q+z(o%^GsNV*wG;CQIee}p|c(Y9g=KAQ7Alrs$+*u2>DYF1RTf@0V zwc^?`3`nTq_&|nARl{Wvv<1qHLrVaS19Ne#4*F`63x_B++@YvTt;?IKT+tA^7S1y~ zerCVN7*Y?#2#iMgB3cJcl`(FRVfjIUzswtQGJKG>JL;;Vx+WGH+KO1(qh77QSsYp- z9wSdm(NfxT77&q^CZ#A}*eF{LghoNXKhc(~?cg^Lf1T~{`YXb2Zjm$FWRijq+PFv` z)%!bmpFLR;x%Q3sbu}D+d_!WNTraTc-n{ytMk@k9| z0}SOeXq3>n>B#m5WLbzQ2W>lF7P(1YR$i9RcAuo5k^>@ddNkKN)r5+gbl&nUcP-7ew#U5dy*b{I7E)GS;#F_QkUGaZP2RJuRVU5Adg=5)SZ4&z zx#{7HmpW-5zvj~yv<4AofXmnz$W! zH6bf9R8}=Zu|#nm5?5@^tMvBdRaWLDX5$+DC40AeckJ*!J9LxR+v9Sn?R}Hm`M}wG zyp?&#SeeIn=T(l`P2lznij zlm^i^MbOc@WI6C1DQuza%=evH@p#tMtm5LV9ek-T0FIMbY0F5yG~xfB#j!70;?%78 z$iwPCR~KhZlZtQ|*++g#I8xeqI{I)F@Q{>lWkI_r1rJFr@58R7RFf5b5>GOkHWt*z zG4-Mt@-!F~{2sZyDay;CopMzki5pYT?VS)3_|I8~)gzIkI7wvl$0B$z&q1BwKTvtK zETqcgHBf0~HCDq-;6($K%hA(pEzZ{Pgtn48U1=sT?93dQNTL#<44bFnJ(IWSN#1DK z%ae5=pp96Rb&iT=V-efW@Mc9+;+?wqDzR#GQTv=X{-WIyA|_mm}s?0YO?WM27HUj%eDogpJ3QnrE519 zG%ZbSFyq0N26@r|);3&ax@^AgXnRmLASUBjiYlAC%R<3a$Hnc|nTZ=rFPd2w=PUnH zXS|7Veo|noJp#db_*-1=$N+fh8uR?AO^s+ ztaJ_k;wQeLC!Sbwt{6xoDi_y!#NV(m60t0Qx+^Ws=gY`g@x&8MPmF7lB2dmzZHYRR zFBJ*FiSPI7G5$@z@-rj1A>@kvm)O+7p0zhy-tb|>ml3h0F4f8PoJ<#(X{!a2hPS+t13Sfq7Ivf1rv#xf9_ zhB^6XSy>UQ$I1eLF3*aWO!H=?xz$f9TH6-CeU^mvSP~zx&U*Wj<0sU+-RW6U&xfdI zL9!lM4GXT1Sy_1*TRwMMmUmi7{HV(EbJS1FtaP`#e!}sW99`MswpOXO8TbeWr8`vDRgtXtrlkRr!)~EE|}5XaW4LSKx#NmD)l}CAo>M!rRI?)S%3a zlpcg?9_Q+6e8Zu?dkO#(Ek!)+Fhn4p#PjS$Pw{vv&a`8ESHG=Q~$EX)6PDeYC`|W2$2d6*_DEZ`OI56DQe@#fnqZW9m@3R~k&N^Qr&-~7 zzEkYm*fX#V3gsG;6Dk(uy=b^LfvTP_tTedLrdeND44l0j&W^lnWkq?;H{`|AUqL=w z*G}4gp;zw(SCiaZwBW5s10O zZ2R_xFRYe%GGz5+!o3}S<1YJrfZO$eWl8w5$eM5(5=H#Ds8N!0;W9}qk(2;yfy;Pkn?!iHY@Q>wM<_j*D-d2S zkQgH6<4L{7?vI!%?IB2NTp*5=CIE5})Y}FGm;|90kd+(r&TN9t5=*->Ee6@n=LBN0 zK;uk{J#a_y&J4j#AA&lu7_n(h2sSm8keTSJ1gH!|=0;~qw=8RBV;TxVC9>L?fh4ww zw=

&5g}Srb<>9h^iT=aHe3(3tQ;nR@hBBx-y&YapoQ;O|HQSIpO{e+1nO6mq;7% zEK%a5hoC?7ugK=g3+!zS{`f8-B@CP+yAlh9ZSV$>XzCg;kg@jrHh;riIHwSBgZLZ) z2+k~0%C3JX>fdSC50QY?jM$9q`rX25lFMS*3ITyPw=^~+MQu^Ny4P;RP}H-~k)c9F zXpTlLB1%E2N3>$MQ>)d(hIR*BMyfTa7P=o!pyE`}cHm(IPGZ^|z+rWb&CvoLrfmHe z^IeqV2GNddaO^2L3(a^+_9MV8SQ4Iw8M}kmIzx@?ojNB+d$YrsM3XHj!Z_E&!op%P zqhCLpzD2E@>@BkmnrMUmZV@1WS91NE^nz)1)QD{z=17jRc*Q%Y%YkiTJ|gv4&x{~cx~x&T7~T@ zyOsz4n{D&-#m?c*Mx(a?QEX*LzzJ;iRZY^tpcXYhCr8wF>E$GEav9)-e8MSn>^IDW zMHWt_sYN_&dzjb?WGfRBi$l8ye8Ayr!pk-#dD_y13#<=}kN>??!qo)rVK<_M0oqK` zSc+JrS?bBOlJY|4jYGGXH+|{W{15XcNwcs2Kb|)X%iS{6f4|850T7P%>?n0uU6T6j zxpS0k_r9Kz?XP>o*(+>)KnJWZJfaR@TD}al%X~PzXO68p@WK?oOPQ+_f!I{QU!%nZLFq3aUoAxY|mufvPfrEwy5AWq+X4$2hknQ&~eemUA0 z=rgG-wWvNbEAtpCH+rAWmzpv2L^BXGs{^q>wHXVT*9E-1%^L`K6JxyzJLJT^Y1GFi zG9X>`W=XWUS~4dN!~)2F(0)1bhy;+&qiGDz>%9%@#RzZ;hz#3WgcTC(`l5Xa-N~EK zT`R)|=9rX3hCp0PD)hw8g_jVGv4CgC7W!@q&!x~w52M$Sfr9puK6vKzVJY1_n6mh&ZpL-jZ{gaKqDbM3^Owet*mrX~z?f2(;{k zLK_C8QU%U99z>gK=r~!Gle3Ec7KI$slnOpv;!sm+4Ut^OmX~Raju7@y{mbOSh}DIW z=o0n54m4V4U0An5=yye(1a(;w9pSZypRL~b5AF1jKD{NV8OKU@p;K_5gnb^9wI=TU zcCBK}R5;!JtQw_Gfqp1fB3mDXxDkQ_CwCpi0KpnmS&DQbiRXnpwhCQsO_JN4cUo!z zC?x^S4mXArx5;p;ozfvLq``_*>V5-tk51JMUDV$7LfuW_ChX6PaKOmO8U*sAS@z)% zz)LCYlM7F`tUK@2I9ZIEx5|C1wldTLNbn6X-9U9%h?|8-q_Q$R782nmDWz`4*NjHv ztnME1(t<`Fz|s(N3oA7!=IC_FrN>e7K*K7qXyEcGRt=Bws&KZIcxpu2`44tn6v+<9 z0$XH2RqmWhYtHh@#6GNol!x;MKX`fCkTaG_+?GHr$)6N>?J1Bi&tPRRL+}ly(3#4; z+OACaq^c`sZ`r;Ebzuco07IH>Boc<;Hf3>%ta0cw4sU?+sFwOXMP=u7T94Mrs0h-# z&l+PF08G`Ixt#+}INzv~g6wka5}jeJ zo7m#%=d96;hC0q&zs0R+>NJ89G}^zy^x=j!Z`1U&l^wr&wttf@l~bL_ZL2 zc1#ptOpfj@!VKwa0+o;oD>xpeu^c94`joTvEMDB?h{qY_w3BBEt(zq#7F zP_R0CXjT7_-$I|%FjR2GkPB;IFS`al4-^&MSr$S^kcPbk!CYyS)okYGWsY)jzYNp; zFpr4Vv25QZTMba&np3TP%lPi4$>1ZDk%uq#3W=V2z5N{wkw;FwCD;|f5uU^1rnjmi z8sh@XlW|jh+sMDLYai8H`>3N@m$)(2#2aty+itZI1ZFJl)21Vv{8&?}5 zFJHFIxc=0%a5&9-@xx1ef z=i$ADQh0#DkB`Jr5nTqt3vK%X^pPW1$1)gLgUPC|-Yw29@bu)bcWnUn;imZX*kM(I z3^AEzMOsGmv`%Slox+*LnVH3E-3GX(M*ORuetOm9tE&;8G_BUfmyB98QWCaaAPFGy_H7t2l(JKOh&@i&VUi6I)3-f)g=!$<8o~KNK}qc+UJ9=|*!N}eXS=!Z z2jWl}9zJ90Q1j1&O*0r#T^>)DZ5$f>XE9|aRvLz?a%=7K9{MmX=H+TQU5=_7I7$tM zRVV~@k6V{Y3*t6p{xu73QHh`HfZG#G<2uvP2bS9Ytq&*L*{k7T!E7(ru#+P?lMtQb z&7E04nT3Ni;q#kbkFwJwJ*m_4}Tt|zkcd5nGv%{lhwZGKD zUfzWsi22cKh1Cr+re{pzZW&jeH44+!TIz$EaZmCwnSf`*B?9g00sdCNKUj23f^f4< z`A6G4wApuESJt%v@MSd zmrKec{)0C=Xm|r@#Mdb6RX-2h5erh1}VZQ^W4r^vqEni@fQBBOlq~qh-G=a zY2$W|8|fa9%rs4w$|pilWO|HDZ|5wgS)nzDU%$a?+IQyb!xv0YH4Yx7ysT3@q2g3f zURE5yXo<$v>xNzqEi37oCvHHF3-oX-TQf82;k(|)3;R{u`LWPnpFbeq751~cvaPHw zDx0M1l6TLyQuKOhuR^+);Znt`iYKh>XAk_-v*kOSt zF%Vy-ip-PZ98LbBj)%_kcO;&|9u>Y;b=*Mfgyl8%_G5A_u=I=}X_r4ZIPrl;MYAl< ztz16Gsze|A?5*H@hRm>x%&5qo8s}t4Q`qKPVoJf+c6v=I*-%Wp6Q^m>EfExE&g7;rweQk?CJZ3B==93 zblq+@`f&=@Fygn^9|H#l3toWz6!DK|Ibch$yc^ou*VXE)jFIgkra}WcW=^Z;aJ+g;SX;c>h0|VMn5|!b^Zjax02-1 z6KF#--Z z6nktweEsc6>#*bB6t)p*+a}t$zEu^t9o&LUwz4gVWJ4zMwY?Tz<}7a?a6#l6S6Ms2A?DsuvW|$BA{NeHAg8@r9Z)u_N;y0DD z8RbAWj=+wJ_=q^RD_oN*MAD%bRkuX3%nb_RLbG)?eagzQ4G)uYOl)6pBx!_6467g6 z-h-0}1X&}tS7dQ)yHtS|zdtRnT2&5cuP#WeD5!2906(HzcV!kc7i7AU_jh)g3@3`@ zwPPB#(%|5ly1{Gp$}~&81IJMh{0J8pR#g@9+5v4IIFBY)7GxSDGmBI~hD&bsZZ*=x zHvLGqdeZhAgsLs>gFE0EFWd`8VC@a&^$*ZDh3&J7xLfp*H|x9B)Z=|N;v?Cf4+8!3f_8G)yn1Y# z*ZmZO^slpbBb>#FE#A$t@@9zpVpF$dWTtc`Tb4)@us>^S?3qyC>jbl+xEBb1MI1tb zKr2)2tE&v=6cptc`GvEJPATCu^N7~+3#YF?{q*%wP^h@D_Bs%X1FJjLhx&^1YP?)H zJ-@Ip@%?y7+&rQ~S4aV;zmW1(O*#*Xr>YyX(q;36*T9p6vcv~$8Ss0QH48+BO>D=T z&~_yk@)E^0Y3&kPbc`|fT1IH=$PoDCBtv?As=t*6xjq^>U$<6P48g7)wkSCoz;#If ziowcxoX?#j(_%PpFftI8>&PbqG3>@YX}>-B%i5xBzs&&po^{x`+oTI(vi4}_&rZXJBefZB575^&ZWMRE0XP6vHcelFn zKRv{}B8a7k+dafgm&NPJvcfqz!<|^nJ!Q?!Wr=yX4h(vEu0$k$;#KEnV@Z?kNl#cquaCA6F>~(N+Bfk{$w^Up$JF&Biuqs&D{L$X>I8iMuDR=)8pzCv!C-#Ad&x@O3fp_Q#%jYs>v6-QkTi2FP)&iRa? zQ--W*t;GLSlCD%dSg*4lm$S1`+tWpuLW{G8wFOW^k=wBfz~G%ey0%|dk`&8$zJJP? z+S)O->YCco{=}U^q(Z7$gX>1u){e$BLfQD*FxG%O!Q(}~i+#a%*l{}2tK8b%K^H+6 zEw%v~CLHmR2YKv(%Taq%TV}Ycs0NyueQ#@Of6o(8UQbqu=0M2H?q=nYMgOAdWBn?e zSyO})kU0A&Ps%@b`abrC0<7cN9R zwX#N)dvx)wl{k5YuBz!rSF)qBb&}t+L;AJ@7Pr22I_FoNaz#ZD5{cvdkL;Nrs~t_x z%Nz^H<Q+dsdEhCK2iOxAs|#*mRB(V!X=(QEH#9C-ZG^|*sT614?9{^49G+&1erF$L zkTmvbeR0i_R9I!SF|WK8x*E7$P=#HlQPWcH{sziAl=i$xKZSE}w~LV@;)IqY3WjFm{#A?ND)2fY)sir>O3vbG78uJ z;&Pl9W3M;n%`O{O+BVm4c>%8Zrefwv}<1=vPs{`%14b;!8_>8Ot}X^i~9LC=y2d%GM&w6AF2(qSneKD}Ay2 zj_@h|A^oSCURD9Q@B^m*qez9<%u7$hgYQ#szybl?0@>-ZVjw{m6u0-I{%|D!!Hj;8 zpdWe7E*-EhkYTsOMtwPK)aka@xvjkm3w7p80T;D2H^F1iRt7LVit|PGvVD9v-(Q^A zQS9e}x=_40ac^-vRHs%zY~%m4*%@tZ8QJS+=MNc@KRYR>yFpJIfjK$1ZIhF|VLgnC zMAs;Grf-9sq{D2)7eBC;x4(SR_o=x>Evgu-<@*+gcFMK6-0b8m1a*dtuCT7Kubs{1 zybUqX6Y$<8*BwZ506|?boS8CW&b14H$+pRN4CFg4>3C0mcf(Z_7jr3&*@_kQupR8x z#C{_rIKZ{nZhNi{d;9{(ukp3}4{E2ELc(;(O*Vz%D2Ia>J+{SQi_Pl22FIyR@*+iCd8~ z7n{%++xXvv)}++k5}^aESz6Ltcv5*t-E6U?X3zG*B6oA3Qf0us)WaU5eFXTR2dj;Y zj^m~{4u$vF?l&JD$A83h&@{XoRR)*?)MO|d2`*XuP*71@+{ z<*EtSU@?>Wf8SNDz-7w5mxI5eARg372QK@w8%aDi#hYyFHK*fq9Kd9{I7kV~&r+w` zmb*)W{n6Hl_O)~35JI^BnB#`klpWX6KZq`=KODMAtbY%#4u>%_kL#Gh~m_k+YrIWQjGF@8XsI(=w=Gw~^}*fxGZJNQyh za@~|IGbjqK;H*_`BLb*0wGRp|+G|%h!0kmfnc>9Z%rGw(K9#rvC!E`4#m{FOK6^R(jZg9WOcpv)IO$ z);IEMf7qWbC#WmMm)_$~9Kw;ZOXIZxU!oM=2TzF^CxJs}JoEB7b1t95_l(DuuBRFM zwEoZHwYBj?sW%vi!j@he=NFJ*&gH;!s%K~@73eXBI4WC{&f%cqOt?VCY^=hA z;P7M9s$)0lw-ptIGYjQ%*MJ`l7&U*Cb5++I-LC7S?a@$3E|-;-DwR`So}<2BsyMg2 zJXgtg2~zDfVW$`gN=bSNd^h@%-%XNzahOu^0jxGgC+rn{4cnV?d`?z8D=VHz9J}pY zjZaYJmF89%o@msl^hT{p_$hmgs@&3ieqM5By?=B!ekEhk*gpM5Pkxg>G@u{-vQ0Ru`;E0v2f;T0PZ*VxE)P<5d0j0}f&9o?zhv&vVMXUAgM zxMkt{ZoOk6v?Jdf`tDXb(@&;kdPJ^x{|-v|O7?ovgRQo|`M2pqiM{bY+vs>8>Gy8- zX-5%kea)bY@%E1Tcee-5YYk9&VO@t!5Zj0ly!hE#vF+Q9V2N&Sse)BC6LGM$x8U%S z`#W0w$BIVR7&uHUaT_5u*)^m?rJ1Ja#vV70A{jonNB;1NY5MeeqdfH`E+cJK`C!ik zBjEaW7oP}^u5KUbOY@}-Y_A!+tfee!;Ttg8ik7w1R~JSt*&wo_h1F$4gOIJ2_Bv9f8tt91N7j!m&#UF1H?@Wi4z_xxNV8ub{9_|WW+jwaL>LWzIa zvu@NVd(N>}WQw-s*h=V@8ggJ@0Mf-1o)Dm)ddMFc|d%|1c|vK0y_PX+##QfFfu6lLSVz1>xNW^Le+zRgl`T>%MWdoYs}AI|DdWc15L;%s=DZs=KgNKzoOZ5 ziP~8j2$V_!&!Mj*rE=RDD{`ZWYvr9Uh~_40i(Nin33z+}dS(%Hy4T?QoQuJRO&uBrMts$n%x})4|Mnt((CyC0< zxNr{b`JRkV2VB*&z42GycV18XXd^?8t0wsyJ z&fKtQ(S}hTkH%ltF-&|X=D2WLhHl1R%PKcoO;=v<$#j3%@Z;UpoE^$HF5;=mHOA%k zASu4%WUALVVEVOAoRgQYRBXp69^?(WJ)GE=_*1{EpjWxP!9=6-1VUIAe2G1pFXD;6 zZ#Wax@*pcLUwaB$u1sGjFyHcctZ*o_B9@i&NXX|O5ehpYYr{EN%Tq61F+90kNC-Mg z$>419nfq?853Ra)=D^bdqXpVYdIALbyZ8;8g=RxsIDCD`EFR-7@HFHI0xN@N(YM*p z_1Nn=4ZBEzQTGK@+6+ATv8FwO7f!-ow)qdOj5f*#yTsgXe<9NpfhtTpbt^+Bd3mN}ehW&-Luee{pXBz!f#3LG)k;YYN;r!gXp;&w=PV;#K6TU;jUCQh- zF!2HrpN~E6U{3Ljd)M|rAz-fBx(@jBnZV>kcpDFKhSHJ~#6cCL16_`?D=DK-rbu~V@R_yWD zX1LuMwSG^rsUAJLS7-WFjx}UTAmlDDcZUK~u-a4p%N@%y?lH2nS`)7vyBqt?nKbE~Nj$Nr2x{?z#r5^Y zMN^MzYnRPmY`=yH_MTVhh&dqMTV+i>Fh^!pwq|&D?(}9jyK{#QVq5d3n@pb{^SpzG z+uxJTqC(_8d|b=P9d2xbn%Z3N3bQRmk}LcnIsO-3@aN3I+FnHyUD<7-|HH)dd|R*$#G1t6tym|?`Rqp9&s}5_^x#7K>v{NYRm$cp!S=ed zkLlQLtCh`-&B?p!EU2!Yn0RMWb#=irVjR8z?*)l{a=ieC-XX==*~Qrx+X_5StS-nO zT-MQ1HaNean%Ba}oLyb8UXtRH4Ws0GdyOEyZ&bfEF;zL!vHgfPBtO`@zYXdbotH4z6_}?Nx z&gA0eirVWZRaF=8Q`)Dd0hCWq18BdR3Q&HHId}*?K=<7()rX`{OQO2j?)Swu4tT9i zEfq~|Q-+xi3XW9>oYn0LdeIY1^OiaYin@+O5Zm)%JH`^5k;$D_H@^KS{{I+5F)~8H z0@CesHe$KPk(z9wOq)m9Ai z4IY;atF64i)81b~`?q_3;}LrW{yww)E#Pe19+KM0M!iQ+jcSUgbC$h@i7jhqH?_5L z=#Cz>PLz_a)=C&??Mr8;(5IZQm2byvhfN|MS_$DZQAZUimM7Wv$S~6znzW-^Cna9e zVKu2hs`S>wALMNXwZZdk*S%ek6s^MTTd@KfA`e5_3b>wNbupSHYE%2N|&e05x{;k?$xN z_LSvGgty{rA;5PBY+LW*OqP6yvKIJuuq|zoc_F588PMMM=#it*feRlkNh+19N>W)WNvD!ZHAy;Y(hJ>mXhNrjUZ6#c*bK-b-O@S@&DOZk zEFvPvh>bdc+u(x2$m;Tq8Am@`bR2bB#bv$$#%n}n9A)U^cR$a2PF1Bl-5~S-*Z=+3 z^+D(C=hVAC@3Y>|!vxSIyJ-7x?fl4(QhB(CiPQ3_-E?QsMqH9lJwjI>f%h27G1gR- z-<97+mf0|ks3!8T0iJ19p}T3-QKO1^jrFN0x<=*Kz0OCRLE{mp`KQ0Agw62Ck9HxE zpIoa)4f9_h6n@(mc0S??IRDo9&;{?Wgq!{ps(f^<`LYzd;qNC}j3|DAd;&;Ef2ipa z8fTW223;^JjA?{{M_s1Q(Ev0MsUhqE$qm%ihsHs-VaQQ4)rbkE9d#^$WY@ldL4c8K zor|3a+W;5u?-_}u3pg0>i5;LRaUj++j#Ge_?zn!(@otUu`0)(9Myb15JcuP@o|AhE zX?C@q?HOl<+IWvTO&6wj?3h08&EAtecpX39gbcrTG|jWWbSz|uUTJu}^`onzu)(so3+^|9ZxRiYmD-ZyQZ>9-+ zGr07wZ0TM6;Pd?8Ex*YDoFy9q1KWf&U|%J0HYH44p95%`>gn*-ys%WYB3=OLQ-9*JKtAw%BGh80T0)8c;-7qUc6MU8LtNW241w(r z2u*r%wj2M;zjwRu3k?s4B3!)p9^BV4Jlx@D0j_kA|KoL0`2d&EJEx*=&O_Gi`phMA z&;7*HR#6=up7tc>aY%~lD6!Z?t`LkR)KcmOEL7r6h> zxFID1Pk1=t*vhxOH9XWHNtBOw{+~P^xxbG(TDXTM15BSD0~@zteibt!`@?L>JnRWR zT*|tGozs5$AzR>0%-4A6{9E-tW)zy`!lX0=rkw6PK0-Y(x^Q0yzxdF7s_*Tj>Zd* zjo;3Rd#9&GOR=>pYOe4EAEbvK#B*PbcC{8K$d2sU&B+gV^Bj#V^=|o&J3t!=SMWQm z+v>(zxg0je)yNYv({wib41fxto)Gae(yA&Tn~6Tg$y$V}qCyaN$)}I)BuDK#jp1st zSU~t@v7jwt8S+jI#8EsmJ8M2GZgo57^=_!VUiP^Ty2yIct(A_&zNPtK z^V!W`x@G#!kg^ELkT$b>_5i(5JYyh`K}?Jm%rAiK07zDSOKd2NP8iNT#z8O~U?m#2 zduI7R^HcVxeCCTUpgkzw>jA_xyV;YlfH>5drp;)L`KF6Nk8IASU;x69P28h)P7r&V zbq>d|+SZBHF-uC7{8Z40R^a5ARFmnbQ{phYQNhV?8r~PY;`QwH&xS7Zju*OP8!i9? zD#UsG5UfYg@g3YW7JG=F09YsXlyxMMQpfT!YLi&E@Va6#m)Bg+_xAF03nDq-xnKht zo~=;x(5vkjNSc?yB~#Xab-sQGKEqYB?b+055IP;QqY}+lHXs@wAjq~?ti51@D656? zD8aUd8lM~428x3@HG8?Q_(klQG2~qe6W$A-VSu0^diZ|cKL%S zeUVrDqxvV2H-OUsbtF(4MvrqWS92f0ZFIcnq`ao(wRbmcgLJsn-KoBUr zD~@x_3@h*%2venW6a?oJF`LV{>$_T_>pz712FaF3@ROq=Y z+pkG|4BoBZJH`kL9`pzXgt(Qhv_IRKx@LO?zOZP`qb#ozF0l)oE1qAn{eWn08?H%` zh&xA$1qkZU0Oy7i$2tDp7C$h+VE#5h~}U%sLJZ+vJxuXq9P*R*y=}{mKHf>1?%i6 zjfKi(As?^{C%$R*<>&clpDAcQPd28vySE{4MVr^*^A5sBQtx5ttEjw3uI4Izao!W& z5DEkic;b0?xUnr?X)K<=w;pr*xMT5Mh>zjO7;NDbaJVlQgm(pTSixyu%OCl>fmKuy{{yCbdw2qOr>X;#DwsYa{Ch<^B zew4>qqkmrTZ{4s8jZ?&MBlQM_K?#x^|E>b~3EcopOB{?c*lBpffDjR5%||0kQ*p6- zWmh;DiT14Sjz)svC-kk$yLRSc(R?hC35PS`4=hE;hw_kfnPYU{;oRgb|9g_ zrH+GGH1H37LV7lpbBlPa1|;h`9k4%aPjp)Rm>`y{sf^EHtj)8%?=fCO`pKqrf;9v_ z4z-aFkuQioZdgAorD&Ew|c7Cl-(}N*L5U=i-hTVW^Q@5{eAT?5%^}(Q#^HF{3Hc*?q@dtfz09W?IHQLfjunGP zy1_b*dK46)2?{Q?n&&FD^%D}$D7ZRnMsY9{I3KypUL(zQV)NLh?h7`UFPDWwjmrsM z5Rd#~)0o)VRyLpAZ~^=`>*JF&k^ST~Ymd+b^)!U(s719Y43oX@$X}Mn08Q6MT4O2h zp9=fom~}1<)QtXR^2z%}K)Q{9=8-r#@&GyGsUdndIdFx=yJ)12mtAzD@`zu%!9X-R zHsTUoNvH7=&n0_}CN8&+P+%IyXq_9gEQ1tANFh*UsUq5la$2jOd>}tiUYqsyjC-EI zSbUOmA;!RsFYCXdKk4%qE>Me}be)o2TOP=d_q4atpqs_O8l1kXke)6U3eo14T)H40 zvAHnvg|*A4)|Dc8_8pJou~!y*ZWO7diA|(JQ;Z|%JtkQNhFT)^#4R0v&G{Bg!@<}} zEGf|-Q2)FkKlv=f5*zaOW!rm?w+t!e&~CREPsbA>kI&^#z_mC8S00x?8P4^W8rDk1 z7m**=@z!vAtt3&AYC!SPG<*gvK@-O`Ui!qbi@H!u(hq2vUP}SHYiVWgn9TH`PAdl{ zmss5`M(G3e;9AXa8F&Y8cl2a9AC_leC58-_KZiKU2Ouja*>~!=J;HfSMq#!^lM`+Q zS@bDD8LWsC+4kTlLzoF<_U|f=)+m}i-)y!|E;Ij_N;%=7lN)zwfwq9=8qdKs&bidZ zJ(=!Iru*3L`Cw{wO3$REr;sBfSFYfZsf@nV#m&8KZM|*B@-)b4a=eje^V$aW5{A`L zc4z9IV=JW??d6U(M|3iPTv|~_(pN6~l8$H@nUyAwse8-lPN4PY2j z=)L=tiJ@p- zL!Wxfa`h#QM~9~!n8+bxC~UZX;G<=T)}oLv)?x@x*a@>CwE^X9CE1en{mV%!VM`WB zD%S_JZY;4J|4wrWl~8sq7^ZA6t7>gA(7>Ov4a9O(hf_M?3W|GqS$bi-XHRP+Ef9M&HmE+N&a5)et zhlir^a*PP#WlT8_^Ri-ImG}_V^g{`!^oR7a*IP!D5pELkKz8m=m2>TwH5d8T5TnBG zCo6zB2N0aNp-wARbm9nOGT;KmT*Z~#&k{4lLz2} z+U^T@yMT6%bgV#O|4YP6(Ji$j`cdZtNjGs!xf}Hojr@SpKP+DN8+8s?<8A&@1AhrM z4&1lmi-Un#zs>6`mU<$bl(Gwa4+X|roW$AG;3`N)JTg0bpnhw7MP2`>>amsm9ex4( z(mMPa1E;|I6PZv!q(R$Y-@`Hq7c&sQ1cgH@ z011OaVQ^wo48uWu04m5<8qy6riGjuZv6X;w!BI?~9M2lDq2a-b%NNX@obE9^hA*=9 zn~$9r3+v{6a7Lu2hoEbT<=@u3g#Y()gX%~&L_oHvVCVz&$A^by(bLtP28+}7wfC`9(m>BdA0&57-oN|OR zH^zOzA#h2tA)n4@POUYuF{&lj$1_xzbiR2wa@LvA^2_pupkLkq6gKR(ecuWQ+>R4} zNFFxy?&j4AL?R7bw#@HnPpoS0GrWdBvh~n+&WlAK)bHakoW-Z)-du2#Z7)XXxy`nYy(L62bShT zE_1qlD%k2VLMOx9xFzmz?C5JNre$-m@A9FuM^8$)Co1x>V9fO)Vu1K9&$fGz9JRS^ zA~q0Sr|YeQcq#2|?`~P24)nOYPTRcwUDdP6zk6#_S$aOXRz!-40K__u{=*Uec5IYVK9Vd@^SqMtJ@1 z`&LsqVPAyjK zd*>@sn`&jaD$nMuTA~GGyu7O=n`tFNBbpwCZSzgAK@B!hmH`X^C{{r{cyNO{a*`J~ z>9TeRis3Mgpec+RJoBSe!~+Zim^1R}p=8UA?nn%)YAoWuu_ZZ_&I2yVH|z1tQsmHV zbJ4ML*}$(thDZZ~0bT}4pZTvw=vM>Fb~=j9vv3t)`EU>0!<3B`s}g{S7He0K@ojY2 zmfyN8iAWso45d~Ph=Q^>tWJhIZ-Uf({f?(0hR?5+hjIkG@D#5vE1_r29VDUp`cd3l zI}chr%R16S98Gf)h^C-;=sDkhZ;O7%D>us~KD&u|-eVO?2?nvzF38X5J(%&ONDRGL;s zWsAB!jTCMp#SwH5R+viH1d>IJRN^C* z(V>ybvg!!L&t>EWPErxQrjnt+YE}gOPLdCKGL~Eh-v>>TUUl);=SRFa*s&)JE9#T)BXZwSUc9c_8HL3beLIzuVlNYu+Ia=dHbLG_Hr*qCV9PFX zmvg&8i*9-&9kOfMrVci5%d64TchXa0XS#3z&+ML~XWn?zJdS~BgX6JHD&7>*rINiK zM;lXiib3!?^G(;I9PI$U4!I7)>lk5_Do_6<{gu=2q+q84ac1PUM= zLRRwe_~$-gLg@D@|kIHMK5L=@T^?+;71N!NaXsO@h&Wg*FsT zOE=UdiA2||JVR*ReXqIAmTnmB?b+Vm)YT*UN{licJ{TVc?{^LRxvG5YO&C9rE*1GF z7D~aVEI#KcWGxSL|CnTbpxP%($Kh!>hf^(t`8ixO{+T>^N?D27LPSwQ`s5HBKYj%TAQ<>xv)gR}LG z1@A%KK-*Wz_nJORw6ifD4OCX`p^|>-8C)>GM%J&MyQ=$gw4&~n|-telI6Y){3 zLZ74N7aRSUXN~Iw5bGMV)S(u&(o?=`Iw3xW1d7WULsC|FXMX;^cqS944awn0end(H z*+tvh@kl0a-bPP}i{qJNf42!FHpd3qTp3Ujwc}2*JAsE{nv4gI`~4#?sZXn)k)P&* z9r*{|-Crvn(H0$i(MiAye*h7$6zxRb(oLP<7r>tpTS$@rDk2CR8)RmGSg+gNi}hZw-Jsz^{)5A0F;+TXkB4mHPN#2Zdkvb9%;U<>)O?nd1zvY7=B}%q zf2TpgFIw_&G(+Loq7m{PhVDW44fu4`PjgLVi+K!I2o-a19&@ltoTqG|jD%bh=NpPY ztT%!nqYv!~iDAb4HVm+At6`PMVh6bjsqiZ+&tCaXmHYIKDROA=`6+(O?e>r6Md=6X z4($Twn>dt9ehNWh7v4Fp9J2vGMV{}*W;hR%hX2d?X{Gibtgz2RN-s^8WVY*vHCSX= z33e+qjDO+yvW3P^@JBIj?e;L>@waJlo&ID?W5 zxr8%CVW5RT5J@99=Rso?CXue{BwB8wLGL=8jprn+w|NZv5Yn6=p0(PFZmlDK@9>4A- z<25&)dD)4+Ouwc|;B-8+L~D|N%tnqMi^DqxiUCGaqeLr+`z=<0cgAZ9$P*l1$Kkn- zoS?Nsg|D{%wAGH=U;z;wT8mSN&3niTm^L#DbVM5kxt7;DG_TvC-R7vS2nP0l8sL6XCSx^CrI;59rQlR5x6%fZy$nbs@AT zDr83{nQd0^GRTFz`@X`4bCO=4mI|*}HZ*#k_zI_iM_za>9UdPje+`IBF}Kt0)g3N( z$Q8^oH-Y$N*^=u{Ma^fK``Lfr?)6jYYtL9-9vF+Hk-I5@dTT)RS;_I!G2mFhE2K$f zi9)TZ6QIi>mm3@12^JG}XJKa+Vsc0aS#xHzTwJX5QQJ4Izr`B^4eIRLDkSR!^qEoC zZ_v3#nF`p&LH3+t0sq3^6XaI|nl1EO%Ma zV(d>nXK%eCv+D^AZtKurs5R{lOvd}R)76$y+V7>Ih8p5 zbIWRR+co5j5NxM9TPSb6Yx}upJiVwtNITZl1ikB`N+6qh!g8WubFw|)s0AC_{H`zF z^*l3lF4c_xhNNCyom#DVw{Be#@E#(z^N`p4JogWNtc2T@Iv5Q zp_l}6mC~AmZ%c}_xS4V!m}k42T?gF~`43(cH&MbDT=6`FZkyKpDUY0`1ZfAg8tCINuH7~*J*fb00afLo#{6|R{520DLZ9#i zCP5Qv<;|d%iH#=_f>kRB>Z_S}T81vnLo_9+I*Xs*gN+9fwk z50aAcV7ib^I?Z1raf>WW4X$>yi`$NIu$pK_6+T(UwAoD zRfl61)TdYo$ypp@OkPGTq&Z1jG4uW*OdZ5J#*O=pU`#$%yYrQ7>B;vG0rv?vaW4*~ z0MRtt@6teqFXRO3Xz${DHbCMaX!`mOA@I=V-3u~q{{B%)YjY4I^tRd?~1whgFG3#b{P#R z`92NBHhbpvyxUJ5>2dXHSZL;13;Drq3=eGQBVQpbH#Q?>8+@ z4FzD@(&9QDU1x41(TDu0L-hL*wnvb?0MZ7n{73!X(Vye;*Za3N8*3A=HrpEJtqyN! z$ZrGiD3exZqno9}`@V(ut+x{y{mpsTUvK`G%4+l|jUl@qbk?|AZ|gboUFv|!TP>6M zu6N3a{{!FnfYgutJME7=T+ayf+4&o8xDS85wRDQqb{^r837LQnq=pM9ylfQ`TiJAl z-#&<3rxaJ8hEId?<7H2m46U`{0ue(j$^=GjfHe2P1r;O$JqX_nEXqtaBxLSk@frBH zVB+@A4(1SLb}DiyU6C$it_19cXr~+LJ($|^5IkB6>GO?hhxt8mE5H-dg$dW+`6A|} zh4g>sBO74)n&sRGqQrq}3R{UVP+Dnrfl}4h2mC5QS%IB}`(?EPKu@6N$cmhzR0rlq zXzPJ1<(hTgc3B?v`yE5_$CWO#m6C0UUv^c@7a{@R_4K&BVTTC)s@-YG%&*W;Xp^g( zu3_JlPDamb>a@o#Aw#pl2}?LZU4bd1VN6CV8c)e;6n|(M0!r<2iu-*z-_N0B(A?5< zN@gxo8H=5s_qtnK-QM#?+S=CQwt3RePdb09aG_HVhjlp8+~mU>Ip1;z!hKH3WGZhc z#NtkO>lv+X=Sd@NoA8d$T>gub#QQHOOgi-NU|4q$6ae^=X-g(Inj)~*T!d?+5`a(* z_Y#Kwf?}le6-5CEU@*DDu?qmOm1;plA7`;N7_43*qw8>#I2v^u5P-v$1d85-2F3g? z<$4hxuzB%Uk_KA9f4XGS?+*!i*|?XI(7=>B&$(d|<5xJ;Ew>CoYp#61uj%kwmvmy~)PW1s%#Ej$K?2zlb;C z7bgjpjeixo6m}mjs2!8zVJyL_6!iiWG;-Sk);z`^9wm!A<-MtD4B;Eau_PmTu?s8+ z=pOnXfEp;HDz?X;DjA`S(+4lji9lc?(t4O@YE46=4-GjXzZq`lga!k&kQ2xnDDRn- zKv9#kG8l3V`=pZ|@`d#YuXjS14@44F>wj}QjC5*w&=4J1Vo%}wi+UBv~{!@2i(RjRDbm1jkQj^ z_<+M13Tn%4Uj`+i({Vs=E}e9e>e=T`3dLd}^QlDQsQ!J~e99GsdHAA>5TX)viIu*E zYp(_Wb+D<3*iePxyAmiJ(0nxXIfb6rlx9TS8oHV0Aay@TuX1PN6Y_d0#&Clq3m&X= zLQ)jnfeTH8_Or3y4{W%g?b^jXElNm}P3p~_al~ZN!Pur37M<~ZIKDvWrH$Ru=F1ms zsP-@FTpyBmd)*;TAi0zRMBg+Src6`T$r$eYF0$32Xcxi?`x9tYRC#Alo+Es^jbRf|l ze%R7$6Lbw)fpoqQ_r=E|8AF=_qfAX1a!ff}!_GZ?jZV3jh>Zp-&bP!$b+Vv#>OQ(s z9*Tx^Jk(9y+KpEeu#xMCaBf$6YTOdf;2m6FAh!M~^*(>@V67Kve$E10p7IHGf|EFz za=6u6z^Vy!PAGX70cp}8RW&7J46}n+PLW_81GolRMltg?*DStiR~LnNvp25KMlRUU zobup|tM|EGo09tZ&~^K@^E(H}HhqCp8y~1mWV@PXJIdeQ?3nHB*>HiO-Jxn`KD%kG zw_uzPQPSs;=eKU!)vJ;y&)FoK=da3_WP3%N!ZloSm1rz^v7r{#9My;fO)_9K)^3TF zgarZGY^l}aPV-r!&g{Tlzk2LSW45buTMB$@w_sPMQO<gu@n%>&_SWJqS|+MUu3ksa%v_WSUpLi(bVaO zPM;#7e{yvoojJ9Vt)AI2+}3kOTBOeCZ5!_B{%Z=%;6|L6cLn*G_G-9qV5j|>{hgit zeUWPW$cBt}xTB|eQ%^SAb4GJ_$1vQ0VEsY!9f0Jdc=r9!W!(+AqMLpFP`?IF0ho+k z0U2wP;1mI;?8J~xCME?X&|2cEyo|xmCi|zLUH^RBilJ5aJyD^?WTF|YGC zOJ0?Cuk1I!MrdeaqBJSR2cwS6ikTG|N5mP6=z6T@yxzF3M`HSMu0uXkIczBJ8gYy? zmTe;$u=vE(N6s^}(bG;FHE*CTk}F(&?y09=dOH3gP)*5@gh_Tc>{ifY*1mv#0_g)^ zj!-TQ-j5pm(k0H3-<9V#zKt*`Vv`Hxm5#=ttlid?LGts%GA1S-INzl$pPgNPW4kWG zrC>OGy@U4H`n8bY;WK5uw2e)Y9EGk0@?XN?gV@U^F0*IXw;M% z@hzG^i*h4M@3i+{Aq|o1cz3li}Xr^mMQ{oOH&f^Ruc;&VS(iiS65iK5ESE z+exjmZ5w(AK>&OveuoCnPe+#reGYor;R`O0PUoo?UjO=k+J;~O>dWohNYSu;8{R@* z_-)&vl;OH4?7_QX-EW7yZHe?2^TWDVm_!vzzA4~WUM31c_>V*rnJYpY;{I~(4;LmBZge1OIOgZ4fook`>mIR!=e7r-I+jt1dr*1 zs`R6goIa+K0+jmxwQIi8+ zDVflU)3;w!Zc|mdYKaPnrsI0EQ`Oqa*K9w1g;mGvm#B*Qehw>I(w!Z)+6_(1<%_`8 zE25tW1?d!uID}A%QAJTwM84q#-c{+s@KUJI>LLdn!E=?WXGOhYZ=E^S^;Q@``0l^1 zyLa>3wsyCyI(^%UmI#Qil$zJm9P>nUttHXc(Une|^7#f7p*uLbFwa-`P~O|`=$-f_ zU~eYd;)CrK1S2|P9>1R?OJo^E?$)-p(bDMPnQ!OEr+B-K-`~T>vwV#D_OV~?AE0R> z?~_f?BVUgC0@8d6GPSa?foUt|QDIo+iKVfZd^X@=oRV%4^pd1;){@05z*8sD9L!I? z%doW0@bkxmWYwc(4lWzjBn=ue{F7y`=$!2<%oke3czJj{?*Zzy2*e!4mfx^j$2!C# zfDg$gUYAT>mYPm&PNgn$d(U}UGd6?J!?V}-I&a>4*|am_bbDN_#mQo;%M(f7?kG|&G4AF^c_J!b)} z{u0a+_WZ4~?oj!+kU&R`EWs%*i=$l{TV^zgk=D>SY_K?{ohu`8sN!<88YA+T4S?-`EfS1jCcnWpgO)joxE9f z-%SDMr0a4iu64UE)3q^b{@X9}`ECl=bjnYI#t@n%k1dG|SqKgR7`vPlAm4G|e`}3Sle|GUTM-0Q&ssN||u8%wWZ3t`dGRzb;Ay`+)eptE1 z7lsQ*ogX4h*|~HcNzP?gI-gI^(S;8XC@mk!$MPw&m9B|j0(rK6LYM3tsXWR;d@R}b zAArX?^tB*N9Ck|TGTW%wCq&4f!!+nyv7%3WhPIDv@#ub!|E!5V;p>~2=rdpJgYy;D zYbEp%e?s(bfwD`p;g+$1!7hQ9Z~VCJz*Gf6JT9a)Dd2eJLZmfpdR^J^H}E3K|{PKuV!xe@+mx=@Tv-i{lVHicrLtK9y$&ljt``WF^OXq z9?lEh*Y!=jt#Qf_HV?P^I<)&C$dJIP7MS(S&5@!fM^1}|b%iGncM7vcqY&Uq(>wYL zo5swi+-~Rc5{0LY_uTO2SRYW$`hj3^Xvg$J9aZB;W1B=_U=OFrJ~fa?_uvSZ< z-JdhA;opN9OEMY;L1M~L0gE}Pcb#BJKgmO|`WZDxKT|~-W`dR{#|roZ!OC-@DXKD$sADD5&23{H>+wxEewte@^ojK6(G6 zkM2KtcL~qAW{^1H&hoAva!(nXqg3s4W8|39vuo1?oPh_SEU4=Xi-e&B8DQ8FSK4Z3 z78IQeoq=|ng1RvMbz;bX7DN2nXmL~U3-16WdduAbW^*R)y#i8V4uDF-4j|J|23!Zw%7lP`YNB83_IuKdz2zhQCjIPu}2n3|^5jMtmfN$#} z+V2)XpeG##p;EACOU=KBNKc(tqSZlu-TdCLTa-|JGjW(4dJeNc{N{KDt|lu7%ufxh zTq!?fe$PFOhaxG7tMF$36g_m<9}fEu>ZkLgv|DkV*;6-95@-dmP?j3inXAw}blIB#4a6NF{;hxCFM zwBbtOA<+I)oeF`X8;y?caGvTDPg$3dyb20YAkpLEMSFmI=W|_ps*`+I6xy!Zk~53* z)Jt7HN{PGZe(erWNR;ot9TS;^Ms=hKjTt5SW=mM;C|jar9Wit+i7K>gw8{Wl$yN_9 z@`^toU1a6r_{cpfLNk=ere$xhOlK3Jx7F}YT*P*>dU$c9|Od2%9@?l!z$qdOf+ zoY&|V56_Be~+R{Ol@k)Esbe zgXV;t3nlG(kg7^hlhqOHiM9EPA3X-pqp2rSAu^r{0gCtgnBqEw@ z@EwR#7)pa#R<1_)x8|z0>#m^3;Y`Z7!|4hbuI!jwLWO&xo9WV&mPq@}c0`WfA#Namu#EZB+L)f7oh9sWJAUO3A$;w~6D6YwpPpQq()DB=}G^75+=Z^8r>A|78tx7yW$?Ue(Yer#boC`{%u8_w$7q21>_(%qpEVvAzvQ<;4W#;{$A<xK1+twR$sG!>)sDBo1Ote_} zGg2F`fy8wQvPazg+FF^G9@U!4oq}gAd@ypPutUp{uH(;bZFb10ePc?z>ee^EPkvR7 z{R-p3!#nf$ymP(EZg4fe^k?)5ds~TE08Uf;qXdA#8>9f{iKP1NgdKAqzpFl@)_Kwi z9%6sKT2?bwD1)IS#UDu=lyw}ls*Sq&wqbwIw+?&&(Yi28a2l{CIu>KOVxOrW`J$?N!DxrsEbx!lGHA#v=Czf^<=4 zQgk;{H5h-H+CjpI0!5RagHV=4JU_^x0dXRa)tDDk8>&dNYD^M`_y#%rWJ*wtB5$(* zg*qkKy{kKU3K>mtR%W24GCk0f(cuS_`JdW#&Dn>u&DTl4|BU{r&~*;rW9!$2R`x%} zp)60;9EHd$2rpTmpbzG9_$1P?AOwwmtvllsU!hO8b{+XId0i-U9RO_qxU!$V?ubMj z^h5nCH>((lsQ@hwfko}M96n)tn{ICUAleOrg55qOn4)>meD%g5mYuAQ$26>M^W@P8 zN=1w@cx2FgMY+aB0k2uDO#(iap4vInwsGWwKKjbyHm*0Qfk;blLu<0X)#Bz~ldEoA zUomn_fRLcEqgl6(AI>pZv#$|}%Prq{DxS?f%y%B^Z!ku1N+&Y%TyxAJ z?&YzviaXK#<-lsWs^LQZC0v-sV=sx88i?j@@-=T*HF8FNs5RM|ZXe3`rk^7(#`bhevX##6OZV*W%rvLk zP{Q2CD*5%y$A>)pK{etQ?Q1fP2Q@up9Ly)f`$P1M$~Ij&Fy%&TE(?t z>QHVg0!l1a3Y>g*kZGM^MZ|TFZ&<2Ps-+c?SYunb;no`$ulupb%Wd(M&!&5+&05AI zXp>9CwXRhou9-JQ?O3_uz_AU~J^^P>_T}c1muPxVR8E(RQC1zZ8w* zG_Ojxpg~8`+JiKYNY{TR_?oA&3VO+x3QLwBcpG}Sv#Wv)R6ye00Fgr20}w2b6YFPw zAeWT<^4prW`|WS<*kj(gTlc?H?A&AC@7I+d-Rbak{to2S8-extVdi297%bN?MMPN;{S*+} zF#D$JCvbWZak!}B>s>|omatYMD-NY1%O;_lYE2}&2Pc=|sPR;MxvR~iBTC!j2*gHv zfP+&7WQy0ZT8OoLu+r1oGaB~ka2auUGTmp672qkv_&FX3rF@VKw2qnIiMPm9Y?`-D zfwvX67ZZ>?xTagbPsMtPc3=NGhV+Bo@Qx8bL-MG zk!baX{XhWn`vbf`sdoY?i5&5i5kGTybwC0C`2Bu9sPvv`{_Xwlj-=sD_4tI_)tSIi zug`(GGK)AG(m#^t>w09aYlIs~p)-6fed`JMQDB^1iyL8$M*XEN-KXl1xno^d9q8GOh z83@ja_@4CjHD8Pa`MKB7+L6c>cX!&`T=MO>-`$h;rb}WDg-EtwQ{C{ACmoW|z6J~3 zZT3}6pQeSTweV&7M__?Ko0}j$kZg6NB@%A&xAAD}Pg^55MMehX|1jSlsl-2$h_%XP zX%%Oz?0{DzBCiwtbWM+FU`UgqpLk^cl8)F3J9jf!%uyuXJ}92a4K~Le#)7XSA6o#I zE`}F8{=nO&T&?L~-tevcOL;Z;_h(Z@jGzU-5on7%1WrDc8~pu(2kFhX`dmSg@_T=? z%qzVZ21FZ9XS?KgfJ4&>-0r3*DufwJPIV|okv)L8JT{SxQ&D(Lgjn%bpl+`u1JBGQ zC_~kb85K$t^L`~q8QY3F(k}z;BiJ~Edggb#Hi;*sNWTr(AWTlBb z$>2!X^r!zwHIL5BTr`s%9?oJnGuoV5`C&tkr^G-k*+Bc&h%iP)T3fBt{Zzh zE;i&>%y)VC@nxMGH+C-LPk7GzJ;GOYbA!hbFYqh$b|xEl1jLsDDbJJD2Q1ycy-qRZ z?Z`v7p@Qb1S|uxlpm<~zEBE5XANtV6t7>s`$qya=5cm-rYbOM>S%tL2GmeG*@Q6MN zkCb>g7ba&v;Oj`g4WJ`TU%)5L{;jY=?PCO--!Z5#Aa=I70V(V-_lUuMT^AeFIGAtR z1zfYoEL^>&4yDIJ`{v4Bi;sz0XTAKQ0MxCvmI{ilZ6!!8-~}Jf{&cbg)RIr}_S3wj zF=u0iE1bDS_(2J*yBJtJ2XI^?Qunis=sz2_z`(DIs_SXou-KZ);rJjO9?X&4H_fqU zKY=eazVTM@q)Sy>GRyGBQ6 zD$kE^NT)|%3gUT*gJ@& z;wx=^FcoQ6%EwFDBfph_Z0ULPy|^G)KAXoQ=6lJl;kz^z1?s2re}d}=mq3#qJR8q= z+Q%AZKC!Pv0lJW+8>q;=F4wB?c+WuFUOua4-X_O-lktfzxdm+grg3p;I@>c|n35Ww z4vcv{yvv1V=@m0mp4&0nZM5si0OQKQVk3c0muE3m@9j`b@8 zi{v=gfH%T6xQ|;K>=;%av@ZSj~2UfcC73+JTZ57|I~RQ?cWqk?kD*-@Ll?mZo2^V{UhJxvRFU$P^~<8RXgyrEf-MVyZK%(&;1aET`9 z+uFQ(4D%|C7-!($T5JC>322x{l_<}tTv(LOW@oHlAqL~OzXg7h`4RaTXU4j^f69FQ z23&bq+>4v$5dp=P(%aD(qkQbg%|VI9-7;svl!vJc(-wFbCytY`1gw|E5qU0=29vj^ zak6Dpm@+Wm12~Q#{^Vo!ZE>(`bRp>ZA+0|@^lU;#-Cxq7X!YC~@LU0rFW`Bbr(tfx z{#?rf&h}l~6ooc`Wthm;*+0mZzLPEWP-H{>kXD_iO4&nL&9KEW4lt+4jzh87X>hq) z$v2kT5HdSPW1S4DJnCe2CUG9O)Df{1Brwx$Sk2JtxDIOtJpoRaqRg$kbVT8A_NQ6e z2Xjkrf)dHQOWAPFj^6X;!r9U^h#Osk#0ymswz)9rpdM^Ee!l*QI7lzkd(Ycn68o;q zmaO2uGHB`;Xla=RXWo?Y@|f6R8IQW#^eeAa51`4X5nF;+#t~&mAK-DZ4SuhGC;uIE zr-b|xh*P!VVpK~&WTGXbsG&Xt`G{(dlDoSkFLyawJA#HA&cotOXHGdjq2!-oZJ^C; z8_ z3{lm=I8#4Cc@3=IkYl;LHfgjww;=?ROHSO$NEAHzi9G}*M*8rG_z{7yT^Kxf&3%MK z*&6H{+_rDU?m|WDX{8JI{Y|51Ap8X3H)ugRS>A%`n#)z}&CJ`_WPiVU2)fj}qWiX< zH8ofeAHm6xdFzS14eMTnx`_!|4GrNO!qj!Ca;QG}Vcem4MsVDe9FisTx}w5 zBVF%Zeo{|c0ytB`sX1H@4KC4oG||>`((=iHxF~dvL<3$#cnfI++>uZOWL*HE#vh1{ z6|y3%IeWyHh^j?Wm`)XW^MDVL0519#eafXrJidXERi};SGUuGU%Hs}2d)(ofd^Fa1 zhF9;6hTZPfE6#0hbGVkxh*c7F3yBo)wPFS1T=nz!;axWj3Px*j-oOG(Wyc@!q}1gr zU~(8my($SH+|VfRqD%zw7zAxxMSxcZp)v_02B>Pm=SnyE&Zb+MK7gKN<`H~V zZT?Z0+Np@@huo9IYhexIHHwHZ)JY3J6_JflR9K5dHylFuh1>Wh)-4($Rr);E zSBlQZ%C5|n7IP_H#v0LCah=9%(S{95FKh2lb5%Oksxk&n9o+dCbiaSz3sG%~nbNnj zOOO09RzLfz()!w;|Iy=?0yzqvV-DAiX!%SpXs0bZD=d3EQ#wm2+;|XS0K;mZvjGcC zI3_w9w8$B56l;B>8EX{xSrEYc=p`t8Znf>s(JN};9Wf|bfES{y%jT8!V_B#_BOilJ z=?FmFv6BgZY#w_8*~JbhyR>UKqnkiNK6Z=o=4pDO>0)`F{2bQE#!R4AOq894jkP7^ zM5|N>qcr5}r=i8C7HWb0uyr>kbnZ!RU&18F^d78Vs2?4DQEudCALs4c__@Ok$L7lo zCt`2IiFF5Rwwk^_%Tl}@@kM@8HO+rbUMtp*sFI+2Oh^Gov1kvei#Z8f{IG>A`1%v!tNMQ%NoL}aR_&rN?OPmokXnCd(6`2wxh?2l<8Hf+kIf!Narhdy%h%YK?Q496 z;2_$1K-80^D`9E?VM+N!s$fry!+~p;K}!aFK8a`QI3@90w=DX06xJ4uuueS0`HeM${We! zJ_@-JF}c{lJ`j|UE-B0t>P3}tCm>OB!^9?NZm|Rj=?s{UxHs zb-GXdW0Q>~knAGnkeutmVN6l<5;%-O@~}pOKwDTt(R$YJlOEseUWbk5#~zzS=QWfq zto%zpV84Lh7UJTj{o-?oM~7;V^%}GWft-s=*z6vaZzq1!9H6cB{4|~?JHZA!Yb#2d&mP>hl#6DF7HB7-}TKZArFEP<@o0LNcNd&H=NB+ADt zPbFOIuSs$6c3v-T55s3I4t%Pghtl9XLykN6@*N$c5)N8UvkeEs9nRY?b?kLh?ip=0 zs251#YF9Is6JRUtbvKLevU5T#pdEyb3v_{KbS8|u`KWWaA8RMxwh9$ ziE#{VmHBgg1>X{Fh=DdVN>H=cjd%;{Yuitkq-oMpDa|FX>3S}V`s1)mUQOyj6JiX~${(hY+!;*`Y;e_YP zOT^;hUitazu>2^8pi#t`d37!BOst@*n-_1?#koa~W)r%&<(MZIz4#m=+-e^_`UpX7 zs3g$3E|L$)hnQ;uFJZf46YpfRq*x2Vd=meJ_3*SCQu&MMqZj2<^+Wm4Q5URxXkPT2 z?Y|VJ2ss+61fIrzl-zSfzKRubYa#uR4Q6%tiBE(9_3==;V1A02GIFN&JQ1Q$EAZ0Y zh<_%mmpF7E1w?n&zllt%CeoK+ulsT4^hT_&@>q4>h7Zdxomb#0rFD*Q5c*^YlH8au<0CjlI zNil3-pNaKl9Fad>XepYXYbj2hwRKNI zCQ`=kecgLzXAcsZ{tL9Jb@fAV`3oljsz$?;@MByPgSkEQ7B=18-O2DCdLA3pg={to zUuY2it8)$YP~OgDolmxr3%WH98EArh)!9yyq(Fux}{ z!=miWbhNqpk~F&;&#hYCyt&&+VIMx%vQcNVgU)8@Y&FhmRV<_r;haUQ#3PDA zJw;_c&`m@FfK;$Yh*C0an&WfGURp3;W+C#Cbm5^-JmFg%PMT-;jE9qR<9{yQYGVF9 z1`Is#cNRm3xN@Qefcj7nuS?MZ^M@A@1N`R=cSjgG3Yhtaif?pntYEn%#DAC|5Tq#1;Rzq$Q4SH3Dy;CVGe)@K=qw1HYW0 z7L**?nn^3FPpx?NqEvxjhN0bAn|o1cgey&FfJc5)!>F|;9@((e*tB%IwF$<9RL#9% z=&5xksGeOg^UqfYDB4( za25Yr8)I$PirzSg)Dk#r1@6wezD)LL0OqI|99m1f%wK`m1(m=rqrEHOFo_2x?v!|e zDzRCJy;)2KTiiczT;D`F7dGQ$jf*DdUENY zy?JLoRVHQ%8;oHa@9$5r7gM&AIatOp|5>eQ>BqnX>A;6#0g7NTh9EOkM}|g=Lu$@J z$)Yt>ptDTj(G4|Fk0F|JpmhDS`$tZ3S#CV4dnOushcGJD8dT@Qk!PG%(fSX@ z7oS$Zm(q}_Ndn5|WOJ?~qeq|1Wjw8p+L)KZjRs-?qy~rx2WZ0atgCNiD+3AB=1(-a z`AmF_UumbUc%)7M%%|$dYRpm2nx8;_Lm3U%Hd;AGPe9#)fU9N+m;tiBE7w@%if_#p z;-rYjB6fkA$%4fF3my*E__B^^O#DL4qY;dUVhM;2|BM67rL;APFq16%hS%e#=hd+C zSR<3Jh$Kz3vg;0v&EJH``e9GCd+cdO^NeJ*55p&su-z2TUuXAoDDyCRjja%*XXOY< zXk82GPhde6w}R(~x^*Yk&vc>N=lX=TUK77>=QG0xsLFO2u+{w*-%0MdG+vadH=^2x z2au!T_ytf>`lGI^ANDhi6CJ#hhYrv=~{I$$=M-LSY8m7rbmQi9rpvhyk zg6~x$|4hs-xujUjlf%+Q!7K|$h$xWyk~SqCbxo9ZSM7!7QngV3_EaPLwVr)F7hqo_ z#wUMbMPDXl3na`1tT^?8p08z_{**OvfPQf_%&`pG6R6wK#_2;EQIeqgB)R8fF+c7J zGCfg8G4`;igwP0t&$#5K-peG$Ef8cQ{T(>wfO1sYIHo-)8%3&J(R3wjzc;GZOCq)e zdUnhT@EkR=%UO3wLgmOk;6lSaj0hA615XUB{q!tLw2(CMBrh{{`vBj;c$DZ5!inLB ztp}5FCBZ!u!H{ua&&CkYKQW4#+P*NE= z(-lMDvd@3%mps(SbYN6i*G&Mv5CC7hp&tWUTRn=Ym zjeE#_Z^JyfT6jW!0Wf&APf_ng+!@-XoVXSc&a$;{ri(FmmUe03n3xdXv`HmRq9aeZ z{D6>3Mbr*mN*TdBT>e-3@WmtA%kFEM@R_~p-7ng1Ykw-m$qfp4X zwufG0Jr7Sz98g@psw<3puez4DTu1A~|6BF^3hH@ALp_)Me^O5ab95TEblX3WawKn{ z$YzyaT1hqtRR2m;dZs&p?1fK=GWzQ9~!JI66ufT?n zJa1d#uDMJ+9f%cM%}ZO0Ktdz6w~E|}i5pmmVDSJF!qNZ4`O(0B3fS2nh#qM;{{PvW z{TSwKsd3JlJ6`>0RyvA?u}rk7_rDlU#llWD&CY933Q{vWUB4w|zKH2Ag7 z+5gk)X%Y=?sOM{?!T-H_l3#zfrCaN5Dnl#E-rsfLZLs|#4SX<+>L~Y zNB(wFOW)pCo!%iW;_vxN1le_17yOQB;z#f}lJG<}@q=sj9OjrAEC)M@s`5o7b zjbu2uGo18$_Lhd`c85O;HQcj!c%>A>4^emFnqzeSH z$$y3PJkYD>yfze%_MIBjkB|(gg1t{Fjpt#!41Fs8DJD~GadBdTl&QZ|w;S#; z|F;Am^nYp1pU>-x|4VC_zkStw?SGHf)aS6$d-h=#FOsn_50|tA+I~{SJPEnvH^^>y zP*{^QJFHLD3d9_Scd~`>V-15RGvAVK@5NM&1_QBxH^fq&@Z+GbHa(A3kp2l$NFe@n zDev||DvT#W9-qsffIJ)mX1vRv4CnfZmn|ZukMgw)0?&YaV3-eso)Q8m3sz1T%4+ll zZhb{dBpHRnBr4=gKqiAKlZY9dGB^N}P=R$Kz#wAmo=<)N(9)Av{|lu43VNb?oCru9 zfJ;H83_I%DNgm{2qfbaA5AYDd-c1m(LOjgILM)4o(|^O*}>5# zE>m{^z39^B$>%}c(Msku*PmVP>6zL#)zecJUurm!Taah{$`XA`eTqKnA-4yYEQ#wV z0lRrs-*Io>b@ue&X|eJ43dIUk+Q(z34NjlE#5K@#JAV47hybzL3JzhFFnSwap(8*$ zqNE38xWNh>jv;By3GYEMAGha9u|o0X^v1DQe~mVAplWV6z`mN@^U@{Ibj|KQv7t3u z($pQz(~LYGXaYL#Gr!u!zKV1PnsO4oHZUWhOBX-0-GD|kQt7~PZZ;BQexCqfE+^O4igQ{8-&mB zX|AJOmxVljtAHs5(i zOvJR*+h^YJu6Mm*#)5_>mNm1o|bI%~YGpTf9+l@eLsNA>Mp6B9+%II5LO=2MKfAXd^i zp=O*X-fm;lX`HJoV8vf8qd49gc(o8!a#cgfXqnxjK>d^C1=X;Zmzhua!p27ky}G0$ z1;@#Uvsy;a-&1Hm=kadyOGN*E-~B&ncUAY9v=iSopY%IriSVmG8VcxQdNR8urx)(Y z=UC&mhtzJoLHVZ94;0^5f-n5_O$VC3h%v|deVbmn`Ut)QzG{k^VaU;Xlyzlf5Rl#& zo>LM99)x@jcP^Y(I{{x#mBQ$=a^}$~k$q!@29WNm?fV!Vms#EN{sOrRLQM{ZsO|mb zWOr8&xm*!ho0jmY0ECA_0wF|m2sw>7EZp;iecz}3D!70DZ{pqrPOhp<8}D=Ot$T0X z+V`ri>ZPi>tExM_R`rr}cha3s(n&}NO*R5q*qRU~fFMDFvIIgH1_K5a1W^o#Aw<-1 z1VLpOc6&Z`CMqguP#8o(M<;GGjLP-@yyx88U7a+@$ISQpljhdl?^)h+&bvOZgrLRm zA~TIZ2}5!BkUuPP-SyI;)n%VHd#g_iudYP^QiXT7d`GP0q? z8*4`SZRls|lTIprT9dKrVo|P+#KCj{ReM&?axbLTEA*U&b?9v~}gYdM#E zJ(-_?`W0->%pd;ZaC>UF{gL+J0RbfQ3PRiJa>+-MIb?9$lA^~`dq|)3i?`ZL+m)`Z zp*-G#DCK*}&6>{ntpk3~Yo-#KG_5!M&#(*M7vSHGg;)dl z$32Mh3w52~-YOWxz*hGOT_I(`9K5A7q-6OkEAXEz=OoIx0exH#%Mw82C2vUOgAXFQ zRxirQOcW5wpR&DK7pZsc=n#t8M4CoB6y#;L#SEOcY9p0z#{_LxEIlKT2TaIwi1Rgk zUc}1iUT?rKbRFdB@kE0mASJ(uDviI-eLq8C0f!U|u>0NTFMr+RKd|Fp;hSY>K5xL| z(U1bk>oX7}Vgv#a5%1+-{`iCs2{3pt^O5xyty&C``6Uq&Ei60(Bga6J9t%+_T_}Hx zNkJW@&2Nc_9xh8j!Lq;`{h9nxI ze2oMXUyB#wwQK3sUcN>lKjqol>%9M+_L>dqO6vu3`nzqSC$I#8uKV{(T=o3|SDmku zF-MH816T3x+9<(A_JEm~>rAoX!`E{7(a+XSK}BW zMY80?GSSRo2lCPcjk*Fo8n+hnSrShwf4mZI&g3bbPUILvu8%91h2-o&KY{y$VvbPh z$IqUUZZaDYkc>#C^FyKbtjqc-`5JxKx~Tdk)~k3c?7Tmn8XyzTli>-sSJ8ZeFFT0PHit-1Jr zr)LOmk^r}{C8TGhx$K{>6gm~G(KBF(C!a&7Lf$Q>wn$UOvY%IIVGo49;p@@rGv!0z=+}|rs2XNZ2Rd?h=xYL_V zdfAHUW!*W4^3lBGl#gNt<{V$bnVg4%qJ1pnYgGxSIS%%M08Lmdwhsx`x4LZ8C9+OG z&fGcMd~jjS`~HViC-O<~cmiAq5XE%~C=QQoP+J~h$CyBYC*F_b^}rn`eIgc$|9jdq z>jEylGp$6SEQ}~f+iOOmwcQ4D`Ro0&U+6AnGNsJ_Jvl1LTHu>%$xI;J6LkgZke92r zR!wrAOxHmrTIZ+J!Aw#!(zQx*w-Yz#^m(=hqLIC>7JDnU7Xx`xQ^WxrgnJ=>!R^!W zVIiM9id8_=MQn;0#T^N|jqa}3c_5~b>iCNVxL$v^;K{6yzr_Wy`0_IZ4vQq|wF|{r>6OQB6Bv&oqHA_a zT`NO_#o}d;ABoxHtlQvJrq^xZPc^-6S8=;SsGR17ap)}DVzG7@6v64f+XefsRuULn zv0A@Ci}mlZSSM7Lp_MKd&}oA>~Bw_3{&yP`!^23>?;(OIc||DNX6X0B{Au3M!(3Q#`<5=cfux zEaDqafoGsZ)UmzOvhSR8Y$oL_q2>a=2nuk4{*NsrV%J+^;9*R zS-tv)5-0A3 zEKmY%LMRjRq*I9MzmA6ZYEU$|Uu zEg1`Ssr9-WpqmoG95ah4A zQYr`eBhh4MFu=yxJp0w_*K-~UE^KcG#H7dD$d#}e4@TX(;xf%Nnk8h+>2B4u#)d%1 z?N>N}LKPqKOCo-Naku71Y@DWqg3(5Fq!&47g5_YS18r5UV@6DNwR+js#TMCj?>a_} zysL3@qf{jGi|?vZ7>9f=wkw;J8e zaKKJ-y;b?0NDGjV$pMJ3bz%#eMW1X&Nk|I_6(BE@Nk{Y$cs?h%l5i0z0|&$4eaozo zw(^NoP{*bLvO^3u0@so)IAwgN4;73BQT~;#KA&$R&@S0$p%pq-Gvwt`Yh?XSWd8Yr z*g9xRkDPkPMxSr++!H->48<2S=6hGoV}z7x{d2W;$$1BVoUTn-6v`I z+n<9uEQq{jL9H=@<%`TIeoB=?{1LdL{vH}HSmVF~BIJ~KV2!-*`=CIu=P7|Ygc&ZD z@9x5^S0w-E{)&yFBLIvN@$OiBoDw)7@xyYDhEUoG9n+HD6$b24Vlv#Nt40v#R8{k! z%TynC1z}WT3Nns&UeSs4;{o5n~qg$TxqDU193T$EB+N zpjzjRQ{ZOYTc-y7DozG<6DJ<=1U0XhSOG;f-I)v~PVs7&=0jFuuFQCf zY%v-Bw%pX>Jf-G<5iMyjzfC7hU#J9FydO`8_a=wre{$xrujm@F01_y8AH*(i45 zyGnL|Y;LN9#GpAKhS?F=?1ZC=^_nqN0k&OS6Sw`X2m(QyRMQoG6J90`T_L(ee6>-2 zyBz&E7irr4T1exMg>ktYvL1n@m4y$5yThNtwGb|9_}2L~D}ZzG zJy;#+TMn-{6?lk`BCLdzhI%%J$al)(uwmG+{&QrLB7`9JQ@`KJu%Ghp0WHaDAKs*F z7}nX7LREOdRc7FyuRxWsZcooagv>ZKqMV$2d1mJIYHQ)S6u%r;S#L{+dg}Nwb_Q<`1A#Ip5G|*i%_B|_M{mY78cck9Mlzm}S)|&T{4I~03Fa6y01pElwLn&`!xZ!=5;9i-V^qUoe02wM zZC$SGkTuRd8?r{wr|e)~d^_NaWbVQp0WZH)+{`B?L{_aGAd0EjBlrto4RyRcG5=x3 zAoC2>1m-Skar_UgHu4!j6B9B9``BnFV(Ckb?FukSDY{;1Z_IWuoFlI%9FsQ`PX^zI z%ed+Np@iBnryUpG5BuZ$f=QmmR|&71auj_VJVi6j$WGRSX~ZL-i21Q>p|>SuH|F4k zfIYH7Sy4Rwy?g_)4WEk)Tu+5!`o?;aA+oSy#D1kEhm5 z0%vvu_(4!QBx+GA3?u;uOACjCuyn2Xdg2Q|p8DXExBt_Q)vI@`X5-=;7wwyR@l_o- zy&C=DkfJ zmrqq4!})gl#v{TV`F1Sf$ORR~;;d?+uTRVs1bZ4(bOSGa_awV~Td4+35M4OYiS%E((Pm^}H~1&FK8tY(0ObhX#bF z?r$4r?q`e!J68)xESi zRP&c`kXhqJT^DZ#oK02tQ|NK^;io+`+^9&ETkb-U=pcj-Xdg~y#>(}*!5&XQ_5$n% zGkVSMxytL8ud25)#nT1h>Fm_JKGmHwI&-7jV1FtyD!jLvMA%665zXNS=5X>K`Nx`g z&1#_RURiTf&Eqw%)cj7YnbO2YTbcrCMi+`doSyj|$vA2*bk9)%JR_i$&g25Mjvw z7ng~|@I5?&96m&Gvh+?J@NEK9ETTjS%=Nf|!~=>YEwCE^PmK6ziAf0M4&#}kTtk(Z z;q&N9sIIk;am8jmYyFep4ps~bWvfiEBQk;gA|)~y{u)^Z2U<(>I2*K1p`XrzR%i0F zY)7d*`0Xg@p#+y1>;NJ*z}ER9P#jT8ymO|Y1{cBG>d@2?GQ6}kK!-UX;T|6bvSTqL zO5j(#Zo@;_$1*Lvm==ZeBVViTWvz5d;th-H4G^<{TGP{IiAKzx#CYA)I?z|r_OuLQ zUn?#2aU%o2UGkd3Hi8q(l?l5v;gm~Ms_#+m5GSTV4kyk=H5TELOe=^$FVLQ8 z)83;~w0BR<*K2-I^OKtYsQIt|zv++o0YzAt1uaR6mTGno8c`I5=*y7+zdS+YL`y{b zVv18hUbI>{1SqIjvu!W^$IxA)eg6L~{aL?VL;dJ3^a_tmbhjH+6b!Y3%nE(t93^rx z(7SYUK-~TMG|~P;CIXtAoTfahU8Jr1e@1|VEehX-@Ano&ayFt-cmftRxSIAA1c`y> zK~*?I$pYlf)l@qfr2^hjO?U#D54=WJXu=wVI%Ikm zaJupU;mv&!x=n|eQivj30c9|$P(lMe(bwa2GSIaXP!0A|2DJm1Zdm?vF)05&>X+GE z`@D;87DMuxu^F4hWumo>)w!l{Yq*KudEl1^`b`dNVp*<8^{u#c*C9G7x_&6*?++12yoSo=%(*WPhkVzc@=oUXZY19e**uC*J8b-8|Cy3t?+ywj6{n>1+Khsn@mQH5p(#kd$ zn9cu?YR+|bH`TV9nTFk&6yv5g>`NE;#rJ17Wuu6kNHv!mq7kfcrrT3)POXN&W_Lc{ zU6=5BxEj@$mFwLBAZX)Z0EmcKah9UnOT8%?+Wv32-Bz2P-=1$ZNGv*k~I#8ns3 z;#n|&u53*H;>6IUdN??Fv!_m}`?8;4C@{B+4I;*7*@bMe6Mb|L{JN7@#9^Az5tzI{ z=~*GsRPVE2P!6UCMTRDkugQ8q?B7dU3%Rk7xk(kGDkN?p$J#%{ODu%G_(lG27*j)# z6}Ag*;!`zW1cgxa0eCzLOd+EHj4v=zqs@u$6S*YN=#X$UB&ZO93erpiIhJxtOx<$K z6Rzr47Aa>CJUEH_v>y<*2}nz}c!Ia}^cCbC_%zs4!Sy5Ug;Oj$;RX9$8frRC4K$l9 zk~TXbp8^a#7?|t!xjdna=1!)yhH$_gtKB)6i7IWH=|;AQ6ws816?WU-T;JK1)b&JK z59S*-H5GC$tzI)ywOTW{?PriSMf3TLq^~{^=PaCwSuc1^->GwY{5+J7^U!eL1>W~= z`C3FA1#es8k3|0?%sf87&#$NP+E6$IZJXz+B_1W1@${azY0#VYYu*#tEtzsCRPSlb zn&~KWx3&jUSyvJILVs+uX<)9m*6a4pAMQRq+v2^Dh5aWkaQi%IH>Dr*t={IlEB z(1JJV-b_5s)0xeEL-T#~%(CT6jSQGvHC5|V-T7QQa6Ka|f$Ee_IPH9x8$!O_7i{iE zI`0EF`{HWIzvFtwFvlvfjN20mB)z&n?X8PN{O-+KpOJ|xMvF^rLKmeCa>AutG?b_4 zPIzea60##Od?ANps;K^zG6Uge(_G^G_9QCgNJeZaQ0XwW5UYoTDlfyZOaI$Hw7coy zhmrV#Ju80roql&)8z~&+_QvU5=fn}~>E6Qig=ag7Qi zd_Tcwl0T=GmtbD(n_iE0@x_|Q%aRCHg;BT5@W#yh?>8~+a?C@c{0aUXbZx-Raisk$ z%`B)30xZO=m~h@Xhppio4EP@0Z#^tgyL;dZemZ+2=yQMYgKl52kALgUgGMx<-+!0n z|9GkghslihD_`+ufU_*|RSu%1b<6_+lsy6Iy+de}db2UtTl~*Z0tvD-RZ&E$h4v$` zzws^phZita7n1}usTuYX-vHFXUM853ZH9cr> zG%WdJ!+wu>nWKG%Sp;GPOGIo#E=}9y)PeI0pxCdl`@I3q=H43cyOBBE*T~txS~@d9 zzEYoLSHd!Dgk3EAcR3{l?gyStS85IZ?z8xdVUKb!?NzMPeZjEuOB}H+L2gYd)>CE# z=#a>gxfTf>7(K@3`}vj+P+tzz4Dg@vpTSEQA$$>$NX;?Y+2!S=)1oTK4NW=AC|Nu| z4>{iPd*r**g{<6yb3c52x&}L~pM=yP|E>ai`kNnv!VAAgjyMH;=oXyO!yffMl>17S z8w|qNkO80mBz_Iz2nmHrv>i5jjDr4#X({eUL$Sv1#B24+xtVy>`b@AXXPBc=2&_>*)nD%$naBAXHX z-NRs2Xn=JR34o8rN2M@2h|b62MP8v$BcliaI*}ga!Vwz+-IWc1l`U`|+!Qu1M+$gm zeUt3#ao69RrU;2ivpzlobmKp+k;eQrWX$K+!As-KAM!996gDH-yc_r5hQ~H48rWm& z3{z)Y-FVqyF5F52ZqqLJTA}QoI()YOxASp z8~JY_i$OC_ig}vTu)%krn8!rd&+hVgCjI_9@AUh>n(|Ki{QQ@iHlf$+t5#vN`ImUx z#d?4}6wnGG9?Y#)lJ2FBNyal*s_xAvy`@q2R)xZ$%r;XD)GmTDvzcg5<^K z=n(-Cb_5L1UgU0IZIcwdN*kHnVKp5t09GepyLMuNDIw4vk2`mPxCo~VidoFzab)HY zBLuntbdUq+2StS#0=yEd0Pt5i z&=H7x7DEc{L#j;ZDWropLkYp@I0gJ~fku3UN=X685!W}Vobix)=4XHuL^CbH1<4)j z>!RrJbD)y5eF9*NEujiri?HX;Ii{$B-*t}lT=jF}1#AaZ%VpQ9x?Ub|eTyicJbVO% z7$Eq3J6%OVb~9B-Rtf|KrJvNLiQg^ULJ*zb18-p#|qmeV8L46`IfZ~P{ z<*~gjgWss#U787td-3=<|3qoER%#i%`yABZJ{@SdiBM^*yc2+}$p>ZSs}OPFBRXE= z^hvOW&@13A<@8FkPD&vNGf1*Ld-p&u4INiM{M%;f3fA;=bLYV7#EG+X$tMR^XP$1h z9+;&Y+)rm#5457{v-QTGR}XYHKb>JCRb4=J7#DO^2mXmrQO`#YkfZ|I5V|?i!r6zm zOgk+F;F6#xAq6;eSdmTdptYjm>fZKX0mZtC@%&Gfp776Ub z5JnL^mmqS*LRhVl-G-N373tN_Tn zp@v%hM}}MDpWCFRLz>mCyZBCy9AI z4^Qt?rf2pdbz~wB-!$?^7nw7N%bs7iP}rSK&Tu_ zdpo?fus@H7!=Wz_IUm0)utLX60*`%M(<#0$4^DzsE zwW>qlwBsds5eyqM59K^8*R=xrV7%rGw1Bgw$_2z(NHSP!h=G!Az$?gxM>B-I3Q|Cg zy~Ye7+R@pksSr+QXhD0)!6WP?0M=C@%S5dQoK`sV?@HrQd&!&uOoQ94SF6^|?LmG% zVHsk~QtG7wM#UO~g|_?_??&s8t2==)b{zQzynL%<%5) ztwJXw`2cc$B%xQr0{wu51U&i)5qA|oT!bdURQG1O&w3dqv2nM1To_=M zF4g&25$kqu+--W!ImctV<6gFH%hjw5OI=CDABL~m)Px`vrwpHc+cMD5Ab~~b89-38 zmtpVJ*U3OP?w-92aJR9)PIki*%HXQG6l37C{JWwIZQwH2feMak2`D6sx@7Ml5`fnM z&ozY)(v&MIq((`k(MZVash|~Y+DE2#VRxZ*AOTzJwP-pRv~O-u6KJ|?_11J(S7F>V z$3=?m=maT>CZcYRTq3DfUEaNQtGqcIO5#5JB}3djed|9$)kH0Q^2xHL*G7(Bb=AL) zxijuDqzO;DL(n&c+{p+pM%Y?$r}YQs5%*dT{6~Zv$KDD`P~jtY692B`=Fr&Wg zUO1!b&ZBml@WYW~YyaX^)`6Lo5|yv6WypPQpm280PhCn*GJ zH$s?|g~%pO1L6W}I_*jW8tn^th0stXc4{BHBBZqEY30ebiPbnBrUSsCdJh9}6T3rL zs`OpFl_hySMaomO28#ExYG-C%lDg{(1B6&6{_#4ogETxvks}4gB8`~CQ-j;i!{#YO#HgSK zECaov0&G#>V@XPl<60lB*krZMvj~Dl0w!(p6BDm*8f9BXH?<)8e-&(`ceM}abYz2F zWd=N*!|m+%@Vyt4-L0>HtHVhxAZ<%)6nwVw+np}NPOP5V1Y<_ zekI#N6lC3l>%8<VnacUVJJgUigT3Ide%Z zi1indq{Mo44YHf872&u@Q?dqewM2)+-;vMIVT=}JWoRalIpX9osv%0FmQ&r{i*}>M z-64ZsmV`)rl%(XLA!5vrwhT^+t=JqDXmPk)vd^(c?3)H9mh=Q@jB?5cp~YMP%L+*v zL|`CzM-POri|GcW4^53d05CSC&7C9>Vj*k=PUCdZsGx{jzLQ{nCdh2%vo5-R%F;L? zQ{^#}Oy%|gU4&C0?nELzJjB}Y7_Nz9So~yqy$NAEXZvtM?#e6jA-cbscZvuKoDlUA z)1adjAT0oV1<=sogpfEF=3Wt-4ACG47DHB(yjT{%Vpzb;XD8T&!O-4i;5f-&f+GEU zihNtkd_J#5v&QA`Y2}g8u6&oG1k@(zC{06!&NB&LgHl5}WP}XZ4{j0eg8PB!j4ZA2 z0fu)lZTZ0k?Yi|~$3g|aY$bm8FR7HrinXq?>QlJK@8R`|C)kL!0$07HMmk zEMph0`Hxz^!p_t&ZfckmbClF0lBemQh|!xS2%%|l#&5H*j|=HbI^t2g!$X7kP+@Dw zcFJb{`!jFYy~UUm+i&N|JKXwD=)wVC3vFH5HjxV3EAW5jh#UCjH=McU21RWCDBQ_! zf_9zEt9}lMTYDd_5N1F+o4XLVK8jpyKY>O)0=;B`GA%QJYB3#VrTzp$D`~e-OceXU zJ7B3Q6e)`fn5%wLXkkT=Tga=BBlS`tW=e_lMYSB0PxU%e@TNHJj`}?7cD|tz3 z+1KXPek0VC53{SqJUl8UoL6Z=T7+HxY|@npJnxPyP2K*D6BOP%xlmQE@qgbJU4n=8 z&lFm|5z2Lkl9*}4Box51gZ`Ck?PIXF=%_KV;n+G(cQz)q3F>Anot$=VB0^Ym_|v zbqzsNNgV+wT0PRO3j;OE<(l%vq_u!7^2^Y^0w(}uCJPpdP!V-OheU+PsVb_&Qp`Y>SU`x5aenpvZIH6x8 z36$sAHtRLq{osT2T#{`YY{{l^Zz!>UzxYXKk!`7j$#e)%cLHpENK3%cn8wf9VveMo z7G0A6nwozF--QgN&U`G6Q7bwYv!XbGo|th1zm_!Uw~=G)!?gD?ahS>h-2l3C=+=Ka zb)CTx_60h^W{>Shi#%{_AI?C5ZC`Y9#-ZIA_=ZWL+b7K(nifqBe1)8_S0pM6Fl~XT zX~L%QiM2%7ai@z%wm6%?7*^;8W=P+o3<9o}?8wYf7AviFJC-1>%06Eob6w7PaJYR< z`^e}Bw!>k@cSh^<3|R%p#lxknDAd&zR_M}uIpW%_D6!g0+eSv(R;-}yrI{G#yW=o- zRPkh4NPZ)jtpuS$@}v2wIHP#dvR2expqJSzrQ4Z7uS&F!^!S~^oqhpKZlCUKWuEw) zS!&YZr2o8Xq%F}iUBGP@zzOp8j*jN$V{0-ui&?~3!@cmvsDtjb7xLN(&`oVbKL{f> zI7reoW#O0ry%WYDS-Zi!H=~cxMVJV25la%(R_GkM9@X=hZXn^|PMj4!7WAq-c>8(^ zd&UddpG?H5L`%^`*bh_I2kW;liugkw#d_X@7_3Fx>p8Lvskgx=)%qqjx8Z=@qiH|Y zv@Lih6mTowH-q6sTf`sr?5*Ga%aLsjuDy7asXZ-o60ulfPJT{c!KvX?Q>hY~-`SK3 zpSs{^S5I53zq`^>iinLo+}ho}wfl&CfSxr|O`Y@cWO&Vhi3g>Bm=o(12C~}-xWiMB zzh`^Rjv8{lw`VS$*jHk;(dn{*-eI5(9o)Wux_EJIWZUd{1vDgy2LT>M9x& z3lQN&Tnu*Xkf;XUXL{=UgS--HS@bsz?B%x$gSrQ(YM%^f>?^!|EW8D-w(Kh!;;9Tb zU$Wwo^c{hc_IP`k-%-1L%se|g z`KF0l@~tFmyvJ_IWqdwba$lrTG}fmTmbP2#r~C!kTA$Wp)L6=jORO>3T(0*2q}Ix1 zhgev!;wweMKw_8m7i7x?KaQ5$4o^s;z+5)#aT+JyE*mGihMFdR{-9_Zw&V)5%_Y{? z1RMUac+&)4=NJ4pIOhFml#$dGmb%7x6b6CtW7f$r!*!TyfsYh8gv zs;)1{c_rL3f1=UlLPp-T>EbGoU0H#`_>%#sjc?WtFur8j#dQib=o2jwZj)c*pq~r+ zdr#DVz#kn&z413H4lZ2-FRbjTE8L*Q@JW59`-N<-n916d)=(cs1ES)6iVDJ>iT@nIrorXrl;P zh+fC8mc7xLRukxgr4s!6bG5uBk5kD zkljQcD-6UnEDLa+;CPmX>!LGm=xWmoHz9yfH)6)ko$qVyVz2G%Jr9x6EOx^gT=(2G zx_K0T43dxle0>PjYp+>KWT!dj}7HFgM?SD3%Be3@chLw0LeVa|yfmjIOpT zyKYkSh;A4+XFaW5xouq`z-E>gnoadDkB)8`9la-%N>#dBH#BE+mF`HXC5JMq$@7-@ zcdowtbic9we4pl?f4ZxAIZRx#Z79bUMdOx$-nSUq&Q0LHr2VT78eYLgL1Q$OGKb9lJppY*`1Z(I6^uvT7QBO4jU)bNAHW()y39$lXHAD z?1-M&CpcRKh>2A*NeN1y##o0|ersxaPkl7ht+~x#o2J$sr1wrn>o47tj0QH0VBis5 zy@0752pFN`ND5vpoX1LPAP|_HuBPJ~oxE$)vz8;qJF}R53?t|?&*$i3s@j#ckxpSh zZ73y#y({eQ$DU-{?5pB}vzh%Y#RTnyF7qICnG1z(XroyFkx>xv*C>+M6a?sZL8u{U z6BilzNH0VxOl%-LB4-~?gNLOJZtcTmDJk?8gkaNGb{41bF~_jxzdzeKp6xWvSOnPo zo!O|@*k#OmaxV-V_n3yTmo2+7LoF z>~nMjSpvl|I$J*AJd3&0R@n4cWP5~_`>`?;l3_56VtnFpV+?-H<9KDPrQ!b140~? zZ;?tNxsUdo&Pk|aU24bL4lUOdr%A0E$8-x@fXp;VEhOE0NR>*|1&?YHh~PG^RcTZU z`6h?g!A16-(N4qwdcGXZ@=WtR%^99u3i=Y-H<@i{sPoH-j`5DfQZkIQZM3!iECf&5 za05?wj^0rlZ>uX7>)PVAY#$UnDQg)GJ%&`Y+0LCnAvUHHlg$$Wxb! z^bUtYo$aAea z(JG8RsDwyw2zV)bj~AK&1B!(w5sQlpWYECUEgD|{gbHj>pov1;2(BASgG3!vju^(h zgA_44hw+OEj@Md1+lzt-^XW#w7k(J?zyqRN{vfchM+qqKBUe(j+zprVRJt+w^^^jq zNX7$7O3QXAN=H^hE;*hEy43p`lweS4;Ge~-@1a+-E$zb(n+CA@6oA3%%BK|KG?l>t zAg|!`T?!llqeRZsXN(;U{c|EEVqe7sV?e` zn#-3r#9aQKhGpqa|M1jR{^7$^i$7Ax(jRH>Fmck>n?BV5qKr4;HRi3$&oMHMsluoH;D5QP=GK$N(z zUm?3MITBEfpvda4h?+r~*K!@`RQk0ojWHK-jVmsxM0>5qYG+@^yeshILA0q%8EYYZ%|e5b%S?SSb-#BD*wqukKnO zX!0mr*M0pBdepBsKxgCEO_ahPAPCh49r3@*a2~(#{`(O&TnVOu+*q$al;7AfFXL`q zk~z6))dUIg!><>hQ869D2!M=$rU9A++o{Klygv-5 z2-cKh;bd0zhsqIOiXRkiGbRf+k2_E$}vKg9^g~SLM>?oY<1N?%4bj7qXl;htLtkzN45(QPBiAMI!+N zyvyT;6&@IFvXt<>AkYp2e5{`JMS3D(mTIaEC-sOACecpV4q%@tfoFMx-w(YG@*(y2 z3X>(6@a~wvPolmc-46cc3(YPZG-C-BoEE$}`$>-(r?8y;`eNI!XMZFbYC!d z>sM$UIpWn0pflWxdY>yP^ncQ(S)_HY9?l`tY@OhhB%3sY{bRkrf*EKP!scxUesSP7 zVSi~IIF-K$T$k5d2O|DS=#nVv3Cbnx4z#|$@eVQG*ND6e1K?2X;Y;fbSTvh4oB6RK z<^=dH3Dp6aR7EgWvG+bC94Mw2(V$#|90Y4(_<($DXaKoAR zrHjZf=Z|J7(YXdjC)Oe4$;~{ex)b}hhU;AG(9=_YB@uT+^XEyRvu}gWf;dP(@h7Jc6c&xk zb|#bMeOt?aG~&jeD(CFuJCe!DxwlmQY#49$+_DwDtw76u8h;hGfD}-!$(ko+N{EG+ zXvHQ^7-@j%P$=8XFeR432J;*!SV<861aaVkV92Qj6B4<8N{t7uvd{K@w6`e{xUMDK zAL<kEuT7J5VJu^$=Hm8Y%MlSbuQvUA+z)eEN?8Et)w z9{4*tars=#KwEozo6hd&jPdxAs~cH%qT}qQ-rlqoKN1e4w`H{2b-w5koqhhn9VT-R zz>?~obESuSKYNNF`XhYmDkzoXkTo%NJz$?;(BPQ-a zVyJB9YF)OIy+nC(*hn(^UuJ|d-R`8T*jTrBW|5m~PNLxX01|W4xAxau2fw(3STTH% z;c1OS^YrTCQ(4%fF(>d{@Ubmf4*^uEu*eYh6yX{XBG({J5U)>{=M(clC&1P9B`V_gxa;h)Q|>s^ zf5P6+-*G0O0KzGt`55>9U~lN&?c2wU(^XF(;8FLhCiFpmQ>EYa`)E5RuT0oi_`mAn z6?oApp|CH+!rq|oe7xlcd)>y#CJ)6!9LoIZH?2DJR(j#J;tK7FZ=kM($wBhoVd@)@ z8gW(jm@D9ybcW&>%Rf2fu}6+Xg=e_%1r~eJBcTrd_%q?O^)u=ic$Xn(F72_}bod#J z6!gnP%^LU}`~_O4Hfd*iyN@Km+(LqMFx(TpZ_Uv1M{~)EWFEl!9)BKtx%A78#kp6X zI!=}$}vEC%Z&H*Cw1GLA+@`=aNzTvf}Ud{Fi?)LB3+Qxs~d6Z~^>Oj0x zh~kJOmQEE5+V29>4b}s0j}Dy9>9PLU%jsOw%Aj2ALzGftPd7{y`XT_M@Nb4fk0tWf zSDhkUMlnTux@mP@r0c@xU={qR4g-N{3%)1i1wqf69#6o>QmElEM++I7wz$Uz5TuLr zkDOxgkD~bO;5$jTI&P;}JhEDVcdB%;ef{hGtD@;S^^38J`NE;)l|g@H)9!5Yir5x* zArjtmof&G_4Hh#HOw={^6oH?`a!KJ5*L7O43&{%t`o&P1M)mZhGQn_*FTj;tx9|fzqbC)CIz?BS z`aEWLzS*t&km&~uG}-$c{e%ZAM>E9%Q7RxIO_P@}5wI?NPX)(XabSToTS{V>?vx*- zvu^A09ORNc<+1ql@GX5lK2~ORLfBs{J(|z*7|L@IC9Lw=v9k4eq49g6(Dxb(Y@|H4 zQ)m`kVtt_bf?}I#P5%pYt2yDw%=5gIO`Ac5pjbaKWVTgc{j zXi#B~D?hJou&!yS{kh_QynQ(Ma}_kF{ybDd?)jkg@06tAd@ej)+SUoD80%?~Mc`PR zH)-NvbCrJ!2LDYl*{LJ#^ww7ZoT9!`+i)0p=-WyYVsLB{uhWE$`&o%N=b**SG?WnD zH}Pc$`5SgjN++cI733p%Y-CEGv$A8x>7QQO^Kn|)&~Y&6!`ViP{!ZsMk(7&jp?Ac3 zNex}<%pzB#*o;r5?KZ@8M4Q8+U#*KK(ScLF&d!z!p5Ofdj=n!=D4?VHL3+u4+GNBd zlo1ktlj3L7Sb2XnJxmH(u)g%1ragy9o+?_MWJ^iny#ju1u*1@>MI22B#6v5UF{c^m{F3jvZ>ZwcXzG{@G~B_mhbOc zytu17&z=(Xa0zU|gG~_(uf&tfiOH8~yGaGyt`FNN-(y=HmRP1woCr zm5!s8I^khuS&C^AK~;^}5I7(57q6lKr$77b{zPAb3MhA!(CZM7&>x~Dw=y|aK8lVz z6rCGA6#qarnan2kA8ITdEi@i7l9%TD9$ax=iMhu6co$`)q0`YQu0M$T64~nx#p8#J zqowm!JlL1Fey8hw%g|RlYgQtU)j{~(OWl+jLo<|K7bY#@%8>OA18 z4XZ=Zfc8XuJzkj`()IXrT2^_&FkL-9t}9QPb-?;(3A|#R+Z)w3)djg<{az;PW+V8_ zj45q;jt#@7qEC31AXL7qNu5|` zJlLIgF<;-fi~U8p2VXv#t3w8`hVA%%SiL*3AfeqY>i8?6JLB^jyNw5zZNucatWVcZ zQE?HsA>&F0l@QOyc~liI!W$+e>YWPRhLo=wU{~#?EJKcmLbThSj*DbwKsQN11l3*{ z0OY{R!da816&hc*bHbe3dTUpG?a!hOrrY)NV6YV{Is2sWU!1f6EX8)U5APEs?>HvR zVZq=lFsbS;OrC64eYzFP{=>r6(E17<`1NqRA%+{x7vtbHN#7p9$RLl*r6d_3Dw~`R zNb{g|VuWKNbjkK6dlQxpXGuYO>(DRcVBzhnIO%;InaEl6?%Ac#L83X;;d$~c&%+p`g-$Ftj=r<*ToK*=Al#|T-$i4uBD~!5XltSbD40f!r%$&J{{pNKA+2_xD;RT zE2)(GghpCV8c(>{<8cABAm9SLICr}dnn?bB8b9lYq0kS_y4WAnEQXI*o6KmmHWX^9 zWB1mz(-0iB?ya0g(jN^#>!g{ zBeCkt*@zE>0P8{8`<$AWSBVqlHB~|O+ndS+<{?PLCFI%~;lOG-`MPj8`q%^eU zx-Hi2CEAULOQo*FDOoqm?zm(cqd2?an_<>(NfL{y^#GdfB{{y}c{@?yB#0XG;ZN zKuh{=w7z1t!Bz{!v@TjtQx8hLo+t z7LH!(*L(UaPLcRWLC+=YcF5dU#M{qv`5;ilS%DcYzsukUClEZ02;+(U-}XezTRm>K zr;#|Ws`iWEJKcg3XqAzsGih5RTop36L&S$nQvt2as4(x6aFN#I@|TJ5NdP#&E0=wf zcs%QI+OiRVMvlvS=m#-RGSY&z!Fot{U_n;M!{yBrl}5BaYfi`a@tPFAP_HTE-jC-j%3&@QB+TR&&6BN=?H|g!cb7n~%P47}2rv2GTT7hj>_@sin5gj?b5fdq<&SIfS zu-fr5OYVAP*RDTkXwG!l;x z4cZ1i>-khDlnS4l3PTGcx7JhQ;3w6m{vr zJ78Ss76s$0=lO?8r~9UiCi$XukIccqwuoQ9a7Y$dS<#)eZ*RxM`IFyb`%74^mP%Ax zzqAee3DuTxaK{WJAov!-T%HoqEA!#n{uB#@;g3(7ONGNSFvX7f+j-oX>Koqtwa%A9 z1x)Yvk4ek;7$8cJJQZJEs&mmFPW%qxz|OZ3_csI8`~q+$S7Jt`nMKYuSmMOoLexok z7g94$oIYq&^il)Mq6y!omz>^sD|&UVJHLeI0j7W=r|`r3sBvCU67>n?1<_8k_sfr_ zdnfXc*tdT77F{B{>NWvwCN{hvJAu^u2&3IJuz^hIZ^| zhYy845k})qD;!Jf&iQH*T><5v&;a_c%tWJ^$Yo;NR8?($IcoNFY6)5nPr@cU`&N=9(HCTgvcOFTrd^Zalx!0 z7DuecaFheF2>KEHi9=EL>P1$4tPZgE%RmdKzD_-zW5`!{GC&ieE*3bvRLWTVX&BjI zib1#n=mf5V(EB3Qjm&iem6>$$zLk>1`1q}VgX_3$g3K|Lm*+W{{PFPfNDB)e0NT1g zDq7`UG2ku|+X->edR4qzevLhe+eKC%`F^~UrIG9h5e7iP$Jc4ALdl8?e{(BzgJFIv zJOc2r^(wymy8JBtydHFm_rh!7`s2nl`^ePm?GoY%a6HxYbo^ZUwv?592UO;?y7TMw z#<9P%Qz3Ir5#&vx{dS7&SKop-dpeA>p_B@L>DF2W^0*3YM4SxY*dEWtdetup*)Y&Tbi{@{!UJDtNU>Fs60tO zGR{Zxt5UzP0^G)|2;Z^nOy;Xuh+3GJ1Vb%Y!NP3l-WX5PAW51!+d~9VJz$V*7!sBw z*2{VgTXt%R*e^ho;Br1yeHR#5x>S~zh~28X6^y2MK1J^;O}`qliq$ydPH|1XDV3*o zp7tFl|FZeuGdII;d}qz|HFx5wt@?pM#YQ9~u!480eJ3*)eEU@hx^ zyNHKVBX#h{F`JQXCsjL?Mj9uk2v|dv!5Xt{1ydVa3`kaH?G53)&+w+beQDiDrmgQG z0eoFVuT2?Rvf2IkiS?QZyg@A-(pn0>l*jb7ciQ^p>mx26&jh)i&}viegEKY$Gpzyr zg*r16aThuOYiK;sgTv<9&vf?(x?8j78D3 zSyw;-Yki%~=E`BhQuO&Cv<6*fZAy!PtRaW3!QA#Oom|quxba)%G$pF6edoZ(ALP<5Md;6D#%m#mlzrhSG>-XwWGYJ4jGRcH0 ziG(`_PoY_E2&=~9doDFJu3l!Em#MX9BJImM4xf~{E{&%#)ll2*sJWOw!2cd|Pa0nC z@T8*V5i5Z!-rqdqC1x1k@pM> z7EM0jp}H_vqgN1DE{!O0_O)xq*ce-|0i%Ay0_=@l6E2goG0Gp?v-DY9(WSeGj*HJO z-4lNEjUzP490385Zssz~zl2NH2L7Ksc#-tdHs6warA#~@A@!Cs3qnK&QwQ0p8H_57 zz464JkDN4bBeI(TRK6!1S+T9*NadQ1_a?H}Y#g7z4jxv}cN2Qd)jq@x_cg3(rGuXK z?w(HLy(fMo-e4`AKfdvrY~tRH*HqR6>QsA<-gH&ahw_cqoWWm#9!&a-*ANG2+vbJb zVNchB4<$Lo1)MwdQ=zEPy4Dwen4H*0X~WFv-QMkpCteV3a&6b*^vfTui&JG)!%ppW*Wk9G;26GAVWp=T6qL?;*QDU2jMO9Km>DIz{R0Sz8k={Ex=S6t2T^Fy zP>`P#BHnT`s}>?62U&jw0GNERxdYnUoNnHD z7Gq|+%k!wm)eGhnep7T4CBQl-;Bw{R{F7yOoVCR5Hin9>&Q4e76891sE7ShT9!TvK z;e9x3&B=|IR6i8sAzOgdJ`yA#r^Sg}$f|&HPPqs`BRy*?T?7)o$Zn_;v5&vKpqn}( zkc=j>nq|m)Zc`;UU*ga5-;oZHoB=2c zA%*;s=O+*ji7mqPN0X%Z2PdPSv;vZt`Bc}E@{oUVU~}2}eMhL)mu%e3I81)|dL+y8 zx?A}#J%w~<4i0-KcfD3F2iFGL$8w7!{Yak$0HOu)Y)5^(4s!T-4e>#{ASL>+LQ9)| z5Acj7LO7x``@xC8`kMf7DnzRS35mAGcedaDY%dKB^=)s8mzx2@gi&*}R36)3VvmfK z|131F=O4G-X)PnAob_d)cs>8Pt#u*n!T6gva%7@BHZHCsuh21n_Z%53^J7oi_w1zy zWnYZ5EBImd2qYY2hwLN0qEOzBhXDvi%D*=s^|G7AYv_KO!yXiEXX8y*KciJWPw2K% z3*6IJ{Vu$5>b=u@;V@q>zVT(T-771D?-BOhUNL9N?{yLu`8)Y0>Fk{%M4TO*d?#gj z%Q9XiO7~;29g<~yRvcK**(Ij-kv@hE7`G>bzCjq!=-9G5e6^&|?HpI~Q`a4#e8&A1 zYDap3)HXvUl;vHcTQ-asgZ5bWetc1Ou z5Et-!V(*wkam%&SS7cwTglxA9W2RT|K=3kP2*WFOVInFzRic%vSyp-0D$(`CF_ntI z$Su}IzcdWYoY}inK{QKm) z*(tM;%$CuXe8`LOY(p%TGqPO3@uAR5xK(h(AlA4Kk{*l~ z@XHgjA<^2zXvg1ojJ9UufxizR61q_67@eGPB(C$79i!N38t}4Ox()ZP810y(6V}hB zPvA|Xa(-J=^FjD$+~)8=QoBG}FyhMkrR}F`I3BBgV88H%{2rI}x1uHas4oOsee*Fn z^6nN#*6HHt>mi>9u9#u3H^g3~eJ+~ww_+=f?fLIx7y!F5=2{?2t|9Dz2pO5dE<&Qi zf+>5hZPH+#F>$n}+UcU1bjaoWwyHLKju3M)Rp z+?{ft^qxz`-LlusO%rKaH(HUEpz}wHPbyt=z z={8g2ZlmM!MPpZX;8C0$zvMk9xl{KFzbCQ>o(TV5$}t!RHc|(;)*6Ul(g-JQwV)QE zO3-HjBiG-{CQrQL3 zM;*A%C@nhW6n1M0Ijut>^?K0^-peodq5(a7p=*j* zkAL^O?2)kdT3r7by`9}FWRJ^gR`L76*ZRqpUW3UGkw2XZR;akmX>j%4)$M1+<|kaM z=dZr^fJpVl58wOR>h{xP$Ui+kwEDGs_m7=DHg-0iSD`K50-ug=gL|bc7$ayClIkn4 zT#JOUQ5^MB=n=HPSQ_CeEb0yUKc|2=ggs8U96`BYvpZ~^;bR_v^_}vO^~?tg?!7zQ z4L;+{8&#yVQZHF;oZ21j1+s8&q`Mt%8m>ZZZJ`zc9G;+$t^dd=0PTafCDLT?xOYRy zquzKXaw{3DFHxaSWx?UrrpRo);{sBu^ zEHoDj0ZsxkW%VzdyIu}*aI3lAM#w?-Y;JGRG%;=2OoI`+5ZTsAsNiOeYn zJNE2M?8lwsl|$>+{Bph4u`L$6((hUKBKLSW-S6Q@!LBYGr8kj#*}{di74KaL*Hnd5 z*(t~0DvZ%~4hvETbWc4sdS4y*&O z%uc$~7%Rum8Y?%ZFIoNl+dZR~1f6_-@Aud=u>6~=*MY~jF8_Em(LHuqc`SIzsOR?Y zuf8NrW!ewBG09HSuYuMhLH5-KPm2F=e0U(+;5uA5woHtbrVZ7lzY~U3SX;;W;juD& z9sV8WN#qxN9sQ zQvG&oc6+0tP&u18$FsB#ZULfIP0^ZP&IORP)2B?_)5m!r8ID(0cqVlB0vGt=QH+%z zqm2!C5;kJY(=4cgQJ~;VD4a;r0H5V&E(~!fn+2NzMYtH~B2X3Mic%lj|GJS(8LKMS zSI(KUGBmPs#JZ)i>C7c9OA{M+0}f#K#>CQ=C1*BGK6wKobF8N^K;{G$Ga6R>%^n{T z^Dv(`e3OUY8d^O!x|$d!y|J&i;+ zF7O5;$Sp%yf!&bCw$>msq+5)mA|v^bGP==LFqeWU7A7h5m9TbEXJLiz2d{;}h6$l; z8=9#ha63Yr#dfxbn&}=fIOtBW>u>pbb}!4t+lJ%Tk5{M^cABCM>`Zn(xfCo*FhHg7 z|9f4D?TL8Y73=?%f`C^zT~g3%3jfbMvMxC0#LUa=RqMt0a9f<^SIzpkefJw`a(gnq zAnpn;vcLZprCaoNZ=rsmSQNq;-UUfH2C8!0OhQo9MbJ#HX7X=3JlJ9_YXQ=oX?~|L zPqm(~o>1o%*neBE5S}2b`!eF#=2r@jK3ah9E#`#L8VBd6Q^fgCamW4d(DS@MMr&6r zN~|F~UaJZNJNcg^ip$%msz0(|!>Lz4K4+M-g^GtS*5AhhG|S$oxp1dZ@WyW7g}|>uOJv&KK900jFMCJ@AJ= z;0E0IhnrmrXR*$_{?U&%l@+wD!&3*qBMyQ)5>n?>w_zr5!HDZnH>NO{kL_T*qDu%E z7#j|Qn{7Vs3?CO!$nZHusPHapS2A}Sh)yIV1SDuf!n%oVk@i|(WrDic7VDm4&+tbP zY;3=weYl)UzKk)`&;~=W8F(3{wELvhRU|5$sM-hZkW5W&{9R~kQt)D_X%>x(kOr}s zD43Q52$dyK(G{&)UwRW2QZ1J&0FsxjeBO%Bz8dz1{`9AiC-N#%pn-&MZyAJ^Uj-A2 zd1xnE`DlH4|BCbY`VW|4_l_M#7=d0-asCn!^-G*-801WjDGSW9N)Ssxc$W!~n;0;Z&))AzWL}$uy3( zF8OGo^0?sxko#cEA8yt(>s1tbvc9}?1xcLb?@BoMhrvS;Y6=`JnLTdgnpQPzDo%ue zM#Le-EFom|@WYg0f<1KH@aBs}CW%E*5X$Jr5?}rl5=xlnF+<)uMP#c$Pz10qpiCSx zru*@rm{(vbi}X_e*bNjxgMsoAt_Ua*%PI+em8`a(T7voQR-snfMzhW96V_{DP_S+F ztc}{?=0BiC*vdksQuwI#kLRtJEZ1AFVPw&KHtjzLdog5AAyqr$gBA(QDlntbKdj~S zueYbS+a4n5JuNoFPp*XcJ*L145kVR|x27ON+g|C_SM3>x06)%Dgf@jt*uO$P1l}Vm z+AE?wK-1*=dKhskq%T;Ho+EY~i?bcAftgN-!BU-T`wt|L!q{#t$(&$rvB&jWQ~=Yo z=&*j#1=ML%JL9p4^!ghsZ*cLGvz^CRihoFSB77yQAH*AuCOv`g2=70rDWg$@uFPIn z>hrg%r=tKtMUPdEy!(1G(lJZid~B!pm%Zc4UQZh98PCf19C^o8Mx7@FZzf|8Ne4LH zPv9<~0FtxsjLxh$?$i|P(ZD5e1Uc#8Fg~o!XPvGB=2X?0d>6D~>dnNbCRLLHXSP4a z5w_3!zle1Z%h?zExe@j7b#RcO|M*(&=65D*^U0UNArPnR@elape-_jF)$JoP*b1mPt+pRzK+ z2=77+e{-L!YkIZmqPdKH91eu0^$qJAW*9zEY{ce~`r#G$nR`6pPk%b>xkvkV_HQ(K z{~P;^%Y}iD_B;vph&NytOk=)=9Q8F6Gb&mr_b;eEU7`Irol6-je+PUn1Y-+QoOzXiuyMGpBW|epeOyfr?#!`C&z$}?Thdu zwav}7XWpvs?5tk}t;wagxx9gUA9Z_zCcHU|0>lB|ZF|-2{Hy#?fy+Q0 zn=muN(vC$7i?7F6#rCmPv!}!G_%YwaD*YQ_p5Yrw7RSBdN9Yo$2p%sVpdMUUBK)Yu zSX#(jkga3jq3Y2)Vi<|9L*qzfIH#AP&m{&Hn@?!GFD0^Y1>5kn5Q60)Y1#hyw8`ly5-l zf1!wwJPS&3F^_))Cfnav;BZURyq=l>LVRj32?a|?8|qqE?eJ?JcPgm3J+%|zU=MlB zEHM9jV5`R;Xl~zCaHgWn+4HfnW$C*5hLuPNv=~up@#dP%4XvrgZI@I>0{nxTLq#N3 ztj2If1DV>A3*IveyL3f$IVmToGwILP^*Z4fG-jT015dc#?QZOF@leV&)Ex*7k8Qec z9wI-YH8qQC2abiS>-)IjYp98!?;X&F&L~v^e|{K#Q{ZW#!3J+s?f}ee;ji=#`U=W! z_2QkSYyEj5v$Dh3o#XT-t*z80yA0Rk(09 z72ee7@SFP`h8{`Gj~l6|Vq~|s^?;#F_S81l_8@tHKRupZsfVk>5nn3O>~->|7t(`B zM2aq2=sJZcN$Ry~f39~eHDKEobS0LIXpPfV?Qd9q4He$FR9EgsNSCI?Q*I?#<8E#A z_&Pclu4+oSv|ub+insdp{Vs>*57cmnGwQ`W+z#xI7x~X2Gr?*g9Fj2oXyhtBF5Hqt zMwGt(Fs2@HyZs{^<4-t^h2KRdvm*)UrzXgWdzF_r4J@ir#K%?=gNVJPVhFbRzJ)cq z+ku3(@rb7eo>r0iMS;L_8j_nDlmMIyQbFzx)~^axZ=v4VRf9$z302n)z}eA!E!UrP zBFrfj0S^;R*0BbZ4rlwS)u@V$vks?u(oKG4x;q%lG%Tl%-Lz77sBplK)i{0Cp~n8p zsTX$*YGTc$_2LNDi@SxLr;04Zm0Hm_$gPk+z^frZF!vL-RcIkUjqN;wDPwK4<#$6f zNo93(eTCm;e&4=`u}rxyhT#YLGw9<7u&&=w^?snoeyHkRd<$Bi#Q#0rU3$Oh@qVql zBo}+qL(>27MIy(%IA-_&>k)HW&yb!H|L3~9`gQX1i$|@hEXk#()D?$f8FZo$8(_0w zL>bU7T4`+3Rcb6*OH6@91`iH~{Uo0t*N(0@0|>o_&xFGp=)JKw;DwhM(*y8E73fV2 z2@%PfYX0loN6vZx5!~VtNZ0g0c<|s9o*C`ToF-a~4SW85{w%Nw{&zGA?=<{$FbFgw z1$hg=p(vUb4r>stAe$?|Hwqd$?%;~iedaIb+&%YRz|xDmVOPVwdUPeb)%vy0%;5e_ zoDDC@;41j-?3J+bSgF5!n#oyGKv{sql_6RSe;WaI;- z#+r6F|CG^CYi_S?aE&&$PxLiZYju(7XubMBvBpv@7S>80sTSTY&|O$O5{x~h#sYk~ z{3Xgr4(SisUr1-r2v!L>KViDFc%|{r1r`)aE0xs=#7|yx1ku2s=+1T~*d>~IBkQW| zb1F8kG+xX!Us6Ialu<}dBnw9EpAffH$Fto9CE#OsZ*tbA-EPo^B~?rK>-=?aoWp2m zJz1gQYleSh=Y*D9>W?v6guEi$UP#eR1B-47I7k;Kt}&g3US2Z1EZ4Gh(b_eOOO3*U zSr{oia_W&sc6YTQjg#BcH~7advn$tib+7H_ufP1q8?L#ebmj6DSEM~;AH8Q}(V~&v zkKq1*!P#(LcY+iMlF z5u%r9)ff78QXA0M5}k%GNgojI?zC7@yfi!-qLEI_P|1h#RB-{qvDbe9o2!=%b~e=I zdoOui_d~wAo3_{FUAXd|)4tI{?qiA zNfFZh^b&S>#kJS2AWmt+0b>pA*BA#j(4K^Upjs@UKwhcaC7kDOU>9wwx&~$VJhkL7 z7b8?V!b&L+TmdcP60rc>3pxqm((v?x2_2e!8IghqL|f(Lfa!v!A%+^gORuoi$l-@# zK9KflT4#+T5_0*&NbR4)h!~U>ZT|)%=<*+`ZBD0~+3CstpF&wOIqw&+hY7zX>4QcX zlN61J2z*fI%0&qHo6!ssV8|hN{(Z@|-PwL=jh5z!3~J=R?=i@KACa!Bc^9SMtNbV3 zx^5IKG8-dBozT`1T-7Y9=;@gm$z-3iWo^>wNp4X${_tv;H`7&@W8EV=P7`IpwZ+r0 z6v(7A3y}|)r?4T1KV%*TXN?>1wh+)jvxq)o5`oQ=Mr+XxDBxAqKn)Ci2svmLZ1&;E zd(|H`YuauG89fAnx*s*ZUq2lv90boT^rS0fE)^W z15W+$_7$TJJU&9tE9Up-wM5eam)C_DdA}=~^1FTBXiaq#C|RzSd3YmR=8c1zkK(Ic zfyg~XQj!5lw4J__9KDbghFAvWWHF?&vyvsv{nm4HTKCIS5IM{gbO7dvsKs_98x^Ur z!L>-67=BY+5z~d=Ap^6Tg)$wgo`q-brywn}fDXw+W^Q+h59L+R*6wrTo>8vp9Qu9;s1=Z%&f(ms|6#tvs$C+QLGyxahmbdg z9IYZlnq|=KL+)+}oS-}qHRC~PbtYXsnQrc>K!iI$fyXe2Zys!J9&CORVa83c?6T*n z4435T>QHR$pm{$M;juq$9umR36n`S)N8}?f;_2qW9$8rv)q!&4ni2J!1dml0MpE5a z2#!%o)rZ-Y{L}28@aB|BsjMHiQF^4d7;v6Jn8XYHj4b1Y{u$a7*w^M23ijWf;)v!Y z*#=1GOg0Ou6aLgTIza3KuB&B?fEi)r|MQA1w}AqV*xmu+VjNr4tFYx&``8bm)3V!d zSx0OWG%c32RI)!4oho{!`5v+3DX}wq#O{0II<;Q~j@u&8m7j`e=2naW()2)$1ByXr ztU1}d;O_)&2Gkg0{eiOpglYPxLmkEML;5PiP|Q!UJ(LWpK{KyxEb*W#lU3Qratq8A zMSoCXZr7`u2R8rxrh!d2^=tqf*`w+LCyKAeOfi3s`|%u$&(AIJ@b|ki2mFp2r07&W zQGIpwC-HCB=44Zt+B^xp)GNqa8UT8C6I#3&Z$jCZ2;J4rQ;LsD<4zbR08L&#F4%?W zDSDCe6D0dHWXUw9^|));rTP-?#wmk+H{?NvN%ApTN89r8>`^FWUOnfTBr-xg5lA}s zITfu*PefHcx!tu*I<;;UJIfjO`Z;$X=O_3w((Mx-1jYIZbhjR{?i0D|gxMG>3ou0j z?3C>{fI8^W)NeF)RQrEa?6|F?SnL>^*T$cAJ8^}t@Gop$8VG6Y%va~-8yfPv^9^v< zz&x<&t&Co$H4=mn?n{#1g6bh6piFYXFvw*Clov);EEl#nRbGs?d@nnU(CkYOxZ4Ey zc$@pcr6RU(Zpr13+;GDqH~ixXhuE|%o>?)0~S$TX?u-IZVBbQ%mN58EDJ?Tk)688HL zEP&0BJ{L=WR2J@|7$wCAUKMi3C=b(8tLefw4BdsR@CwUm1x3%J*oqY+7ee}91etYm z3b_zErt@7!y~w(|>bssGur6+Q_trQZ$$*b5-@Uil?`!8+k9k8~+si({LHdlg`b(G2 zk0_xCoFZ0q?|staPI`n(i7Nu^Xias57Jg#>M8**gI~|3M6VD$jq!R(f2!|csXgGnK zMYXIZ;4*>^^Dk=RZl-wqlW-=f`OrMoS>KP<#P4og7^~Y_TknSB1*>Wc6kC7?poLr; zd$VBxhUS`GVb$fzq||S7hvrBm!mXtZE<+EyrbkMl4RtNKf(sD9u54{htR7yz3Umf1 z_zln*1SK|zv>aACcLT;5UTFflndf`$McZZvUN{wqg}s`!{S#Np?KpwlwqnQD`ha()K2e}KNfMs0Vz)f{-Yk%|X^8tzdHi2jT5Rx7`UUDfFzy;#o{N& z+I%GD7RFBp*;Z+F1gU|p!?ykA%12Oc7G|x&;I{L*nbm9z99}$`8*DKbHVZ-oSky((6ol0XD+XzN34Kk|DZ~hV8(l#D zI;wY!DMxWY@MzDfPW8*a5O1l~_;C~*RmM8>qjZH}&dis59_`Cs?IA7QLcSU{J|WqC z37x;ZvR};dtCP(T_`F2>Dq05@h)hM(EQjs$7<(g|Z%-VqY>C}1`DgZJ&~_EReiRe=W6-%Y#@_((`)A8BBmt9zM>D0=L$4vu;Ez>tCa-}v(@Ez5 z!za`(H12puS@TxMzE$?NN&*xM>?Ju~RUXc~h6No9H0}KS4I5H~!`x}ELU|F6E7OO- zZWih3={xa!5u{?eXdTcMm!EWZnW~Ihc6%@hW#JUfM+7MeooWQ`tXV&><}Y zlHt`C?;YrFu;75()?w0SEzlf4R_NYy0kkb_4Do(?Mu@UZUCb657h&z&y8L zZjkMo-G*$}paP(0bHCRJ==1xkK3MffRiC=}_X_6_-UDGBQodlVe1d19xbQTCOA(R@ zgBK{1z(!01gcl1M5g$?zlPN%1!DJE2H!3LKO(gQdltR$Q^HGZH#}%JXNk>}hqY8KU z+=|2DsO#*ibAWPicCGy}&5`ap2Tv*R_3-t|CtB;GKo<0RI6cAs%)YZcW4`bz`U#y! zJKu#!%UcXKYSsv4nZ^yThG5tRHA++l&6=`jPqYzbXyrKhs-%?0xB?hMad&R_+tcUS z7+K1yxd1+o&;>y12NAYpeYq!%nvnM^2SqWK5{W#(|A5cA5dWhJMHEqWmY4?PL;nCT zc8|y)n@dJ;3iLYSZe|Qa?r?lk>d>G_W2+VAE=7T7)C&>hi$bO+xdpJq=X1$>z&@Qy z=BQ}qT;``qJbIgre3Oos!`w2V{nlHwB+~V~}j2D`y%4?FpgWIe|D=RIm zRwz}U;Oma=BNGX2E}>U~`gg2cQlfSb!#-}kl|1Cf;qA7Yo??gLH%5kbFo6kd4_00v zb`GF+q&Kt=z{gRX#CQ4kAfu4SIs#GrG=1q+NKGU5Bw38IoDe{;C&3uDNqbMSFP?#3 zEUxGD9zNvo_<8aJ#Nypja|4Cu?62n01oGZIYwh<~*J!uDT8SiZ|1;QomOWGR41Wa& zA44$DQT%(*<5zKJ?Z8XUE##+;o3CFP87Z>j_?4^29lT!z;fERwBzH zicupbmy{`p)FLX5^!+6FlS<8URS+Mn2SNk7eF;GbGDo@@eLB%+zSTD|0R&GVv^i9i z{K%OIIe&DVZsfP&yg6;YMGqhx5s!*{9eoo>2X4tkDQ@|^GCQrb&4s#;uOTlKDSWk%*IdtKnwp|aO+@*)YE*D=E# z3>A?>8?2%9tw`5Se)XhlZ%1y;S+Dw523%MXB84T0oJz1-Kv&k+sphgwb`iQDBYR~w z^Op?*^E`R`!DCR=xk#$F-Z4l({|kVtPP5lktJ6;L>lL=8GS zrwr7q)B$}RENTO-`;xiV0dYiLOoqogL3)wa0ltw;YHj2CNG{o;kGEN8Eks`{&y%?k zeH{Jtc-4r=wc~-;o5(yOGPFP{EN8Od{WacOZ}rw(^1|0dwMPSY2K1*44?q4a7Dkfj z*)`2WnvUYA$CzX1BCKhN1%+=`Y5c$nOe>QJ@|q zGFl2P?M3M*`G{d9s>k+nf%pVhuksot4haj&JcxD4hH`?h*z-8GZUo8`zfHPyW04y} z2FKa^=&s5ePg2czprTN$csX(a;8imiHyaU`nS^wVXnFCL)VuVy#M1{e=-nX(%RgQL z8)1{64Lgh)$yE)@uelo#8#A35%0xemENeUIH>agV4#Vgz1Tc8amtc+ncLLr5mIeH_ z0^5|7VMEBcyBj5fHKI)hz|=G2u|#JdcR~NolSx?S zn4-fkNNLygl*T;K09TVBID8q#X-fbuDL1viE?&^1eq#!Vg6m)_=Wi5h_O{gx*43q3 z_STqRE~weW8UXtSyF!^nEEy3Qw3y59gHlipMB_<-0S3mKTtUU_%8W!Js#@(?cp%&O zO3Nc;nR>858BnGSsvn~NoPOI-Me5vh`%UT3P|rb}r7-ONXl2KBcfGTYYa}fK9x;k?7JRsA~*%cCLf%6XZ^CF#Q?>&tEnt&bP2cV5{p(z0+B4R-hPIxKtilm7z#=`Kn zwMMwEl0q5iI`sbR8n5%IVDQ^Wib6Xlq)*T1bk}k0UgB(F&^2#jo-4RePw^Lq9j>Qv z8#395oy$SIsrhZM`*<%+iQbpy-H;pa?jFzG;1PZaGDfllIc_a!CxEktF^kan5*(wf zBg@}~BBj9_fTlxelt;uWK%mQob5RjMNXIzB4JFueQ3Kse_-RyF!fFams3?y3w%5aj znnLFKP)@}zgX8ddN?yOdj?<+Uzw>n`a4?@~ar*I>z=_{!U-LMp#q-v=tmfPjwz5V3 z*!A@A>$*qvJ5ld%MAh~soph~?tEJNRlH?vifa^l<|?f*(hyo$BVd0rk2J zjXj3vU_qs)pg3QmIRQ$xMB^G_W#J$iRB{Bth$g?&HV2{9M*H)6%+KXVY^8)|fMetM z%;4o>=QQ~gCHg|6pF69ee)|p1(_(Wsy<;%GcTPKV*NY8zAi%ZC_(5dnv;nH&Co=GrazA1SClwfah#~QXQ$sa zNIn3AF8;qp+y6lIVDl8w3p@#Oi(20Pcv>W-u=Kzb^Bq8pM4R;d276Yy%tMc!!NNy| zhmsX!Xv5k76*Ekr#?c^2@EBf#h3~*MTK{9&F`0|GzBh>RQqzRlx+u%N=01&5i@~>hRbkJ%;c)VBbTenfbXD zcnHs~VC&t!qDT}gc%#*@J-h<>8#Z(6t!&X7uOr^Fe9m{$8VrZM0mH(cKT4d>QOlCT z669l$0TWHOe~O`e1u>Mhh}97J7Q}FrgOT1s%1l_vFgiQg*@7v1KGS*A-`<4Z0CTl< zgW=2~v8?%%J!{wQS<5~sV@Qypcz9%Tas(_2GH8!Y6nb34&GoQ@;2tFKu;NB+tQ_(~ zNWdaaxLdPwOHn!n5uK)4LRoFd6eGH!%r;34Mz5W(t!&(tFgQ(`)>4c#w!Vj2`+31Y zRR*xl7mfY3Z24gkA9pRQy`rzX)eub=K>~B0ViTh29~Q3x9bpmj|F4lBcvk*j0{P%h zyag?DSj!u%ecxI8m~{@D6^-#U8(?r02jn3BC(U8L0M@J2T>u6_B#J!L!tzRMkhnCT zqd!#P2Mn;!-ubLs8imrVo0v3N4WzsI81P+7%>P8{r0&H-Vtc&e*!-r?{i@ps&sulx z(>VY_F)&^TEI>(J+nOu@1Sxju{{Y8X6h8)xWf1P{+E#XpNXXnFw!aedO8ipS-J)a} zO{pRYyP6gWmP*h;X^<1y3d|(bkEFXsbqt7_)&-t%`;Gvpw;G%v`S}*T9-li?ew;A8 zn?x)thBDV=lA~>qY1>AbBX}&CTRP(HNaed+)uF(yXLkic)ew1(1-tU84)4fP);G3g zY-|g=P40f%)KuHlKE~Ksds9;*%$kwz4S{&7eSJ8Xh>ULAHX2C;!}maOm}y+lo{9%H zu*LMa^+#))0ZrI!Z2S;r>P8Fydv;cU$so%gZAptEw=`1`fR8PqZ_zeKPIc0wxTk<) zaP(QI(TLTd$<*H?hbJ=o(BO;dd0ZyTqOFax_wOK{{>$0G*y3!W(NWMGK3}X}W6LKI z0P{;p32sVcnPX&qDy2EhN2Z2`rUuy)Q-gz3L$-S0V$t@c(KZA@6*GzKu|RNMK~?ok ztiO~+7|l2;8k{n+iTbdAK#xR&fnT6bQHU-2l5!i#_-C4%gp1Jy|A(^8&Oy3F%uw_R zI;u=#GDXdrm<2fWQitgM=o)n3ZzPrYj(Dq|%~F+w)*%RolX4rSPJM%R{1rhT{z}k_ z=?a4KKO&5FQEKgAXXEj2&`$6NLju1eC^A%%CzB+>g{%`)wly@5Z2X$&@HKaD+Xf`cl?c}#$S=&M9m`w0 zgNHVb^tEgyfOd~^Wpv~Iyl6AKYuksnZG$tT^iOT#Kf`(v0FAB(hrf}!f$x|6@>w};Y%iO&ywRAJ6uV}za&1|YVlFb&xV2Io6Ck73ukl4u@v^j z7sY+&E8Hgh*sQU10BhzG@b_pCImwp5n~T=}O7$WpRSuQ{nvpb~NaKj247g8W?*Z+` z1mzugN_3XpRra3!yFbp?4$NDlME#D%aE0PP7VlWdBYu^AZMQ$p*mzeQsa5$8FDaES zDG`}zNA|l%N88&0#_$ig?Ww`FEBlS70x|9n8c#X%8Rtm5{z;tT+9x~5otXyrQ(E}d z-sQc$%WLqE{Rg@u=gJe11L{QN=|UkVLE@F~C?<*JgAIuw2N6fUR=)AvH?}E@{bKgs zCbt)JZxEfZa!0hdh1eyTQOVgp0&9gdv6ix6>ZTKS7iZ7+*bFr(6$Z@kxuP{AMZT46^XpqHJzDl{ar}n1u2qUgOaTWQC)@H?}tuY=Ho+3 zG3s98q-cU#a6fU2>9r)X9#T_4_@L}#Hv9XLZ-jYN{rt*c8#^!^i1QYT?==5|wk^?s zI_;dAGGD&?s;lna##YAC`jJd>XChr!*Sania5N)ZtsVBsnga=5%&?Dw7+-a_;K%KC zDkk$C+P~OOYdA=W_5MH-7W^*?0gEUCPDpV`ixgZ(dh_;U76hcL*cDGA=^1*7Jr-1; z9tk)Q@>)JJUzdF+`Q8QdSFN1SWe+yZy-_~G4$E$x#T6k;`!wY^w0~IXscf*#RnZFD zO{v|9&UaFS$>=D=Tb<=a8=C%LwY8J~FENLoAj8Hvk^1}<(FF5%c3U%@3&hlX{CvOw z;``6?0Pm=+ak$ub_fi-F)+1t>gs_373#R)EwKe?i+WPf(*jC78K(`fZg>JL0Vl3E8 ztw41y!#P8HHY0lHhn*R_H~DeVnMr^gytkTQ51IhZw@+ig7M%{$uGNpbMJMj|&|SC} zLeoAC<8l@&B@W-wezFC~SwaFX&7GK7wqwP(6_y^URf;1D9I&Bz0m&jYcJ?%~ri#?5 zOHKkp2S4g?2IGrlCkiftV-Zfu6CCDY7{5eBv`G7bY(@krO>uUk z+#b3o*e*>^0Nub_o#^l-0&2vlsl9JyTN^tT)RI0&G;4$oEU!&E0&XBsB>dHm_G+Xk zVT}md3Y!0DaaM+o7=WP1FanW=_&i)%m>;OEYa1%SyMgJhhSfh$IHQh$tJbSD#8l>w z)K%9wbKz>Wz&dP(>+JdUw&OrM%bMSNp>a@w)pLO%0C|`62m++%>~>(DUwl03#Ko0! zkc&A}2D!aZlP-kPa)I_;2$F?pJ?S!KOamc#6WWm+td{b?UOeD^BX~9)UIr9QKc7TL z+R+Nb9!usR=H-%Gg;d6VD3zJRU4f#?lvu5U5iuQuWGBSAWR5w6{Pt}WQ{uj{s3+l2 zTlE}Z%;}ukRoRbW_$W1_%;EqQVIY*`T?I6suu1C#>?3Db9BB$AeIzJA%3sO8`3%p- zPbYJq`y9Mxles5dWtxc8(;|uGH?NJ==8|`Bc|WP0Zr-j@8!GiIHp=;IE9O$|3y42< zOKjQ#um1ChJ?GHpzdu*tIjcQ>!EY`@Y?vjHX%e&$MD92SUzE$GQ4s2qjTPp`dH!{k!~;tof;x((LOYq><0M)J}6dgdI>tm zh@sl+A~;P!l0w z!0ASCLdbF4hE^4u-DoUjU4)hG2ohUDOhV2OqzoiAoZyfu<_Y;7FGb7CqY+ahk3h#9 zB?%yVmPJL;`&DK84Q__ZjfilyEjwh_$yJQi!?8wpU9<+dHELj>Sse<^3kJpD-G;?w zwT3@J;5jA0cPdZILpc^!W7Q1}h|*h)3I>=M*XD0M^d$bF776TBd5U>PI-+m!{}Q>3 z$^V#gX+sr6f+^Mjcz=+9suN-|l~6`roI7UM*oyAwH#apmW3}{XnTCc8esOtE51kbX zI5zj&3+-(G|EB4Nn{Q~Mre&|%c~utoZs>Wuht3Ok7I5~D_L6IV2E@$VS+xR~tUnaF z4}kW;5xv9-qyipJSfc1mglFW@c%Xg6Q@uU~xoDzli?1n%Se>3$kKKE8UGW|8oEBc}|ar6olAfj=fD zxsP1S<(3p>=-~t^lAbO{?!| z9s(}1LwLq^Hdr0M`eIL4ig$P`!uhrJmW_yot?)j&_?rrEcOj4ok1P=N z5<~=iD_tzplqKV0?|KsyfiVS9SF7?w>FhV#4drCD7IUogkSmq4 zE^e^uM3$V6lS%UUmuoih64K9-G(N;lR1TXSc(4oEfTS7l6d>EVObIV5Z?Z>Q4{M|0 zbCfpI!tsPQ>>DJDncqjIvRv{c7Vcb<8yG(?(RpQx*Ctx65MC*t9KD)k3<7vlAm|*A%n0`EdGCpso!Qz`Z>e#!y=si-3HOKRc&?W`*a;5f?oL2 ztcEWQ+2P=$WPKakL21sn;VJ4=nZWJYvXN}A4bH5`M8=<}cOmNiG3;+))LZtQ#TSOF z46z{`$`!y$v&C>`*^Q~Pk>IpI-v0JbTU|0)*M_+;1Ala?Lq^sj;_vLSWBc^mN?d!- zMUaO;uI`Qc;Hj}lm{b=FmSk=S%b9-{+zl9<@*hS)C0%c%QTvYQjZ7LH13MxlbGQOY z3|*Uu&-tF0BV=2j1}cm`^)>`#w^P3pUj07TUL6gAK7OcbjQ^aU5;mMcjM_a_A4A^& z+@vCTRDYOIA>cF*r7<62Av@$y_i^|cK*$A{6*jdvnZ`BS9UoOByU)jxdrr!PgUcNRbH7FoP;?)*Nuw^tiNu z8?h9sE8>iVV?bq7C(gQC@kRp3ORU1^#_va4%qMC~&Ss;&mTf?*%x3{QA7l=sE(Q7% z%v9{pLYnH1r?TB$zL2V^A^6fdp_K{)iN7-dPbaT08q*Z+G#rkgQ)MngRbgk-%o9#c zbA+6lL(Ak65ueW0NJv#ZvZh8CZV%vgaX-ATw5X`dA3b%N2P8Le)Y3PFsIX9n2`yN8o|P;m zQw158X(^A&9g$*$Xk)|ft7~no`<(zborbx*wT>H<+wvuOBQlHUI1+Ay3=6^GR77Q1qZ{NvDWO3xTm86ad^apnFHT zfbQ+&6(jDsfdO)TSz$!=6}mr~UZIC|WreN>g37=EY+nH{+A}vq@Q%Sj@}F6$<1gwv zFrhP5nE)!J->VD`8b~RuAiU85Tl7NyZ-UmRp^3Wu-2Rj!RB|`HdCEwO*j`Fo5d(3` zqi^Aggl!)H3NCP^;ysS76Eg=}|BpNgjYYyKhl&K|VZi!%;u;S}(f}iNu!!nD^H*mT zc+YxZ?)#qkm7IDI?zX_z=IHKf)iiIw(dA8Q@A6!HzlG=*%1ID}j|q9ZV(tn@XcC46 zWRhBfXjUuL*8TmWup|gNlPjChDM=9 zi^FXd!CS)rIG@uCdaQ>}LYxjI%*c`=J50Jvb9ZqGOTbO-S}myUkCKjZV*O>varuw{ zN!VoTJBu^%;89h*M$_)ZK`?%L^ClYGgq3g;{{{brK#&!%d!(|IFc3O|yaZkaQm`KP zj>P^Z?n!;g;G;^+xb>r{kKU%MuCKr&_T`i({x`7^?^=iP6_;k*hD)~^F=dn+M3+}V z4<_qJF3Ye4UA54tC`}MN!{x82dVzGKK+X8*p%+P1}@{8@Z^&_etIVzNp5s*)z8v=50xtJxl* zP<}w`toj4ZTajq-4u|9Wx{JRTxujL)y^4c=!&-jzYucvEb5_7 z)Yr59)CoU3nq{yw*S;Mh1`vY~?Sh#W%j!`C`p0{sFmIH4pb^QH*anL9Te`#fs6YpL z)VX7+aE)8G!)O&Ht;UXJzIf8}9Out@lHTVSdrkyVUf@pQQ75bWbM~Bf_Bm?5#Ppz; zahcPP_*D|1#9HK-drUJ`=yKUr6QvSjhUzBz`Vc_0opvSuMk#Nm@}*Lq{Vab&U*7yn zaXg=nkI>+O_XA^zT&ruDq=&Heko`9hDTK^N0} zoTk!&=|u+ibi9GL5(dGrZL{uss}=gURe|h=&g&Nx^!W`!|>H2 zuW^di)wE*CW4u^hvuoky2M@CL>ICB9y)K_)pgI9({OUxvJe}8@OtOyKA_Z4I?t#R9`3)bQZEVQ}y>+^`uErQxdhjbBnuiDDNsfw)fY%$aPJ8F!1lI9OK$HHSKMmeL z3|!YFW_5-#?Lt=r`lG`kq`)ZKpWf4-i{}udl($2zQ@O5c@Q|77-ItdTN;b;xv95c z43S1Vf%^?tacSU0hl>7F#5o3Go^(4Af}a_d8XjoJhMFTJK~)4Ii_kiRp7P@`+YL3( zBy+e;JTlWf1ZPZ=?(9yWuB6y(G4;y)fsI>c&B1VQfst>Z%JM?`EW@AOI?w$0lFd8s zH@n#NwC7RtcVGDmbA82pY4Z~EvFqpEzjHJ7?Il$&gC1T1dYHkek%2$p0tN5U zuaa9FWd;@TKvet>iG_(<68IB4wktQ5!(X|OFD+lATTbUjbJO@|7b3Hu9j8q6H;#Em zm}xy2wM*f#bRBRFZUq`K&3MadFU&Rf?!a0l`07YQE1^c&HO|H8;H=XV7&aw?#n0t%)734W{3Y8NZnKF^Q zV6iNoz3=Hv7jMs~)7Y{0jP}df2bNCz6rVB;XbFR3nw)j`%hvP1#FZ}Q;QYF|c41FSR=N(OQ)?ev30;pB_LtC1hyy0F*~ zsaE3tcs4?lEKL|y%uNf;WT~`t7;?s*&1GGA#LL-CU(THpp{m^ zsEYQL5&zhP5NzCQhd>_?cIb!9X=tP~jY8^oYT5D(F#lCh1GaXknMe@Lvsh3?<_JAI zNCho%ge&W6Z99zMI>w}oSLX0JY=!iES=F|x0~m$5e!wnbB+~SLnV^efu{v42a&+aBZx<7yo&b`PEtf?0jMW>nyjwU93$Cx9SyT}P=~VLwmj08|8BOefP0%-$4Y&9`n@y%4YL;oK|bU6Kkp11KpV>< z#4qf8ihi;g7iGP;#r(C%aW+#~tN5Ms(kAHsccy(f$b0qzrEeFPRM0V8!7mQgB{PtZD%KNPTg zo$QFi<#9(eRyou_Jo4IDOQ=b_d|!DUB;LwpRj0pT1QfM%6oy=ePZgv%}$5kukQ8( zP9U|h8nC{<)08#fl%zgSHVi#GsuXgA{GJR9@Mua6$vQ9T(;l3 zLdAN;u_i~U;45r=0J`TMMD0QS_&LF+tj8gd&%U&-Mv}Ta7W^sYNe2h>eK=iwoPApy z_hNfD5Cm-A=3qrRU!RSG@*(=5&7JV60GSqaFl0e7N_6EWS%%>w_(#$i%XjPr?OO(J zW9Pel&n&2;cwvq5>>!R%c=Cuc+ez?ZzSi%m6SiJD_Sfivovb$OG)){G{19{8i9N3s z+UD~>&0VlphS3w#se0PFToLN4excw#H@z78$^rfQYWPV=TVDZOi>+ZNO@ZL9X>@=& z6s)zt@UhN-ZD<>{vZPqQMwkNcPv(e!CXgE94Gr2`XWuWgf?O!oWoRC|UvS2x^vB;8 zX)1)gG!1#_$H3c&K$6Ak54}IuQETwomcw&W%t^+dGzw@Mv0KzaoI-*DpD+!)c^;)a z8E1aw(lfei8u3?Ocln{v_(s0(kV_xLB9g^QGN`*eTix!h9{wPK+vBk_|4j=F`xdQ2 z=FdpwaqAA<39ty1wir%*hu^iv>0AT!N}6M~Z)ebo)P_`3ZYnSdWWTD^vK+({yB~A< z1&;T@7YQzEp!DWT(2hP2-w5;j)Uh^aU3ny|urd*k`Q~a;m%^2)3B0%HSu0@C+L2s( zsH#@v5GHI3`1Mhmz;nzBNV|`zGVAG}tHV1;4TQ3{!<(DlGo+V3{ikJd; zfGk@|crX-AuxuG^>1^pTZa9%U3WM-Y8j082M&j2uJ@BPZUB-JyFS|7p3~YYD{G@%q zg$#3ndQ}G-@wxvTbw&J-pocIXY`txb%%efN0Mb$rys6Yy(A)yn7m>yu+7s8va&(ciwN#vB2CIa^ z`sA}cR}m`GK2ToIzP<^56;zK)dv%;cvAD0MnTO2BW{*AcD%;oB;@s}1dN9l zDfB?sR}71;(!qyh{X8l~P8*~d*X-99{y?*@pA%h1q9t{iMqrzO_WK4Le*hcwP|AM; zlskU&cE?>ToqH{pW_LM!xy|jPQ5?iaRkJuhV z4Rv7xA$lJmosQwQaGkv6!;ZV6(YqXOrjEADx+4+!L+ztPx+67g8=jMuQ8g7&<*4BX zf9fIBR)wsmcAo_>n@jLon-Ao=#fYt&Lho&@dJm|vJ(|H*ijvfBd<0sHmzw1~cn~sjMd$uRC5Hm*G z55~V0^=A|ETKlN!nZ|!d_U=7G+rE4>v!|3#?%9(B((W4&NwSIUuK2XU=LVbU%sE0KWPO?H_ z>tH+I$!MW9W^Rru=_*d+0K*?>NEj<}P)NO*X%rGCMmDBmpi}=M+=*zzH^x#MM@}RP z#_yVkko>~FAZRMtB%g$wlS1E1do5MyhwTCMNfI7N7J@eQ*a`%Ip}^eE`1Q$LI(fXY zV`}eIN8_^)vsd{$R&sk$CHI-p}{BpW;&?_qo|7AlVM zzOcghr7LbH2UrMn7X%rCcoB}R!$kce+3$yefbYUF!NGcyM6 ziFg492Sc6)iizCyc$~@-7?5$cR12`r#It&&w%y2t95HnOU?T0=T1TQ5x%D*V1|1>! zN>`%B%iG~^G>{liYuWBUHm`B3X|ly4X5W2O)1K6V>d}@|(3zh1fx%9IHFpN$@k}Cd z0=1$a!8)ouovg`e zZJHA{(|HLe56lBrJ5erS_6L9ijW|iugsM;;TlDUna(4CFp_a@ElH1JPv^|k& zN&Nl`;1ce89cd=pF*`4f6{Ibx;<|FVCi1L|v@xmC%sFl~2Hl^^stt$Mk%m zD_6+p$SzA>BhYMM*yI^(K(RP1LVW_&5SKKa?f$obKi!%LIpC$YDpBKir8c~{{?Y?c zhYH>=#2sEGQr*&AYSUOUK2|%rDvT5vjICd~x~MuL*H5f|W|cBx{zT`|-r>+vJ?Y=# zP86Q5Z%);OXLV-Asy+4X zFd_jmskwJ>ST~B>n_G(^gN-=dH{{c;Ukb$amQW!%ZX`q16*6uRG4^#7BLry-9}@Z- z`}3CifoTh-2OSo}%g3cL1aT!tmlQwTo!gNU(R2IuN$ZB#MhHa^SGvs~kd*aoWOZ&m zF6`SU9NJIQ{Iuo39Qap?@S|UqA}VAk%Hbh?A8bP&!yCVirGQohVmg3Jhon?eV1fMC zB;1_+nZ$y4C$KNb<2KXDt`UMC3^K5?kdRGcpG2mMFGys}FJ?MVl28sOXm(11*qNU{ zc@msC`ea|#1aw`;tLlLw)Q2o}HWnehxo8?x#6kfm+eJ*iRJIE)hky%3J4-6**2S39 z$j011Q_cb{Q6Pl{KuFDI`?twA`CRluQd=gknu*Eb6Cygz>2<9%f{ByC?)S&rvqM$EktMa9t0v%};ZC@o< zjwzQPp6&3g1cz+7kNNntUPzcv4^E|Gm_?K9MI^5jPp~h-hE`6ZQh`w{v4?u+@tCxsqw4I9dZd_BGtV_oY*@%9pQ(%@ld z?Q8|aL0n3bP#Eh&>Cg(Q#5lkJs6qkt zniWg!hwq#)X8;LI41p1B=VA2|u8)IpvLfIcvfD|Lt}ILXT~l;1K1d*81GsALKBtJk zfFVLP)=}*T&U(Oa-g?dx>?R?zSNxLstFwO^U4}8Wq3T8cJiiO;EA`o|9?H@}HHhrT zq#*MVe67_hNJ`5wr^y9W_*aPajk;n(JDlSM8?V;mV zVGegJ>bssTy1tLUfNyomp>{7?)U7&BAsk_(4S($LY#DAiaOjZ0sgVAdZs^bwkU5*+ z^ENJ{|7LybD7gtJ41f%Qsf#w&Q9_GGGQIT_`Y9q%7zoMtP8u)^Sv?NR3mTuVaKdBT z9jfDCFt~li%inLGV%L6zb3WZ}dUMW?^1XneXWS^HY9cBk!3wEelgk{A8=f5?+CVSQVrP++~p=ynDUocew_Aqh?r3y2XAJp_0 z+&6E*ccAt|VPL=xZ8=52(ScAyYOcM#)jiE+u3Fydii|gtU?_p)7Oa6G`oh zE%->O(xSn=eQfi;T%Z#IpNZW`lnz$NRhY;y*(bD|oXxXS2w#h$3*@_O=V z0OTh-xOpEG6v<1jb>@X@Ufwyq#6SoW6%MVQn5W}-W~}`X39^UU1%0yDdp~Sw76vcg zw~{>dz%DF#3`txB=iMMVau9S&7YtHmdj$wa1Z)G{-eYyO=ot_xqG*<;cQ=LBr3hx% zj>2K2N*4j=27vL*x3B>hDNA0mZ$e3KF3LQy5|y`FA?sA{TeFo@O?G8;yRctg&z}ZA z5vG_S=;^Rr5u_44VkM>$UQ&o^|MlGM?T;gTBj!{dA6mZoc2+BbH~I0~H!mN0+?haN zhps%{e*1HaMI5N0VVgk%p5@=f+FlJzJ)rxFsv$Yj5F*2X9n}mLl6qHk4Il0Mj9~zz z=`-USrtiIX<|D5N9TJdl@TuAV*)aYY;hU~}rtc#&_ulIhTC0rTwU@@zUXpJ~zLNO! z^gX5AKIdBsbsA z*F_i^)^kU_9tkQs!gq}$z@oe;1M}P+ZHD5B8sko8z>Mi5{fVtxqV~O$)?K7A7yWI* zaI@!_^c%8sajn9blNy1djf`RgB9+gj9#62X0Qe(8P8=gN+$J?Zxt={fEv&-qM`CAQ zH|Ol*A6p;LL5WkI}sl>)z&v)o>%GS<&%J9QCd%ogjVHNgQotwY%Dg;BM z^S?8{t02N&F~8?f4PE~y4{XyaHx$;vy{F)D3biT5*`$cI`2)06Z6y85-}MVu5v)(= z|8={Yt*K{oO{HGDS=D``>u)jMx^Ua>6V{q>q9;NcZsNbQTIUn(5~(9=lCl4KCDe=uR@4Jvep_f z7(^m022Uc4@EZhcBpA~Bdy%Kh?-~zixk;^F?-l8?$NhR{wU(L7wtn_T&{hBB*AZ)R zHFVa#@xK!O29?;7J>ZI_pdXa8k3I{1l~Vugnn+7p4yaK=2n7RxX3{2K!Yrj3d%7XU{$t!6TUipdm3NYRC42LJ;TxlK` z>!`33O21b>IPX3}xh1KPhDup$NJcPP4n;;ZWJ4$aPmoUqnt}Pu9#!!9uQ)qO`3S7s z1b^oUIU1L_RHHY<1I}g3oB z^Ds!eG@hUQZ8E8IX@0W1XA3K;@=ck)IOXV3^>!LUMkY|pfbc(>ED zG5|K2`4n$%S%@nyo4@I)#(z3=R{A*vJV;92`W#i>?WtT3a9Oe2y3f`cFvBEF69 zY#LDd3i^!P?b~p`*gm*+-JaXoso7GxczVyewS(J@0~{%jqbz8(82LR~4y6^%dw3DHE> z&!6>s`LnKS?%Fx+PI3SK>zql-3Lk{VxEv)5OFE)ytw+YNZ{T0_2b3=&_*+@WJR9Bq zzz6R%*m|EA??LfzGN(`@&Pc>UlpWY*G+gkd&R&r#9f-oAwfy?EL-{SL%2+`-xuzP6n++{DGkApG7FmoVfk5fM` z7ua1`U`>+~0y)INjL^|#AQK|i{>)o)ZxzZr;GnXR9(i;ZEv~duA5#e`nQ2crQ!Xmo z#kOH>p9Utnc{`qwmTgqWZk0J8&D&9}&;mB4HFhg*ku;L}=0RuzR=_e=)o9U0D43<_ zi}#^L4WclyCpJp5Q|AVDHGN7NcNBL>ObJdq zF-qB6=Fi1Tw;Yf!2pDQK*2*b&V20g7x=Skyd|JJwMfQpL^RI26F8+!VC?Vt+93((o zDfRMCMPDO7(m&phGZul{6yCasuKfe57bnna{y|m}6cTa}7m6svebYq_QbVbk91hwj z{1<|wdElo87oF|+NA$ffBIY{fa=uOIs?O2uRS|K9_bUln5FLNN?dF~DJW@^VhutR+Ud2Dh*i;a&i zxK}}MBmoj7h)=Ug(%UksH^P%-xK%g5-!>}URI0-&YYV60%yRXUB!@pK#Bs-s@NvPd zHyUxLV!$T)Bzp=a-@{XQ`g_9d<$HJvPvd0w6C|fU;Y>Q|)|9d20Z~jqSQ(REbi}|7I}m;C}hX;HsVtu&kRY5GamD= z#I1As1l0a~eFE*aG;au{HYHX*=XWX9Q7i;*FBCiP@0vd&6H>Si8Ezn;P3d*Qb~K53 z`U7ZsNH0Pf3Hd$pScl1&f~ZOR`}X&REr{@$)(CBB3m+l)#lr~U@*46{vLNq zV0_OLc;Age#Iu_9WbyXReV5mPkXVCaQa^K2pnNY7h~MV#Ew4Y4eQdo=J=%7b2G05# zvw~`+s^;s65^y7J9JGsb^)sM(GboPuPY55+S=*i;GR+onYm&Py>xaUUA4Bj2QV4*P zBX??xLqiYQKaL9&uL2H@RH;Q<4zj)$eLP4%rCwlVQMNA_@k0VBvF&VXCj8KfqA z5Z@ubv4S{tjM#u?$!kccRRKk7zUqtTHB zGKWaT{ulyvivnvd3wV{qS1w+Boj@5K2$Y2z#5OK99>2Z1L4_c)UadfGCBtzu|R5Oh(4m zprLm)q^?ahG^DOcx2z1h-NBXkQs_Mu{6EH{+-jGbg^(Uz`W1xzw{I9 zKYO!hNQ-)6F^|(3cZ2M_g*Wpi5RwFtgn0aqf=Op6VDNCl@cLb!-6Z@au}OsZOUW>E zeR6u|{daEn1bD=A^Jmw8;?n)kd09T;<(i?!<1Pd=DECC;o~Y)BdO|K4Sc?TnvzS0pkRfthv=xFE z3rKo}bg!fBFK0WrBBwpR&gi9MrO|)6j>3S3%YjAZP$Qczl*U#J3VMKXFvHkz_GLKp zU3<~kq009lTO4@cckv$QKoY-Q<8?%wUsmhzxzrYiPn}lZLCbG(svB!nJlCS)fy(${ zxA0yY6Z91633M^^;x-c`mR`(7JMl{Bf=h8B&uTX8*DgP(uiJC$bHpo6)l(LvBDC>| zTc5f0)@N`6_hCw>)`Sb#8nB!;HS7G1J?r#?muvesTz2bA#9pnfr^kzi@t^eAt*n*q zYa%R~=4P_C%HyUcAY@6uZhH*%QNGo8+536~@)FiJxQ1ZR>9M!#{O`s8BHKJ~^3MLr z_?NTqpy&U*Is4`KHD~`KF8B&5$JM}+v<$690#T9`ER`8#sPfsIOvIub-VVa7 z4Z3;Ggnu?2mFZq7R#_fJ(`;-><*B@7(Jq?S_sD5IXnsDL#Kjch+7l6CoI?s z>Ua`~&P~rQUFu&7dOSf7+dey?k)R6Jx&sh(!agQub-VNYiPsN{+5Qlo_XN3RxS*jj%6)Qp5Hai^ii+ zFcf8h@M0igsE-u6*jxF@J1_qBh^fuhk1Ec~OC;VoG^5|)yGN{GQ;A?O5wrse{zTfQ zF4P5SpAt+Y+Z{k;({yaTo~#2dkkeunbfcbN!uH=k4x3;Mb|K0?RmmkGRKJTc13@?NZkoe5CMYqW zxG1waUgzq@<`vi0ZmI}e|9xlU4if)%q$qF6c7dfJ#_T>3b3k^?IxDAv&Dl}wC>9FJ zO`45%gVncdFtMd8D5SxPx?zVe(%d&!T#Ng`oQ=J0V@YQH5dSCsB=Fl1AthA=#4qNe zL&BYwaBiW40jmVf0Q}p?XA7?V*cKW=zDW-QkO9paK5LbC2k@HyY(e}|raFWQe!O=v*En+#tbdQM3CC#kgq=^jTsK1f4g zz~fQO-#OHy(6be}0%b*CV^b;<&ub0A##A5%2tQp_-BG8{sRjZn!1|b`8GaQG(oAu9 z!B&P`wXt~6s~K*?6ZPdB&DAZ1#r%uufwid~1&{#)G&%>}S+@hf!m0SNm`hE@BOXKZsBTSh=?MM|hdCI0{1R84T=lAe8hVAa!9wqbm?T15vT?8S)P+!*2pthw4hdTcf8Y$~AiW{sEL+d|@{zNTMKVhE;?mL5vj5B5 zn*i8V)%W9b?tbsv_xI*)^WK}8H#3=idzmHMB!m!1G9)o1Bm}`hwh$r`iA8pym91&P z2&R>&FbP6s5tUlBh^Wz8jg%@-u!yxqenL~qudPU~|IhcFd*6FAnS@CF|6p?8-S0Wy z^F815-M?A?f^)X-8XRlYb!+{(>5*nRa1x^$?HSY9N!IqXY50|C==uIeY0lx+i||OU zI3Gm?a;FSz`*Tk?E%Sr04FwBeHm%Hjy6S;s^hh+r*l8Iaj-p3&xMo+o8+WG{1+Pi?`tKe<4qv6_yPd9wA;TsJ@>K~^D4g1-n+{hk1OE9URaLp>d^P`K}JVSU97 z{iMIWo^KU?=37XR`)4TPZjerK0&fw@+yJv{`}2cP!d5lakZrxDJx%W|qGWR{fEbm! z7hdmth+6VC2s)|Dg+3b!bP|gTkgH+U^%OiO)x}-acBOb{-WZ>!*{?&%xd6@kCED${ z#PU8=_op45eBDRHn|Yr}e5X~T_GYmwV%LR!*hIB7%Cl0PXXGjE1YM~GWKBUFB1WUC zjKN9=8Ijm1=ux>YZUAYoc)9Ooa5-QfLuK!z*S>%~&b1>euw{SkhBH(8Xedx{-!5eQ zpW3wxN)~p32vT%{7I(nSpKiy7^M5WaNFj!@4_Hikks9wK)H;|?04^~tyLpPp)p=AVq554(bKb{S6YPR9Idw9b@ItwLvk+K zRR+Bo#CSnYo)SNo@q^1N+2H(6iihrBr4>nCQq((IdT`>brh$Q`^|TT4q{vGW=F&WQ zYR6_yKnxs;+#F3j1v40Ju5;BDrT`K(OGW8JwF$xQ0{(r1eKuVtN}9Y4w3>aH5{7KE zpEdP;6$rTMIu(BqjVt?f=|lC1+j&Yky&IHuxVxNYtJCEpFK`;X&TmyzZ~%BnSx+wj z@(CG-XzN~RLVFNph$x&oP2fC+SYeDB31d{#4LhX*24089Lv_TtdW!~?oZ2|w>ze+Z zfgEamWm)7l8mc>uz#BAl_A9-k6a9Qp`B2(N5L5H9jp;K;>OG^pi5DbvM(%U1!zZ=Q z!)T=w*c3aqi9xe>;DL<`d&_5#Bz;DvpNH@8b9e|_K-QBX2$5Bo^GJKLpkP4i^BBx%?W{=ymKQ@{{~%y-m_KjLXy{y10{B6? zyze5;;#z2~=7URL4WE$))OW;+>MmDY=Lw)f;6d(7?0{Ka1Zy7VHPx!93=?RHcA=(D zg2+S#9Guod$+vNUM48;7c-!d%rP#Vse^YE(#0*6(Yqa2xteFf43Zot~8ZyF3C~;4= z5=}j<7ra$t4NKQnT9ckQXM8vlLZ4k5bOf`)@q-2Q>y|}E zOC>T8x-bx{C463mrq`il)M+?u(L!3gk)$y^C-flIOK}9cKoA^*vIyGl$z4Q^X~Rr@ zqOF~0S4!32gj@=AZ@;)CU0X`k-7123_&Z2ybKctqN7|+t_wj4uJ)HJS4S(72kn|VQ z=aGYvvOb`tPK9*gf#wu;C%bRuIM)td6^mQaxt@vH8BER6lD9*V_H>6VN66?dPm zJjCvAjwqM&96QyWw@YF5d48(8BWSSjC-pevgIrn(q`9GKQi({&2TR3(c$x!RqhM2` zMJCQ^RP}6vpPb_yyD@J3%Ht2(-9f+SS1aIkzFse^oPKS3sb1K;Y0tNB886J+zIS_T zq4LF#tjiR=NHgj!X4o}`{eQjLtQU!~KDg=b&DWf*x2)a1b9#E`g=>tK_ushrZqF^} zp1HJjaOtY;+gB~_FE8D2ZtuFW?$&ZFNh?$`R&MPcgS~NrbxJ>D|G;BhUA&==xu{Z* z_C_)h?p##HjudB)_@hz(1lEYm1m7dNPO&(rKnMR5aP#wceso035I=!v)iUO&<9xy= zU<T#U?Ww=}JjLLfzO}T(a18Nr_=967@`so_H3<;bB_JRt8@82mvF!jnXndN;qdS@%Fb`U&duc(xB;-mfc0 zP;WoGQx6&n`j2+Am!+Tb{!@FYwxz?2615nPvzL1#do|+%nj#k%+TI8VvIQq?JqCW}7JEXv1s~r6$`+c?6>(m>8 zpC5p{_YL%6A=Xs##}T|7Xe3AkjU-XVcmh~SE}8)e<|A^!fgAt_98!0X3K8y85<>AS zLpj`q;Z~ z#-#)HQ18z}cvbhKv7UPiv*-JW4a>Fj44ULem-N`Cb)A4w$KpN?H_BhoP5-s;wi#0 zYUfWeCRxOT4slvdswd*Vz+TUoHM~hGEN)PH%P==d*D!%R%g=NDLAvLxwQ^&jg(9~Gd< zd_CDc{J~8$w>I72WsgY7bQa#~$*ez8iAw%#QZpLUa#A|R`5U-Ofqg#}vUBerQ7-7r zugiDdNIvjx{+9&$NGlPfYD{_L6^4F->w1O<**nkO1)~Gc19T(VE07uV&bzkJ-i8#<5*q1HCdDJ=3K_A$h@oI$LW`EM%3&4` zS0eONL78nG%2QG%q8(u3a@@AsLW*H%+`vwjrB6XqS4lK4_Un<%#NFLZtMh^Ibobqz z7Y!s2?Jf7NOy|P$-Y~6vN84W3dD=kX3wz6`S9|ivkL_E~KXs5-lUu4IQf-4H3=862YXc9Kf}BizyWLW+ z{EPQYG)#z4X6+u#oP1Fizx58?eNNmba;UxB~& zQEkTfR}C>I*k(PiZ~9)q^6oB5DgP(sYc}7*%NEx?K{e*W9>&PBMD zqRgWUlAs(-4U=*MVset89=)(YXGmW%WM2%2v^{clbr)BjaqoejOASK+ z?TE2;tBWD_!C+Khs!73^KB0H((V&dqSWwc~hp|}-FBRJ24|j+y@SS);mu|G7$F$fS zhj1j~a-v*;<>qtmMm`KJ@R{7TkF1ft@@_;#mhGN(4`0>zS(<;M2FH)#BUzxDBW^zP zx#$=sA!GnhcR=1DHu`0v5XWX~4x;w?hd?mdkKS3N1dtlWX}|P+QI%u!=o}<2`49-U zebGB89s;Kxrynz6Mv;T>3Nt-a;ZW001Z-zda=4uk;9jy{>ENK_uygC>(i+ggYr|sw zb60|6P+q%TOp1?dl~o0TOwTgZXI2Vow_CC7*zMjk2qm}tfvLeecxF- zO1}=xeFJl^5i1C4J=W%3t<{1YsDjghIgxWkSl&T)seUzaMacHexguyyNL-^On-=f7 z**A+0vr~|7G}EVR{;VI~?3rj7tWh*D*U1~&ve~vwZSKvY&M#yWnvu;A8S$fzD~m5w~YfVZ^UG6jP~U>LI>$Tv*!X zdfqK<%Pu9Kzf;B))ZcQEvrzOl%QHg+}^PsB2Xh7#!Zxw?Yh4ium zP}i4aBtQz`U&|2kI^q{OvPp&dm;vpl4U?GHcnpm2J`}##h5x8*#xCgz>i;-0{qiJX zY`5V>JT95a>?e2GukLEwwQJul;Q#uDtEKzl=i$alHc(P+q8w$Jvq^FAK~#xn7z*wL zNg!oio0#%%xk{oCA6pJA3gvc^Hid(Gr%&Sa##T>2)rrr|49U) zs2!vSB2StjBzYu98XlN4JOs6r03*O$?@rDa1PYs2vkQ93IGnZcanM?N|6T2D;45XJ_F%a-YrWUQa^itL;D`Cr{ zoN9Sn&gc&{`vVs?%SZ=eNglra_u(@eVTX$Q0YytlXF~)v~WU6qG}y?VNZSwYzUx8NVr@c4I$-ujVuG?%maN0D?O+f$a{n)EZT`Q~#gUH$OY z@9&&1ZH)Ttt2&*aJKRDyTO#7FlilFE`<#;o)8#syDHqr1a=!Yw! zZ9JRC1q2zQA$#9}^uFR^$ z05i@fm50dg!af7Qz3YeGLk}okW3*uu{(}Dsjpr)l?)+%Obq${Y9yVZ|#?nN9!bl1S z!2n1BwCTV)iYJ7%3VakIG(i_>u;)>U9-xC0g-`~r;?6SZu`2_PhmNp!iN^qbj?Q?5 z{IihT)8QnDAD~z9Tt`44B%dYI6ZCO3+wm}NuM9sAm#i)LholIb@z0AG#LX0lNXLFJ z0u{F+pb+Pi+1|8~PN+uwq;h{7lfxb#uR;C`@$5QO5U#%~v8>$RE{Bi2e8X91-Eh_l z-)KiV(Vt0t!av5Yo;Q|TALxy!%9-)8j(j6au`B3n^nCr-af~bwTRdTz?MJ9&SPGTB zYB23>YR~*`@gMSvLd{nf5XN*L#FN~-t1an!gt$PU+vBZbCx=>^-f?C{ERy`Y`Ei$ zKjsI>EJ&9wyyCN;<#UhX{{IMTIqDM6=7IN8T@rUUwKvoUE@oTM$gDH|ilc;Nxh+kOSUK|aqR-gda$0vhfo9It(HIcptyaSF>~C&hH3 z@Yz>hMDBCs^p_4yVWE7M+>k`X7u-VeTC5i|{`B4q{AMNrmFk;?^E`2INJ)=>m^Fge zqmLwnot#by8b}hEkcfl^9X2!yPK(Ff=IrT50>@IQP+GH~+A1v3Jou}rQ05NSfF^fp zu5>|2tZVdwHOohakigW_zlVB&w!et62zsOBW6f(6aqs3_&QNvHF9qv;8}C<)N07}w zs%J-a$&DYxY6E^6Y#GV$6#e7&;NIkQn}>SFzU+W>30EgP$fMv0KkzvR#yvxuuS?$R zwDicoaAn0qqN}7duECvRv1mM@$pJQsRFTy|jljbPoHd{geu~R)6+Q+0?BG~CYNzor zezJY+pwpfy`<;ocU8S|7tV6Fz+uFydHSh2dUpz%XJM!zT6HIBY==M)W*Ot0CPcOzk z0gYEX?1!f|porlN3;^jRJMYZF&p_Kj`Y&=>B+;@?i2h_)rvAKIevw4_C^f1lsG1uBFpXfsXwM%Vf;b71=h z4_KWPkJ9r~bblzc1rLI${C7>;qN)y#pD3)c-f$}8B^H5r`m9frH%SbJU0s{o`)s_xl>c6ITV)e|sK7d=+tl1QaAAjiNgVs>EO zC)hwFhT@<2hw1;XE>!~oQx`ajs%p#7Ht&j_QQ;7JEg^ zwAx~kwzd`hzGO6p$d9%*D{8g10sq{#>wqq|tCHHYbuhyDBjSfT<6~YD3Tc|_uq+_u90^;ctNgtxMA!kjLuR!8eLR5N8-HMkH+ zbd=JlaQfv|;A)u_x&1A+yJIw8f;Dmn714RbjQ)^D;y1#x@`b-HwpUhK^cC&|LYI@vA&Bt1|3~d4_8n!JT{n`H_@i;!B7- zNyZF)arakIQSo!bk~H{f3*{9Ts_McKYsRDeCBPT6N&|IB;bC~KG$IRKD|DBA;5`NZ zj~FA0GvZU03ROCXLtx+`O4N?gSLpz9-i){HYa7QP^zOsXKK28ovEh3dtK>Kx;6G6C zgY>&^-$8nUcJK)0*>dqihqT-Sm{UCus_C_!BJW*UqVg{DPX`6Ud=;(6Fi2Fmy3;j#8zyHL_~-(l$H z0QvRUMEM!;81A}9-|=Ie$v}VD;Ji14FR38KG4WwkIw4XWp^ssK*^QUex9nHjP-kka z{Y8B7FurxG)Jq?*Up?Hm?;w457ag+2*aMDDKu)IoVJ50a5MR@RksXI;)Omzs;>|>( zN<*ajAgxpdfL8jQPjjoww2{2hVe2)NCGGk+un4W!O_6b+t`m=JV zGgSVWeO4;?;Dfz>u(3v~X49f(0*vDwGCRLe`S+kxnHo*{G(sCF&_ThL!W?+eL#3j$ zAD>n=){!N|0L^|{yy>gQq*o?rb))nm;ZR(=&+~g!szj>3B-Z znB~;@K|G~iANPli3mx+NIR(^rdDN4Q@0dk@YdL`PdRBfcCP((;gbHyV zxPxa1*uS%ww6CQyBbQ9uuLDEbW&1%8`lqKk7QQ}x30lm)8xkqSDWAjlv|Jw6YDq>42J86LsJlY$2^ zU<>d`Q0M2dItQ%VG5GW``o`@dH$10;>=$U6-*a34{{8)kx_OSS(Wj4}~Spn&00jvGf zz!i4QblqqHeE?3%7wwLfkLTv6S=l4 zN;lQM)?p?UvBSbFWTj#Bm$|n0_*_&!@{>srvJaG|;P+gR0?yaQ^{Bqi#CotFWc3_lTH@XCzgvd6jp)Q!l`#S1hw@)yR@8bbJ$|BH zt(!G2Zd5xv$K08_v$ejGG}gpx$n`roTsNbloLe5i5(@T9xWB|JoDK_0NV{MTG5$1~ zWW*zSO}w>ZCMJzwe8!{;?7#g<7pdL($EhaTjw#qQ2NqZ7B&Of=bUn=WKVLUL*`Lmh z#K)Bjpz-9g09V15s8TIf8(^o*DL!d93!rTP3CY;d!9<|4fpM7QZ>iu6E%;4{+~fvU zA8}A89$e)z^XwWyW&|^D@1C|leH1fqpM4_C{2xx#0P5HA;B|6AJ#AGfn4RkxBB6oM z0I!ci&`}Aw1^F$_7-xGc=k#x%zMb>_M_#Y7{O)+}oJ)Ri)~Xq8s*?*^#KH^V4LmLE zR4tf{A(S)%ezsP)co5>U0V$qgup-eFYeSY{&;(g66_JOa1cwl^JR`J6c*}CXK0@DP z^YM2%Ts(OC=zC%04{=e#5jY@Va9Lw3L=H##_&+sy!+xfY2+u8V06s#8@do(f5gsLc zI&!*gZa5ctPqw4#JooM65gb5H9>T$eS6H&)=7&uE2;u-H^C%9Ibjj0iwgl1S_;#F- zv>=^~Jh__IomQg()Qg9a6#lLOHJa|F*r$Mri+Xn>E?P1!hUI|a*P@NRCN6%vCSm+H z#eQTzc}#hNG59pX1u>E|{gL5_5=g`hM1nLbNhB^zHqxbN!VH9mql!NnH8efds3xTw z>jV&9;;h`$mfRQm7QgGIcI0=R4M?pj3eeyud& zYYEyf7@BVaRG63@5GJgZ^n*7MO(2`8n>%+7n`w(!18LDO&9Ir01CCRt*)f?uNhVW~ z2Oh}?MXZs?^WFm7C0U=lv2kZ1uFMn`e-2{=#t_-zXP{~WDpQRh=bZE*RCk4T>uvF~ zr%>^}J+>~>Oy~AA4f&+E^^w)$P>R*=quZyNbL+<5_VzdNho_1eg~@B@S5NT%pVM$Q zVq1O;9a)M-A7-IM6hTOQxGQYfbv9&Qf4b0(ih2K?oJf2pnPi^iecaX}9b&Az@SS1; zSCZdJOeC3=+{Mi{fWapl7D-n_SJes}JCk4l5J2HXzA(bw1oM;sQISQ$EbbJeW71Z= zVqp2G3z#Ku8Q>wTwPY5FMN9B?V1Ke$dcUTYT6L-2EUF)j6ii9?ru893RZl_XyuQ*> zvvBaXNEj|IJMP)hn(}K}%fmOE73?sY)l|p4RJ&>QNtM;wI%LnV7B;JQ7LnXi4u{l8 zA)&S*p)=~4Hl;!l>CW}jw@vkT`83vCUbADzn#BXi?JSR;JT-dOl9ncN$>?oD7-|6q zn1xpNvLzh8_cpvAF!*~8gA;~8q8+q>^|H3siKPV1=5ASv2C7yPn&EJ9bWwkp25|iI6ug>4 zz|V4uh^s$@xy{L&OW9q-f$PR7{1$KbYV-X(f;OY#`NwA-O7L&M-xI>eg!JeX>qhzG znqg_9j3Cfx*DP9mY`LK=BJ5Y(`qLF;W+hCjDFym-a;~n2c9saQEA;C{ks$j7rGUz$(6z!~^-h}tzJ#CtH zZr%Q*_Jn|f-Q+q^)ZW5hM{`iG){rBQRr2g4o#>@}P02G9^Tg1)f~n{rh$coF%&b#9 zE>6-qM4NiRR6#_349<1;KGj+DkuMmL5)?AQmwRcFGA(J^hw{BQ>tTI`eASUf)e z2wg&&|1I35`t0h}C!M?YTTA#A;d0HiPf~7@{fsgwqyE4;;sB(Vkl@IENYOr&i@D~5 zSbSl;g3}V!B=~#o%sqxgCP;vCC(oYK%1>R!eZ4>YNlK2gAAy&6A9xIcor1bKQ;7N9 z3wucfTGc_|l;a=gXD!@;Ckny9v)5RJ&^9(d9M@=-)EYb6IinM(ZPlMi z*7Df=)yd3u`_aA%ax+x=e4T^KmrOCU-8gCANm`|>ri*Dc!@f`U+|JO(3p>eJJHF9g zSUPW%sydIJR~jw%to5{1T3R|L{GLF-1N(s7@FHrMEaUz%l=}m=9P(-eMMb+%j}$0e zrVT;{oU@g2R>?4Aw&d7M%OcmwurJ0lr~s0Qhf?7$fPA(AvD(IsCWLA<8Mwggr_Ht$ z@hYh{^JxTcjKVW%^cQ`L;bC;@;=aN?rQU}djZzNA3XFG;E?$*Tx-w+CIdT_mGhIsb1cNQ^Jwa`+2B~~;d zSDl(mDM3rorDUK{>1qrIRMrwTS*ShV=?m)At9PhZoAU|vxkN!i0^fEYICabZ1sQ>! z=Ls^|pOpQ68Huc$Cx4klTD+i@^U2L|6#(gnO9s61R=Azc-Q zbeR1LgTvBBxK{F76*m>h+0(1s1ZPjLQoIQp_Yzwjk)FWuQeJ+-Uc;R^9j{FG2=1b5 z{4pObQ5d6|-MRZc#J^zFc=ZmqE5a3k`_azsM4q7&uC4i)@h|dXav+WR(Zd^6GkO!! zGx(IkjWhbOD(V`Yj~)NwW0%%}p*b%cQ;z}x`NQmkZ{9}Oax#3ja19YAv!gqyz}oNX z)(7hM*_Ei{I8Id)%4I69z)q#;D%(q2_Ez0Kazu{P4Y&q+&lz&zoG#s5_m#RY-cR)|s8PJkDg%n+OCPZ1;@NiV()N$8|wGJ5=I>0V@UY&iT89{wpJM>BjM(+2ZnmzX1u ziJu4f4fd4v6^pCZSq5 zLAsrH@@bw2c0cc_H(%n5^?tGP@D*)8-|`doE!^qqAe`;0Z{hE)w=EKMNQ$q_9l__u zx=|fvSBP?D`gc25U;pwP&X_antHjPwzD)b4?|x=xVN81$kKyzPW95yj#YmJ7KCSsKyH=EunPoCvp7=MK;i^vE0!+k zl<6s$2q+NAePS14D|Q!p%D#A^H5~XH35qw2tfZg*QB_7!OlO_un4ILuJ%!*VSZ@w|S=gb)y$_erXWeGy9~@p%gW&ND51az!cxmjB9BIGhUI zioI}(b#dAHfY+n@>_~@4&&T~<`xN>Rm)FIVdhheyp^h}gUEfky+CKh#fAB>j0<^JT z<9NOA_nz-Wqex${5j;*Bx;4@Vtppxa*(%IvB8~9OcTIgNsSGjUfetO2bJoo>lh7dw za0@~l0t8e^h+2184Y7oTJKx`F-$c#EvHbv3Y}#j?ai9I){OrwKfLE_-lYBmK?8B1Z zcbjB+)sVED58chla5Rb`KWth2cUQWABN1aUbwi&_F_kE~=Jo!$?DJ}ja9U~jaKqoA zPU=0FujCVcGCU=~zY~j3Lg6rUUL2!&%w}Q$!JcHS(#yb4^fW#I)6o#BhRg_ztVD+U zBW0a+B2{opQ`=x~d#PD6tVDBw2ZqCt(cXg^K4&F79w1?3tD-cqQ~q4>Boa#g@gg$6 zTgp8~N9WR0zUeih1MMySs$vF_>`3zPfba;Dx?8n7@bZtNN1=$Hafh!8=(-R+ z#cDKTtV#Dlt1|#Wu?0i{G{=veg&}jwNCN29x_8iA*wIaznX8XC-e2US{aY3ggNQ<-F6U@^S0t zh=47j2H&4Gcim3&k!V{V7H{1pl1+TdXHV0IFNz5W#34MSoz(D!1^1$_#s2}O-u%^eL z^$!h3xb0FTEs_zl!&d-}l$VRxD;f1xSw~Pk!l>k6K$mZgEWYlJpx2*BW`~zZ(MCBE zYi*8)71>+)r*+}*?gP7h%|Xv3JCA6@y^Rr^OCl89MzMHJ*Dt!*fWg3E7c9IF(MGo= z{r<@%5RD^xTPl-{7up*m74`wceq~*_KSZz6HP!w+sk|9p)K67jhz^L{%TI7Qov2@h z>XGocY=;(Lj+3g2Gx>EMc@*kXJ@4|`aS@Wn`4x(D{s$hE(dGNcs2_zxBiOuHJr|AE z&TgQD)*p*m`i&ISaicEX#u>FcxK7pnzd~!fi=X=v-MR5E{V1W!b(U1~MGdHypwF~tG8g2L-Z^=0J@JG0&I65KXmmJuT)yM@;z3hgz%qidPnW2Gti-U(W zQv@vcsJ0glAGb-~quMhD{$?G#;xBHvqTz;yKjmXbqHP^y#6H%6InP&tiJ9^L$35)j zFxYMMcFOxgb6IB)g_a&RzcbN|<;-xE$yoDL!k zkf*_=lZ%g9k*n0Po>Ho=mb9?WR?0)aTaUp#n8##51msEcKJ2D zLWM~51Isa4B7qVls1K020k#%iETAq)Q6-|zxZRQKxlS3kt6-8&lPAfLGU?b|vi5qR z;(jnoW)}Oe+KgR_nMkNjyP8lS)>L+}saa3l$sf2=-2cty6>K$bfc0il*lDtSOtSp_ zY+b~Dma58nPd}ZPuVtO-W8S=C6Z(He!^K!f?uYMKFZlKipsAFeECzcLIK38hx@9;5 zu@w2?k@*Oth#n&78wzv|9^^{a6Uaw}O!WK)U>)7SHSGKbaQYSYe62n9vylkv^xQmG z;{hZ9xw2T?2dY983aF+T|WkV0VPASuW4>(2kY*shbZnqW@l(?h`-te zKe`uy3uCZLUjZy8!!&nwI~p4h*X-KyA;TaUn%i}54igW;`GS&>b4b*q1}sug^l%ZV zW<%qNhNM+|WRHb0_Jiw3)?i&2D34oFKJHX_{!jTc$bTqFH8^bJShFo_+HbR*@P} zc$G|XlW34fnIvmo)_%r4iJ2fyeU^1bX3mG}H?iGeFQyWnn1pj7p2cgjcY0X|)~@y9 zyotx@EPK$~xQpXg-Tb_KMKk(AxN?Q`WllGS;mw0c`1-y`C8{Lc#BBxnE+BcoL2I#(2`2 zFKdh$9?LY;FS3B&(lrH6Q_sv(48!!9ru4(fdwwnjgXMH8AW8lNTf8tG4EDF`KAkj&<{(M*f}WF3@pbPov(2g$Np}e z(qTQnzy84)zT1?WL-c`k;KZj6Mcig0_@!#fqQDpDyik`+F7Ic%$=S)?(ZBp4@e9Zx z;iPSfk~1DR%}55_a+o$uX`>sWhv{0-dzjx&r}%n!5bNQOIp5U>A4byY&ful$y$Rr4 zHe-DNlK{2vjN8c@1LTn$K!{YkQZ@)|jHs{cWWjwh;CbQtOFS!XKiba!TO!1c^9Fa* zAde0ZFV}s7Xwxe0?|TToD-rk=A+J;zv=qu^nx&Llu@XTdMb%9rEaY=atXNZKkSiD& z8UCq5-wf}*rpWDiHJzOQC3*1K2 ziV$HsJhlhee|G3!P^@HJ8T^}y-fomGZc;EgmF?Kye7F4{*@etpxm!9Awd34qv1d;- zviVG}WyHO3rH%ZW-DCfHJyX9&^GwCc^aObA`>{@q!gg^{!zCDXlP-fReWmeOZ$PE_q`NB{x>A_)oaX6C+;lE4<_P1T~M-Z;! zL*U9sWXnVaNkhrqkX7)M-?aX|t4lfn?=Ehxy9!kv)L2sY76Tp~Jdd44n(GBn1wt9P z)zJLq@}fnpZGIg!AI-^lEZ)|ZfL6BLx&pEP1Ae~_8{`j6-DenR=-Ra&R01~t>|-Az z7?KT$e9X5&=89mgKOI%SKZNr&`DqwYPK0R#l*>EErwtV|N2N{t6QMrj`v;X{O`F8`V_LTDoSUQo;MK8pD*-iOko~l&a+P_ zsDlhEAWhNrV!}d&h@+YE5h`T{@ddt#l4_127tQhCT>rgWVL}S1e6H3>Pj$Oh`a^zJ zR6lbL`ZEX3sV7OZ0prpiK~P>g7R>=mfIA?$cZ%LS;&JwX zhTe=AC8R861GN0YSFtKQR43FTq`blmLX)dk7w{V0Bt!6Q;DS$v1Sb87s;Zi*GXAAP z8=okxBbJP!)w=zbOa1B^axg62%p@5|s@kWBkzGs^9p|PAo7;e z=s(VX9(zpiu%a)|qAw+iRKQyBt}O)><>eT_9$|)8X(~7O!T>}a=HAIbXO7$KvN;?7 zYWt!Uc%36D3H>?%f3X)at!G$mq$@O6ZW_b6#)Lsn@mF>-vSA3MMXBpK(~sC_cMjGN;xHPqwQ2#1*a^BGz$Or#5hH z-%M}9O;wLWTI(Iz>OHlu%&lv}`HlNezI*#V@$T*W?a}RXbH_gc&yI!$!PSvoTITaX z=peykpdLNM6yfc6-6I!q@NhCj2hyK&SlEQ*_+!K?P>w=cXDEJ%DxIE+Skjr2mlNnf zG9}OKUG<-y<)b>ML*Q^fo^kc(12h;y%&bb3&v5F1!6Q-uQduRNKNk`w+UY*Q6`_2X z*#%XR(-kf6^scRuOvlyrx2`OTw>lE4>6(8`@O0Zz|KUrhzcdLO%f$_U(eQl3TTEq0 zJxJ9Bpp&A>$1SO#JCs$3Q{6b|jYw83)Acg=nZBVx#Ncs41O<5$v4(XTEjUE!1JM4= z$^B4{TZe2B+d~`-w0L!7u9wSQJO+yhT`S6qQ87Zt+^eIZ#~>8E?Nv%aAzzS#xTc=E zfrp$&Ayv~SDj+CaWW#gwyBAW+Mh@HKRCST7RpQ-x~fP_S9CLAnLdrQY^s^d zH0p(T#PXmP@CI-Z<)pAl0|JgplDP#=PMH)dKtG(=7j&IepT?iRPA`kR^D)1BOMM|kgXJNODw%amb~F9;@4#Sp&%2>tBs~6(V?WGSkwK% zd7cCubE2w@QY{{z3W2l~@@TpQ_6O{smGDLzsYFjCn96h^MoFNTMyTZxBU~7ACB&-^ehO0Cwy@RcaCh2H+C>j9q z1g{;4b!VGmvK&s#OGJGB9KPT|eRo-R5GONk;d-07u84< z=qYp)`Z&353h|Aq0fhke!C!u~;Ue%92e?=0T11a;YXIXJA+AstVK!KOhOp{T)G1Gm zFf<4p8X8vNRK*?DOBCu@87K=z5_k)j8w5N%>oS~_j&VP3@iV-rGW$)BCUb>QtdxUL z9}ff~p@6TD3I)ULutEVrFmQ|C2O6^`!k6`scf}6N(0y9apMvcoh@3_$|TrN zW}arrL+gE-devZgBUE<|aJ{_Y9O+Ba*THY*!EIiMi2R#`jsX+>n9j5+hdYmMItJJ){W&Ry zow3`)KX`4L}^^A4frB0r7M9+u|u9riDUvu*tm``1*a5RMkJ zje%g?Phj^qB}0Kw)*JO1x>q+0)uXE?Z8Lzls;YZ1iM+8$Y@k^-OiT5rtRL0C8cr6~ zxRU2H%lS4-c^W+Jom-Whfrtnnm)72jH1@66( z{AIHUzR{sXVtap23jFB3M%vs=D&=Lj8>xu=2cyW5FANXgdg4^_mj|)%Y&KG1bLkf? zDM$`V62|SIbggtP^tZ@f#=xcPCF1{RxvYR=2R=bwad&8Ou3cvr^k*j+3Dt`l)1G7& znPS(D{mS{RyCh{kws~+VNyUqL`^N*gY3ypgZLd2|JoQfSZa)U^7DHPW!E=h_scMd; zB9zCxJp;$G1W{O|X3 zUD27D^f0@i5O@%alGqZXX0x*lWbL@sFm5%I5#ucOJ8w7&t2Vj5wzUZjlJY@RVdMQ- z#ru=szEkMWv07fQ(Gm(cFj-i<=IA`e?ZlcsRPAl8-{dnmM+fX@=IDWhEL^p&b6nYu zSj0yWi&%E_4#Wu`i(vyVDX0<2#GL&UOdIDrDW^kaSh|x&Wuo!4C2LOU87Oj&B3UXe zSlHj&>G9e7=qgiIUYOG5Xi5)HjxHMP!i5huE?){OO)^~UKBcz|O+Az!j!@co@P@`fD*M<|rW8zB2M=1QpkzM9kL+hp zgwGOv{3QDL0xY)F$9L&SFK3uB9FFp#Rdy)xET9mer1MU+BmYz1{VFi-MtXf+`pohA zp@R`8e$jrjzVB5?d@1Qm<*1JoE42~)UVt($&sJ&?&OtySD|9Uw7AN3V46;VivC=4) z(a8CaM78zMv2<6yQ<*1ov8&j8Yja)>KUR~$*ju>mnJr@{om6?BM@V_koIJ0)yS&80 z5%{D*#47w4@sDw620q@f8}ioy@JV1~9o;ZeShzyd5MuO?D<<`k;-@+we?fG0wH!+9iXlVhV`H+>+!t8AP;%4&OaVbft!I8 zlS*g3DcvV~)Qo<<361^Lre|35N?tu|gnYic3|R^U4VlE4)*jS%0yCS5m-3N>B^!Z& zti~cyFrJDTh||Lbp9y)*K8HtHV1kQaPZ5EEeJL0AMrqJj#k|Mkvvd z2z&iGJ)jvz&RlA-o_o3fJ==kUZEDCmN^dEcsPowpwmN$g*52HE*N+}6L8O08QXH-P} z8F1KzdV5(Ba(nd(6$h~IqO9+tCP2sob7m;yvHyp*yTt}vu6tfuR=ay_4TqfY$a7qR z0K=K>I;vG_-PfIJ(B$Kex`8uVGFAKL%e4DAZDxG^qagkGb3e?-Z`QXf4kX-MDlPm= z92$sGrmu>9q~jJvW~g)QFX&SO0QJD&wHKT=E_nBE&mN8cm;FB8x9i(+Y?#+~e+JZN z_xuF#P$gS4W#)xA&I_4~oQzb+(>WU&oYj(#3-}6eN3osJicNJx)Lg~W`e76ES=0(T z#A8T18X8C!OZ=|;iR728yK*3$Kgg2>eHgd&Ot61zjI|W!$&viT(tJFT|9Czk&nvb-BZs^W+ZsBhN2LEG zdu9vOIfd^E-%xrDti(JB5l%yB3Te^UeMXZr!h7pKy&4MJLvvJ`zrsrUZ(8+ox~%$*o|c%eXP_k<_bilN^cg99 zAXkt!R6ezOeQbf=ky&0|>Nh>@t50oSln?R#3V+3opi8_?0=L)Wv!q5ON+~i{(EM}u zL9L`iUW2lj7~Vwa$e~c8b|m$N5>hWR@c_;!su@ZoLiGm(Um;H)qPj$s@r+cS+~z=l zb&{Wv=22)HH127HRA~L|NZyN^voW8?DtPkIsQvxuXe{rUagGheMy0>WdkU7v7t3lo z^X74r{ddu5-8oM_HX4O78Tk#DHY}B1l3wCI15~Bql!i^vqQ;Be9MMrMo+ZY}5XuN|cu85VbD62l29r_E4kA^Z^thvw6H=IvEJB!8MR6#mZ_3?`P~;^eD}5kVYvBtO?jzoc;N&9#zF32 ztdr|cdW9Cnsynz?E+MsI%92a4CpRL=f@H`=D{Yy`uvWA(uT($z6)gOHRFF>Y^GBB8 zlj6IPj9Idx*qCg^Hj08yLUA~y(6_xX*P*84sj^k6DrqE<4MZq#et?gP+KJ+~)(#V` z+9*CHKHK0$1lOqab?FWGM`Tgiqr!94pV@E$bYFi0&TfB0Lo+YY3R8kezgZbZYBY|? z0(npDzztF_i|}GF4VVP21Jxs>1gbj{*DzV}9gfJv#d>IqdwQAmrn_S^j48*dE9WW3xoQkH_(!xMp-@{O;?)8;nA*k-2VvblM)`J0wP zzR>dSn@JBT&&~~oIwGM^xIGk}YYz%i71s^jui*}bKk}t*0k4eDc_@VsJ}7&vlqqi` zdar_i`+)QduD{IitYbo}FoW!QfKBoalTeU!Ht&oc`Bn0x00O58t*c9Y} zmM&r}{n|9rSg~fBt^wNoRBkmu(831&YI^uDcyspft@KgcJN?>(*Hre%rq5@}dlYk< zR6q?1LsIG2(4}<&%R((xtTb?p)p#EOAc(ZB z3UawBAjSh(iGV+-I`aoPuAF+d@EweUF?A50i*2pa@6&PV=0dO3SHz~*ehdHzga%7` zIj;7rDy4XmWZ4^0K2ta3esA<6ReJP00XV-TJ$D?yvCgi7y0p~W z)ub&g_OVsdJAsd_@NkrZaMx)L!$lL}-|P}CpiIB^bs6~*0OC6}*)U`?q%zd`ZL4M; zLaHT}KJgJCJSSZFI>w+8+S#+

!3;7J~OYIM7fTa;$2S(Bz4w0(DHeeyN&7iqeZg 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 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/Fonts/sfizz-fluentui-system-r20.ttf b/plugins/editor/resources/Fonts/sfizz-fluentui-system-r20.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dc27711e76daa93784cc68d5bf621bfbd5d258ec GIT binary patch literal 275084 zcmdqK3wTt;`9J>7%-M6!Ub2_$ZZ;wJY_1SU$Q4Mq7!bKBQ7#5W1c`!*N)W|>Lc;*FjI^Ora;l=Yt6u)}bMboF*qVG1;e}E{s|AJ{9 zvyG);yD!6dk1!s>^=U0#+Mi9w6W?yv4oL+}AUnE2ABI>i4cx(ta^i041%s}%$ z$*T&GLVvu!VSV!cINw!)PPa+w61t2|8R6u|m22w$XK;$qB;pj%$r4wga}A+BI>k^0 zVi~#6FI!e4Wwvt{^Qnw}i&S#Cbv}Iw9*I)xImfrt{9Uy7tnPg{st4!~-t|D}KxpAW z9n?zg2(5G(jU}5drNR2CUB7FmMaa>P(1GVxJhkiJFXes2CIE6=Bj;8_0v50pWM}7}RtIO-87uXLawm^za7R??Y|-MK9cWR$7DyZx>;2wsh6UZ#4DNIXIWI(wxJY zV_uciQ-|)WGk4sElkUtO!-i9DswA%y!vL=(&jr7AX0x3pA;xhobvkm5(`(DmTakJO z-9@e9GI1H=+*&*OUXEWO0>e~!^4VEGUSA#^r*f~HIUJX~a~i+lAJ@UXSc4yr>Uv#r zjczm5_wTwlqQqFN;&Hl1N8oQP&C)5nm&Vd*h&%IDi04)sjq7Cee<@l%4p4QcZvS$+ zgp4(uoIm5V+iF~Y89RSI_uV{-`$RPJkB+^kHxO+Ky6%#Yw6H) z$bI9VUMav}haQ#l-hq+SESyLuCd&=Q8qL98UdtI>Ut1+U+z{*VaNc)DN-S^&9S8J5o02eman|6mT(xM(7do`$5R-)K`wy z3ia;;T%Ea`gL)YDxSln-&aHGNeq(fvDL8gUP^0IJ)0}5s&s3aSIMRD(1U$!!HT*CX zGPc;Ne~U2V4EdeUKxyI-UK=wJuHOcgl3= z8RnU7LEyHzb6JrVfKfhC@ME;mJAxrMQq z?^ZgCF2|biy%YFk7V5kfzpd1u>rd6o?ebf$p#}FAl;NLK_Ymw0rpBFEF^0ufy)q0( z&K~Am4g~U=aSe=RoUt&q9ELQWSq@`R=2ALW-<>C(e@^8&Iay!12Cne|`cj;M73Gm~ z+0>nTJxZr^&^Ylc0d5R`Dj9Md-o$qon8 z*xj&u)$ZTz-naY1J!kE?c+aXm_wIRc&xSou?>Y2d?tA&~6}?ySUZ3}7y!Ykav%p8v zF4{G8*MwbX?7C*x4ZCjI-GBGJyMMp?!`&b4Y2PykB|G;#yyvMsKi`u;$-?)F-|O{W z^?T>NcZf@Zp;HACcMJW%UqMkpVKP=+5Klc7bc^GyK#!fj4Y<7`GbD4Vq*{zV#wh0n zc)sPX%v9WUl53O6d3f9sV|9^97I-f=dB{sX@YR0shd~NaI)y1hQOckgWl|RPplr-j zF6B`^W~q>hC{D#xLZ#ra%CYx*f{s^$ldJ-#(HEGrmg=Y;Fw}@X_6PN8qGs&bLD+jk zfbm;^b?4DF^g~)gKLRbh0g!kT-Au~>KX(FR?*`9x4=_G+diT?h=_j-r`(+KSr3dLD z?C?kEQDCz5^f*01KLeJ2lAfZc!O#4ho≤iJqh9X*0b*FVai2g94d8*x*C@ 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

*s8C`=nV?{Hy&CG6PLX7;-?6 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%0A5t z_CA>^B%fCNb%SxtF~*ZPi}*b(hGBcbm%deMHTlIb3?i?dUf$3$?{6)o=MHFTSf(?z z2V#KC-Lp%yRkqyH({oEvNGsDeoXOSIPWDqA%FyeF$VmiG^s3)101dan^B>U;T~OH( z!_cu9UaMFRv^vRKU#97e+7uEqSY1kfyRP=6T;{{gvZ?m{jp~9Wsnn7SbkhfFn`HBc zGr5y$>&`m#(BU`FuC1N@XUzWUvpsOv5eTrvzFBip@UfXQ$3pRA7>0W4a^yYJAYs74 zH|u(P>+0!^`ucre;|r#rScrb&9@B?*nEc1ok5XSz?M3~hzt|gz=lW`|lSEZ_-P?iU z2qO{QGU_sidc9tljSc-lbuArpd5SuvZU>XC=>nR4+>*;;E_H#$ubJ-fTJBWye~tCg ze0Hws0ydcbbZ$1khwxq`4y}Af)PZ-{M2Y-f(XL8e&41_^vD!uD(=gOEivydGszA&# zopp!l4w7s^;z-hV>1XX^NrpX=9`jkdy*;yb&vOWEYq!O%hWDeMQ1XnXj(qR5Go*S# ztgowGwW=0T60xl%)=%0o8X=y#&`SzZ;2RCeY)i~`5UZuNsa)+AjmF{KA=*YJPAJZr zRXhQX1W&>pK-{!*7hGDNG|o9~?PYnv#K`cne2LyF<(CZ`j{*GXiyMv=_?W*+=aPeM z>|*E%*Fdw(p#P8enUC{ZuYlHoki`J&LIe2k4FDt+bMgjI z*h|xDNn<66pySl)Z_JZCts4dgOp6;dc+--%*w`dm@JKloD?#6Op{t@}Xi)G#C zE1wn_@t%9@^O<5(?z$;kvV8UP+hpx+R^vH`9ns#o==V!{?q0gxeIIsPx%5)nY30&O zA#msP_D7cfWa%Y!DQpFFOyyHcMeW$qdsDD07H^ue{mLEq>1`dQbB}0Hi{tyU6E-mN zTkJ4;D|ALo#*7Y4y1-Nm)l2u*6tCG;Te~aU(sF|^9vT($Od+qG-KN5^*_gC8q)MsR zr|NRw&eeTrst?G9DUK_+ClnJ&zSrM@4@W=NH?cEJ)mACEWyHE`Etc>}gN7ggppyNjAjZ-_(M~5K(ESX#~(B@i5iEWp6R2Ll@>(?3GX{plfJ{>YxB%gfAWwri`5_9MuTE}=k_l+d zqxS?pdYg-n95~`Chu1vxXyaH8%CaZr9kf=$#{j zrD-D*xgjyV#S%TplX{sgKHNPkB_C=?$JXHq+GnVxob63l=9O_#yQ7h-_g?W`gY^?5 z(^_GkD|S^L5nHo)8$H?u`37^H@jeT)(CQYzCUIhX?_RbSU3^6}^QFz1N|?vR_K4EU z?-;Tk8tpMXi0`6GXzTD2ZJZ4Mn!rbUHMg|XiT$ab=W9GYUwZ_u{79Ys6WucG$29e3 ziUZ8{2@yue!em2jZmijA&5e!Mwd%rnT_!(iZuX<42J8>8_o`bvP!ZGGsAjrnZP@n? z-*2`|Sl!St(OEk%(a>Cr4aS-qCI)IdsXec)`9Au`m9W!{!BS(g{1jjVL=|8}!ptC< z)WVWTT?I7+3dUk3*qCW(d+Y-K53~g5JeS4V-iFwZ6btGi`&|9BY4RwuH5Xpzvv0KR zJ+}SABcGLvu1n{xtuvceRUV|exi*)+E`$BlCi4wQgj;zKN@6!;I+6oOJK0t7KR z?S?(w7|E)x7c|irfQ|^*$Se%qZk{!colu9!iiW8^2HPTxHRJ-ETr*iBXi6W~1infa zdYSGXX&vlnIOt7_o7-X=%pyE3iY+v4SKKXr(z0sZ2eViZ%+BpaeiH;G+w1+_<7duO zC*|7Oawo~@eeHL(_2oN0b@<_8RCXfb2L8Uar6%mY9b1DI!MwHz zW};@<2GiyYN7c>lot!3fElmRLgkm{GrnyNsiuX;-7)v!YJlBvPG5$YCp*oGEHJviAwsMjtdzc7?s^YE@}9DXkM!~QmzYA@F|m2#gls&3^; zq%p#7*ja1x&bEFrZB^yFW_pn_kBjLsj^2jfe;lcw$zf6go#nnh%cJ1Gf5s0PqA=KQzQ4u3cy?wz%}eCw4#;{z8TMj}xM{<5Vz6K!YZcuwI!n5=xOo%WOnJOw zR!j|`12aFTD~o?I*dNdHt2VH~b@7xO`FZ6x3Xn_8>frig%;d$ zbH#IdTMAR=wU19VT29?a-SpL@bABAUUT0#_W#-I1L)S(wP+0Io;7_YFpF*Nc|3!)* z8Zvsm2@I*}a=Ld2CX`W01oV!KEk76zhd8bxmFv0r=JsqJ%0de2!jGXdq-J%)0=sx7R;BQ* z^h_Ep>l(x%aZs?pwm}K=PuWqcl5Z;HBxfGk4lbVNVQsSZk%#aYzc3^qQ z%L$25i9UcI5cWTx2lBdMfSIPB zW-U-^Zn z%YBU^)x5;3JcotM3^u;0vntPF|9I@`)m$s;<0YK0%y(IVpcqst!y`) zkC4@H8H}^BPXqbgFNYu1W_VK7u;mT67wz&s;7;vvM zV&zivv?uNj2ScoXt5W-bGtia6ZB!Yn+|g23q!_eXEQC*QNwxNLm5}HQ$&lPHtUPrE z4zTz<(mvq20~Dd=on$1=`Jlvh5sEpc^-!RIaoO-dcbGZ{gw>hrrx7kzKg^hn z+8}QyR9%{?k6hd78_1fHN*lX#;J`;>_t>&QOLlwRi^eK#J(grnmA)l|S>v4uAuqRn5pKU95*JoNgqLJF{TA3M~H`HZ!%|7tJ;|`oW zZ1^BIlN*Uf@GqBfe?2-N#RzIYUnsZrH#HR+#yUq1kp5C!AI0pPW%6UphIR88^cv(j zm?$0+Yupr-Fq6YOs|ShUJaGKh2^qHl8tbX?;7ZXHlB(#Ebk!uoZ%u?zDi*Q)(j(H6 zF1Aw)>P93J>0JJ@gFNbF{%CD_c<0WU#lC{6g}$)Y=BioJp!KkBG>=#D0ba^m4x2Tq2tHUbY{|mCXEGCR!|K0?>EH?dwIOboB+k2)8e{{-=I5viMV}t!> z(Z2vd6KfO8TQ}u6X{*$I;#ND!=`Nm-b-R;KyIygH<*n19%T|5LpenC)4n7p|P!`bl=jZIxAmnYPRxi zCx1zGVPim~_wG*PNzBAn*?9!x(vxbuT-3pf$ZE`pIyl%iUQ8!Yf)`SzE^iL;D0WTh z28ZU!eY!!xzcd1NW%E{6%BFT$;)|)e>5F>D`n#*8QK84C8+8{2`kE)Ddf=%BgGzh} z)GwsaW5zbo_}-0Uq&@Y2bkE3U_4isN+`8ghHAHb>dtb#)WEi&pk^6zx`pWm?uHpX4 zLTfSS*Jo1QeFtT-h2|nM+4PpuncN|59mwVUDs?;?ed)_;xn8!E7whcYjv_S5zW$S!KQ^3Gqhv`2@b{Ds8L28Tg0Xn)LMy_&$`HyE=`MUgNf1Q6+R(nI%AM| z*=O<~-O`#V4LZ~9mDyrT-G+rLHdQX^OTq4`@R+L?pTKjIm8-=QqUs(=pszfQfyYWO zT|Vz%|KJ6)X?MVszv8Hci~Pxn{eCjJ-_OKlO-&gE_1zgP%^wqD!BLyuU&iKI$hC>m z@ziO~21aR|)3JJ-x-C3ZwreZjs0V8^GkdbNy_TwPw1%>|!J%|!>wZ5uI@7zK&v4Hq zyc1Snz3pM_NrqGz&0Qv)B{crQfFkl}pjQ;IhcF59PZaoJ`-PB8UV(|}jQxc=_HPAOMh;@w`9&n8>3F)=e3Bg|^IY@l%$Qb!5-TDbU zU_zhh6cf4;u};EXfdjMpx^}?!`%2)bovz{qb{Zq=u+qj-EPnb+< z>EO7&EIEF#c?$=qCd`^%v6}Yb?Vr~<{#P@ffSl*u*Q`G^{F6J2lOc40luKXRu1}RMq5WGJ4^Dm5r^{S=&B$quOW{ z8Xnz^`J>s?LsJyM?in+>OPTu0ox}YHG{02guu^?3=`<9M&Supp$1rzWx1d_7)q~C8 zfvS7K>{$z>m>gQ-AozOj@bFwjrn{nE70ZjQ+6XHcM>+PiG)!pqa2YRlXWMk=YS(y)r}uD?;SLy1`{K|Uh0)1bA?j-w7SY>GYzv* zQ6tFT89!06n40a_M;FHUc{ciT>f1Ok!w_oZ$i3Ptc_5OZitRc4nxARaip(JU%&p}9 z>>#x53(n&$=JA#($MHKGmckr>&T#Mi${Jku+LvE{;;U+|D9J2tj!8DT4;(hxWur?> zI=;b|%(Ze;=2~$siXnKr9f3HQvuiG{d2h{)|M=Da|CxNnnZoy1iSOInTPB92{})|i zl0am?w}d;)|NGq{>@<7lV^i<%+5flh>p#$C{%da8tE)Tz%x(AwR3YT_J`BE(H0HoU zh>U6E8iwf=xmr-VH*|`~QD#D*l30;4@;_jrMhUjzl7ab|nPrhfH(LbpE;?G;91S)Z zIO}@oZz-;X4A0{7Cbc?+%@$Ipp0d6!4^xU-dM<63b?(x-tir|w>rXkg9+`d{)aoWx z{pyKg10~i=9eNRcb^H7jrPn)h3xBg-$%jdTd3&#bMhyWF3BfNZ?3^XZSLIe@fM`k` zLtkB%rqg=u-qXU!6z9db0`>DA^rEk!8lvS(^@{d&awvw1xIgRq{tWBrcrnPp%TR6| z3d13xUv5h8ELOG|Gx;kU(jR=85q;&UE0@}t%46b3;bGxR?dFBfS1_6{d#S;E89**-{hXzJE1KOkiz)+Qd3Q|I)#Tx?>XN&V4{)j2)o8i_f4 z7)P|i%)9Kq730_SH6KUI-j#NsyKh9c*Wkd3QDJ7dEkOmR-$I0xB-y+?y z_?x~h&Wi`{E)$45Y*wky2}kSe6x@mdzl6V)#^xU52OM(L`ZeKb1Cs1vYiZ=Nh0~-| z+4>RpkfYbki-r8O>g$1UV=Ois$A`*oxZlI6luY4|9FA}KFx_{tB!>@hfKP|1RnP5_PSi` zF?vpXdT%ksA)pegQaF*{agnKKlCp6E-%d`5vMbY?L%c6j3o@4v!pmzR-dP7fBl4Cp z6zsbsZ?Vk`%S+9+Q&z9ahOwFL4K8tgporx>x|zLwKSO)9&ZFvh;TC#)lNM2mOkvTQ z)SI=;eC6d#upumFLgl=j^h0`h3;bYCp%=)aDK1;FY14{gA+DA}p-?2d)|$D+Lgh(W z{zo8qf%5I*-!k(Ey`I+SD331f?{@@rxtallA5hpLYKRTlnEuR{gFJ&b+B)To#)2o$ ze+H*0@e_hp>8jBoFls^DeU){*)0dJ>E+a=)Ag``C2`}n1a_OOXA(zhDtD4!C7oNfeP(w1AGVW!3tL|`fVD)$lb_{Rq%iy_t+_Jz2tRdVeN&2kT4`ACJziv zqx0vFR_4*8xW6(Fj$eIk;(mIn94*#UTnxn^-e^*Vi1qRweE9XV6(*80@oAtPKS>h)QS>{s5}G%Yx2R0EOq_>6cd3Uff9M%i<<9Q*z7{AK zDXTeKOl39lRyTDIq#H9jJ#%1sOGmc0DT4$N)OjIRys5owW)^{;;&NoLki!e-q$^v- z4pMj)e?(-vY`LPo`7roUB4L_tOVwY|GO4reS$ze5URs1@k;@UTiJ;~Sh9P61v$0vH zTIw4c8k^f2I_itL9CkCZN=S>^sHb58w63e8oG&$WA_PWby9AYQ>d(9%=UnV7rtTm4 zeO&HScq&=CbrMCV+N4a?A@k~?O$}GH$ni`XC8zYFBC^3h%(^IOpG2PvEu-ZKZo#7c6M&#(rgu z>4E7hK3VF_Hj4vgHLq^_XtZf1pdQk02M><;dqCO+HH| z^kZP}El3Xz`DA{qHcaxuD5Iyar@l!FQMI+&rFUO_>Zw{dd^?E}NNNV&+ z)D_tcVxqNPrwawEwsF8~YMpWHDbtHqs!-HTZF4`eU|I*Z)6j=KertQlQcW$X=GH}@ z{p=$2F2yFDDz(kM@~SJv<&b~c)x(GlnU*T)!y&$@ULD?=5!293_9CrHJKV;GN?qAP zCY^0*98~RvTohyk6r- zjK)ERQxja>OLf)?%_0qdE{q6R-GYtKG@kizalv4Lev4AXLqF>lsUajsPgGqDAW*AT z#VK}M3Y8vISgu*gKaWgoXfKPOm)rBr3(8(P-O-iykj=GeZqEtXf!=KPgo5s0Oj_!Z zN0P#LNaGFh!G=@{Gu@m#jV}9WnI5QozrEaLSt#Kn@LtEgdAfUg>bUx@e5U@mX-j%K zyVOS;vxR}Co2MPXh|RnKR}+_~c0so8E{u3`16t;cS~f49Bd({tn2 zk39;tKdfAb55(HaZ?@jL1s~8UQ^PUwXlVW$8gjD_POZWpROB&RZ+vuhLryHIT$d7S zN{3c{b8EDvjC#S^|Jlgr{#RU23D+`)QSdP2%}TBVZKn(I#H2yS!50Z^qBrfN@E*Bi zl6QZxltzz>c8_bJ_tEl`9kW^oVKwm_y`x^^XF-!9a5aPHz2ZnU~HAZ_E@x(fAbk+K9INI z2gvf|WlMu4Sw5gY^K3gJql?`IxlQMbxzw{Ltk7t+&umYN!%=0`_{vp{`g)RZ#?592 zx|_+yfOe|DGtDC@w^f<)0@9r`u~XE+O&AMi(n?zivAC0iBPiKwV%G`i%+w`QM6%J^ zOnZ!bvD=Z~H}!S8n{hX`0SOS*HI_B2yR~Tmc@LIPB0qm`e}UTHdAe?#x~Ya-IvqDb zB)z7t>Iqq|Hr(h-nTMCFv!ZT8x^|vr_2o0vZ@fx>)Y@c*F7Q?5A7`^s%%aw@Z*Meg zx?K!qka=*;V&f;>W%emCIGT+<5Qam$3V}uH*Wtz-6b7Mopu;J_ix;2yaOh3kWGQ6_ zR@Ttt4~q3$R?Qwk+PUNV*VVSCN?Q(VE&X}=s_tIh^{l>cVlCooDlg<^Q+_00^I~1T z)H-*qUW1T3t6|&Z;d|WxDR@JQ@zLEjO;6i#?6Am8Tm= z8<4FJ$&{@0+KI-dwHYLCDrVNuEjD~Ug?l`PeE{3gxAyNdd)pgCg$Q&w>u_ATafme;e_>gA_Rb5Pn@pp<>3~(oT_}dG+O+AaO?NaY1f?`;<4Kns z$y+WwZq)%d#lYO;TzKa(_~NFkM)&;|8a}>{dZBaNVa}0aB!`o8M0CC}8mfIx5*lQz zeV{Smgho?1qijy8siy>~NrViY$iWjCI+JWl%Dtx&#HI<{09fW0iqF6oA1x~PN8$TQ5YLewa7PONpb6Y8 zDV&y$@gMKsObx51q2?2~lK4*t#_IT3VBYYS^j&)6|xF9*UY8u7e z$#Dv<@sH!q2u)chu(N7%EZ|XccycUJ?zPFWg75kr$+51%)+5QWRnsG`PL5MG&ElJ| zG@ntk1-t!i!;1LHu#@e;xIB!ty%9Xy7@_I^EF2#J+rg>$e+EYI%{4WP&e-yXZJSO$ zWk=8OYe#zQF?(!Q&k>tX-F(K`_;4*s-vsZ7bMU(*n2Vf=)sM|a@>-O%9Y-5$PRD0! zjP#nduiJFaIXz2GJ9Fda9X)H`uzknI(|gv~V>S4EBhKkeNH~%9x?Z#KZ}aEH&*2Kuu;W*IA2sw7>?MiKC_t(Mut+~EqX++=tJ_CeldW)eMk(8 z5itW!@Uz4y^n6&x#kjDcfx5^n;EOs-Q5C@8dVv$%Z4iZbmQn5@d z7YB4l94ZbItHf%tMywTwizCF5;wW*nI7S>RjuY#|@nXGrjW|J^C{7Zu6&u7x z@j7v`I7Ms{uNS9^)5Pgwvp7R+5nIJJv0dyCXNt4L*u))8{(VdTjJlux5Y!^VeuXD zUGY8feenbFL-8Z=WAPL5hG~h<_JPir`ZAEAjO1LzQB6vOPs#amfjm$yl#AqId5~Npm&#>w zxja~|kSpaO@=$r0TqRe_HFB*yTpl5hlt&?R+A;E2d7NA)kC*G^Yvc*?M0t{Yt=u3t z%Gb%0@?G-X@=|%3yj)%(-y`3P9h$F{SIPIwtK|pe2jw;L zTKOS)oxEP|k~hd3<%i`*lXuFy zYDyX7rQ6<%?+ElxmraDxony$K3x9U;7*z~ll`qh9MR6}Z5ji?!F zrkbTj)oe9Kjj3^ktX#@fp7K?oLKUgGYC=t_1Jpbo8C47EjVRom2dwL_h$&QfQqo$3wh9CfZbPrXsSNu94QP#3C; z)SJ~?)Wzx&^;Y#Z^>+0R^-lFJ^=@^kx=dZJu2An$?^W+pSE{Sj`_$)Q#%H>LcnVb+h`Yx<%cpKBhjdZd13bPpD6-PpMBM`}k+o9qM!HPIZ^s ztv;{rR$ox}sC(5H)tA(L>dWeW^?-U%eMNm$eNF9AUsvBy-&EgH|E9jJ9#Rjh@2Kyp z@2T&rAE+OyAE_U!pQuOFqw1&XG4(U`xca$zLj6MhQvFK(TKz`-yLwXnR{c)>Uj0G+ zQT>PdPxX}gllm|9-|A`gXZ4Kwi~6g2Rz0Wwrk+Lm0qpa=(YNAeS|(zAEl4h$LM4AaeAFTUa!}$(I@B=^-21*dV}7m zU#Cyjr|3=k_4-tOnm%1`)@SG~daK^1x9c7HOnsIox-=Z(pm*}_Zx9PX*cj$NOcj?a+vhrqu;CFr?1pk>G$ia^#}9^^)>oh z{ULpwzFzOrH|QJnhxJGFP5Ng2QGJWPRewx>T;HZ|*Pqaz)SuFy)}PUz)pzL6={xmZ zdbj?(zFU7m-=puc{lY^yB*H`U(9D{Y(8T{cHUj{qOoo{agJz{d@ff z{YU*D`aktk`cL}5^ndH8^`G@K`Y-yg`dR&){+oVYzo0AnMXLrGY^9};$l9_}R@%x~ zwFu0qvvO9w)nMhVMyttcwhC6!YOzXItJP+;Thpu#tJ9ipby?k3kJW4SS!Ju=8n6bf zA#2zgv1V8^ty$KnHQSnFjalQCZ8?@}d6sVlR%k`mTx%kA@@a3_a>{u8WXDfq@skrj zx$%=1Kl$-f5I=?SQo}I{Z5_wJ{&q?Gti99Ef=Opr+M4pq#a}s$@BF|0axrsbCk>@7z z+(e$6$V2`EQ%`Op&rRgHi99cn=OyyIM4p$(^AdSpBF{_Yd5JtPk>@4y{6wCg$U{`G zDc?`z`H4I~k>@A!{6wCg$nz6nhMTxv9krzdoq&<)E z!8jk-eBkhb%Lg7G_JkFBGS@Jkb9%sqpEP0$IkF#W(CEG07 zX2~{7wpp^xl5Li3vt*kk+br2($qq|)ShB;C9hU5{WQQd?EZJen4oh}fvdfZPmh7@* zmnFL_*=5NtOLkeZ%aT2o?6G8zC3`H{W62&%_E@sVl0BB}v1Fem`z+aK$v#W=S+dWP zeU|LAWS=GbEIDAw0ZR^8a=?-UmK?C;fF%bkIbg{FOAc9b$dW^r9J1t)C5J3IWXT~* z4q0-@k|UNJvE+y)M=UvF$q`GASaQUYBbLkz+GB}idn}QRiGECzJ(fte#}di*SR&aT zOC;N4iDY{$k!+78=WH{BHZy25gElj0GlMoWXfuO0GiWn|HZy25gElj0GlMoWXfuO0 zGiWn|HZy25gElj0GlMoWXfuO0GiWn|HZy25gElj0GlMoWXfuO0GiWn|HZy25gElj0 zGlMoWXfuO0GiWn|HZy25gElj0GlMoWXfuO0GiWn|HZy25gElj0GlMoW2v@13wJ?J= zGl;c#{)pQKGiWn|HZy25gElj0GlMoWh_!}9ATww)gElj0GlMoWXfuO0GiWn|HZy25 zgElj0GlMoWXfuO0GiWn|HZy25gElj0GlMoWXfuO0Gw3jb4m0R5gV;bXzGR0PbeKVh z8FZLIhZ%I3L5CT1m_dgbbeKVh8FZLIhZ%I3L5CT1m_dgbbeKVh8AJ}5bl&}9Z)X3%8@U1rc_23=;*Wd>ay z2VG{+Wd>bl&}9Z)X3%8@U1rc_23=;*Wd>bl&}9Z)X3%8@U1rc_23=;*Wd>bl&}9Z) zX3%8@U1rc_23=;*Wd^ZlSyH{spvw%p%%IB*y3C-<47$u9R?QMQ%%IB*y3C-<3?i(7 zznqMNE;HydgGlL}e1{o?69K=&=gbVkAA~<*$;_b347$vq%M7~Apvw%p%%IB*y3C-< z47$vq%M7~Apvw%p%%IB*y3C-<47$vq%M7~Apvw$;%%H~%dd#5540_C<#|(PRpvMe) z%%H~%dd#5540_C<#|(PRpvMe)%%H~%dd#5540_C<#|(PRpvMe)%%H~%dd#5540_C< z#|(PRpvMe)%%H~%dd#5540_C<#|(PRpvMe)%%H~%dd#5540_C<#|&aoK5ljJZ{dTa zJ$uZc#|(PRpvMe)%%H~%dd#5540_C<#|(PRpvMe)%pelpC#S&-ddwiUhUSm>oS8w7 z$3c%7^q4`98T6Pzj~VoM9Q2q$j~VoM9Q2q$j~VosL5~^qm_d&j^q4`98T6Pzj~Vos zL5~^qm_d&j^q4`98T6Pzj~VosL5~^qm_d&j^qE1Q8T6S!pBeO-L7y4)nL(c!^qE1Q z8T6S! z&}RmHX3%E_;qjDwnHlt%L7y4)nL(c!^qE1Q8T6S!pBeO-L7y4)nL(c!^qE1Q8T5G^ z^qE1Q8T6S!pBeO-K}5MESHTQo{|J7EYl9i|nL(c!^qE1Q8T6S!pBeO-L7&G#pBeO- zL7y4)nL(c!M825he3(I>8ARqm{)o?+8T6S!pBeO-L7y4)nL(c!^qE1Q8T6S!pBeO- zL7y4)nL(c!^qE1Q8T6S!pBeO-L7y4)nL(c!44A=y84Q@gfEf&!!GIYIn8AP<44A=y z84Q@gfEf&!!GIYIn8AP<44A=y84Q@gfEf&!!GIYIn8AP<44A=y84Q@gfEf&!!GIYI zn8AP<44A=y84Q@gfEh#*!nlzH%wWI_2Fzf<3d|=h$gUna)g!lhQPWV3adxeGRCWAj91GTua+@hEn~b| z#(1@i@oE|4)iTDbWsFzLu&ZU*)iUgA8FsY{yIO`_EyJ#sVOPtrt7X{LGMs7|PPGiD zT82|C!>N|xRLgLxWjNI`oN5_PwG6jfhFdMet(M_d%W$h@xYaV;Y8h^|47XZ_TP?$@ zmf=;)@Tz5a)iS(l8D6yvuUdvzEyJsp;Z@78ezgq0T83XO!>^X%SIh9L zW%$)Hf@&E-wTz%zMo=vysFo2_%LuAv1l2NvY8gSbjIdfpNPg%wHR8n=jW@dSL4S1X S40xW`)I55F|Ec8p)Bgt~iK%x0 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/Fonts/sfizz-misc-icons.ttf b/plugins/editor/resources/Fonts/sfizz-misc-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6f40464ecab6e35fc7a1f22804a4a81cf765be7d GIT binary patch literal 4472 zcmds5U2I%O6`udO|GRg6f7WYzz23EV?ZjEHch|d(6%4dZLLsH0O#+mPVkJ(TCb3Q8 z{v+Kv8*sJ|Lt-Y843;Q3(kiKrNN}0znE?Dk!u<0wKgtQ@*+0K;Q+5 zNABI3d*;l{nRC85=ghrJC?Uir7YHZ&_v|@v?^ho>KS4xY-^qBw;4qi6@G zmY>)l2UrjNcc5KaTRye&;9JlB6uzH{#nAI|Oi z6LERPbX>dm>WjBOc_rgpgc&X_%*i+(+8U4ANV@$J8AmZmyo_yT(v0^3(#$x5SLj6o z+nL2$;B0I?=>^gtuYyaYosG+?{J%)D|LE=`Cj9v(QcJT{NUWP zC$662392u?HqAXpGz(e{MV1wfN#VJa#| zZ`5_Inxjjs-vCBBU~yT%c!N%ocG_v2bO2kL0G?hyU^-nA^W8e2tEqMpscOAjA3WJN zYi;()`CfW+c5=R5qYqL&-f^9!_m{i6vQ!kg{)AM?`$kk$Wilzvmi!={R7z+@ai!MC z#Lo6~G$WZaRVE8z!>t+{tIofy?UH4c zEg^yfuGogt&5CG&2CldxesA6;g1<+c8^QP@y2Xe5KZl~>}-C5$9T&>2P}(wyv}YBDov~uY&UJmxcu?dA+sOnApsAv+9}7Mj z# zj0+Xp?nL{0jy8{ocK+l?tz*{@NRtDyw^M(0w<)bL{-qP({!?}Z~pjMwmq`9eA9jF@laWDMl1htWli1OxzP|E~#Zhb)0&I zARjp1NQ_h_fXR#!EC&%riWN3n^aspwEe*4c9gG$8HszyGQ835h1`P`fa4O>jc2Oe( zcyJlyLJK32IjG=voN_S~F^z*(2M(+YgamMy4@jjD+ug{aYCM7^IR_cdAJLW;qTYaQ zP}@?7-6+(mRD)))Hq0w8$U%CNQ1)bDh%4ai2M*>^A#!=fYB|esrSgR}1nhtU_d`mh zmQpa^p(2VQ7&ckj5!!N~CXQULp|LTIeV17iPjIY*(mi zp^!RmEPabHNBf}<11w;Vffn`oyv7E5W>uxFFFV9EG%VxGUsd zP-h^oLheDmj=5(r;OOyl=PLIeKXbTp&*AkoaPB87SpHFRk}N|Na{tP)M^7$;*pp4! z%$}z)VkJ|n3B@X85#_%vwKZyi>>yo~TLzIm>uZ~P)=wW>skED|%3|d|6#{EvM|X#@ z>whxm5EMNPOOI#jSD$^IMPh)}yuc=xjZ@TXQMC Ttc~PM-nGSFzGT?9|L^b*F+t>L literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/Themes/Dark/theme.xml b/plugins/editor/resources/Themes/Dark/theme.xml new file mode 100644 index 0000000..b5e9152 --- /dev/null +++ b/plugins/editor/resources/Themes/Dark/theme.xml @@ -0,0 +1,40 @@ + + + #121212 + + #1d1d1d + #e3e3e3 + #a2a2a2 + #e9e9e9 + #e3e3e3 + #2d2d2d + #e3e3e3 + #121212 + #e3e3e3 + #ff6000 + #006b0b + #6f6f6f + #ffffff + #ffffff + #ffffff + #006b0b + + + #2d2d2d + #9e9e9e + #a2a2a2 + #e9e9e9 + #9e9e9e + #2d2d2d + #9e9e9e + #121212 + #9e9e9e + #ff6000 + #006b0b + #6f6f6f + #ffffff + #ffffff + #ffffff + #006b0b + + diff --git a/plugins/editor/resources/Themes/Default/theme.xml b/plugins/editor/resources/Themes/Default/theme.xml new file mode 100644 index 0000000..7aac2b8 --- /dev/null +++ b/plugins/editor/resources/Themes/Default/theme.xml @@ -0,0 +1,40 @@ + + + #d3d7cf + + #babdb6 + #000000 + #b2b2b2 + #fd9800 + #ffffff + #2e3436 + #ffffff + #2e3436 + #000000 + #fd9800 + #00aa11 + #303030 + #000000 + #000000 + #ffffff + #006b0b + + + #2e3436 + #ffffff + #b2b2b2 + #fd9800 + #000000 + #babdb6 + #000000 + #9a9a9a + #b2b2b2 + #fd9800 + #00aa11 + #606060 + #ffffff + #ffffff + #ffffff + #006b0b + + diff --git a/plugins/editor/resources/background.png b/plugins/editor/resources/background.png new file mode 100644 index 0000000000000000000000000000000000000000..671268679ba6d859bc9801740dbeb02e1a6defd8 GIT binary patch literal 6312 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 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/background_button_about.png b/plugins/editor/resources/background_button_about.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e4c19bab6b9b8e1523865bb66eded566168256 GIT binary patch literal 6933 zcmX9@bzGC*_uoc0h%`ud3kXO`I$|J*g+6ntqOKnCeSq_alKTh zip4HWuJeQjttccelm67_!-tIiw{a;g_4M^m`F?}f_SP^v0>YQF;;o7Q5juE}h#JHd z@TEs?wTB$I3hnM@0p)+`#G=zcS;oNMCnqUySeNZ|YUv*N9@3kHc9yQ7~?Wou_>n4lm&`>kh&5V+w)>w-n5{`B{zNQV4lT zE{XrH2i?D?G<>@4@IWOX=s```Y>#NHOGinaR7la5n1xN>H9gAAdxm6IHi=VjUpy4h zJ%=(}C9bm#31`8~T!V=mkXaqF>-R#$YvvBEcVZsnp*7 zOB2S6lnTr30n^i?{=4Sh5cwY4j%|+}Ydt>)50L~9>6Rt}s{$f_Xl$ql(gdMFE8*R- zet?B4@UCqL2*h^n-v#z8p8U6t6roU4eTr>TDoRR-=fb23VB!eXw+Yn^@bh!`4+ZH4 zySs$CyYV5sL%sM6p{C~6D0(&!i2V?%cjx}&#lxbA9IKh!&#~uLEOFF9iC;?il1-@D zsk-b)pOKntGfog%^{$hyJqtDO)ANlpzkY zY(vzKPYhZI&K~QiVz8R9G>L~|z)Fi_vwlcrSHv_2dM2~ z;V|EY=1ndwJtWQPu$||u{bX8JEvKaeYr#R8w(z{SM+-vhU}b2;4oW>F0kMNbAom6R zR^E3G;wufwiqR|bE#5e4!co@92pWtRg43QPuh=iVwW={)?Gf4?S0r3heRTD^6ur|; zPk>{?i~K#b=$7s13g>_!e$}iRT}CT0=ZDP5eWaSC7T<1B)y(t4R^^d{+$dfLv)xhU z)hJRG(U}5rnAcRrsA$1p)P7z2wX7SEgo@=wdfG>Tu?t8u(=qa}O~m6D{qcH^{am}x zoWETji)So!?(S{ro9nXq3p{>H@1o;)0~c#RF+XRlv5H=2K$5c=tfL7 zL@QQoH%|S&76^&Ic!nEmKpe~m8Qyps%}q$-QWjb6wj|7ciO3J8JT{i?o;J6jDG?lp zsK^Cd3?s*o3{{C&F@YbcL=Q$31&7?4nZ|E11Zsf)ex#9YRcM(Y#)t%((9stlM`e3{ zuUTqWV`j9@7sh(3%Om{tXH=S+$$ockzc#+k!cs>{59?H_4B7sA`X(wwS0Pf0&_9st zfgmn-c6?{}_cm>bXZlH91LXL}m{u&Z059O3iuoLN{5d1N{u_>h>y~dm`SQXWpTQZ?t_0hWJ;a8MDW#Gl^Tk2S*sT|>q&pUA7FK6IN~v#n>$l2p_y2Y&7Rny# zwJNXC)t=K5TrD+n+3v!T2^=?&Y~JD(2*h8NEdJ$|C)2^FG`u zP4MstV1d*+^Q+`KoWcxDZ*p$~m*|i0(s1N*uPh8U;%Hne@O&Ksnb-3B zDI-OyqHj@VVSv5Yv?$P~w)4p2m5=#_dl#3P)oE$#d9pxZ>#w7q_RaQ zjRy4Vn%q14c7H3k;wO963p;8Clj_iFLJYmbG|+So<6jhbF+YxN!Mq8xT5R)~bHniE zKRMaqx7c!;tFu4N#+5IjdVlRq2(CA^Vd8{&9NCZxru(yXKGM5dB=Hw!3fQX1Q=(H} zZ9^00T$qQGJ;Gt8+%V%M;`BonC;211wZ{Y58-|>f`(b=FGcSlM{EZTr!hGuh^`Fz0 z=h#T7^WOv9j7H?I)NY;5bHx&$lGUQ(yF#qQZ*HCEm0%7(VII2omJF}m2F$eJxv$(;MWMr+2 zi1i&sD-CR2TUPR!J=i7=V0J#^ozhF!La7I%vDOWaY7bjA*}Bd*!5U-Gu6n?b-1$B| zVTnPIUZDx9{9$0BMk{sLX{+K^jt9;TSQ$2Ks-X&@BG z5q7v=)=1oE%4 znc87%vlQ6>h!`&(jK@2rl{Ok;&-GDJ4J0Bzk5;v2{e3rwbK%R;6@ zFd?p*$xB`tbw%z@zOkPD&Si&We^y6u{w@j%S`HMZR} zSJHbCha!17fIlJvTe23*Ko>Q@DX}#*#7+p90=hwG2#|&3nEH>AQtek_vuBT#Jg_*t zr<83o#z)$2p(%?RQXyIsbMwXBzk_&t%+@HrC8+VU(QUQk?ize_@G?OL7>UJ*qnJJ$ zX321<1QNd5fMDRreZq%0Pw3tOal7kWvNTVs`UUo?r?pk_o~0JYTCC_$Y&T5 zl0UUS8gfEg}tMwr&LfoZ*K0?@xBtzf5ih&f0n& za^G|t{FDU?K3(vBPqY+n`^qJmlRtCW%81bUu5qNeU- zz4le;eWivQ*9#`6V{WGNTRhixbX40e49N<6k!>OpD_9+?J$n;6wN8$hDASMb#j_Qa z1+-u@0K$v4-Ck(3VeK(=8R5Z!#oIX_{D2ulDf)OMV*rW840JsEP6mw=^Ik6w%e^|p zsP8mD-dy{4WEnJnJJUcDrDCl@Me~O3G5l zd32Ofh2STtQLD@^7%|%rG-T!u4$F#YGx|R|&%tAE_BY~*F+~N3 zY#j(WIhC$(d+kT<-hZKp-WE=UCt@f+?x2wwqv6L{t9SGCDKlzbI-RiVGG@oX>!&g-?G>t$2_(#+=j_xe#S?vp}6z zMR6Sd-r)R}Ddwv^7T=_img!F84CPFMY`&tk+ z_VC@(*+{;8xjuTzcU_i!Deqm*YOwSb_cGDYs6j&F|m#( zYkf&W>ZIq1*<^C=!#N^ZXOK!m=A-8ZxjVcb@0e_acaxymBGIp9&!QO#4#LuvAXjAs zf8h1p4jL3SQKyOezb-l0q}Jm#d`p_regHk znChWy1RgAsZFJI{h9TQBNnw@RqFGL|MaIZ5Ejkpd4JT12dI0YAX}&;zpDu(=f*}wF znuRR7q>@W?&{!!TN{QRzdg6_XGFz{1LkgLRI?{Y7rVW`^mu0r95mlh5F4rq}YMqEW zEFqvknuoLkuE0+Px{j#?YkYO(!?%HKYe4Dm%PRp<1g^j=gV6kd?V&G&moez)Fg(#R zb~(N)Ly)m4h;32W`<+X2c3HBy$7Lgk8JL%KW4p~iDkUH0s8s|R!V7scm`8xQ zg95?-VDz1(=mVn8xs}q4{xljZhkiNCf#7E?5xb_kE<*~W9L7e$|C#j0Z`ySot~+T! zY$@b8z!4Ypm{LUgjIuW@+xYtW7Keby~JY9k*?>P(vDI&UXWKg`jN z*%Tu`(|!gDF93K9f+ZzrRV(>FgB#R!&>Y?zMzVCh5<+5xvkGxKKz#T!8f_e0QzK7Fy|c9UAD6ko-rV75mxY2|I6 zK&V`!h~@al$BnrSWt!ER+-c^d`(ZhodxO*zlG}ivFPADVz&ww*m}BE172m8nY2>#o zk9mq51>Wu!ljk+J_N#WiPN3$g*+JY|XlOo>*9~ToMU;BDCfYMyT^memlf1tMVLGJitU!GDOl{{imh>Z-i-oUri>%P3k2EK^*% z3XoK)OE`;D|1vw8pw1YWh;VW6w`SUsX|0)0$}jMkabfOzERoLPP-{y&A@y)wrf-+s zM#DCtd0$j1B_Z+u4iCP?Qq@!N)GGT)x!c+$ z{7U?Yd$L#CNduMR47!&a_33t>5LRR>1|b3$pn3IZSJQyDrKXw(Q40UGo%n;XoXP%C zQ)CI{1lNCBI179pN|!bK*;-(u&W)4Rz~(iXX8IG()^^LH^|}p zcMeErcVDmKLX=*hF&AiT+0)|0mQK!=i$ix6Prl1qjy8sFZ}PMtJBp3|b!V$wEY+Am zuwtBUAS<~^tm;F|Mc3yld;E!MImlB6c8>IZ?cXaB0zho+7S~u^;x8PCz|{9rsdc9! z5vx=r>9a6wC&*bj$Y?90H%ChwAwZm6Pg4F*A& z&IoKhtT@@L2o?7;*>nV&_+9cnF&c9j=aEQu?~N(`!{chWS?zNC8ACGTDPMa1WLVNr zxW-SQ=PDo5kvYUY<{Q7ptl|wUMkIA$I$dkC3U@6G_1Bl*wmm7#w3Kq&zwn<%f5(-0 zMQPwE0K3w@v?fezAxceg7l^Zp1-5PucJJQJ^KUYG?-Qz+5&SUYR1!$9CyV!_+cob8LB6%+g)xb%*OQz?;c6?{|8oRrE|wu{=e`BFXtsj zLSBs{_!1U`9&XA^A9*EOrmCnj+7-EF52jGdV0`FQ$FPz z9>TUK;sG)C3;y}VB$PP~6?D%j_x9aYYJm2XsAyoD99)M9QtKVxthm)soB$966BNi) zlrr1f5)H$cZ2>%OW%52Kixhd1vNiP^q)Ug;F zns3SFNhTBhqr62)NNbBaW&AjBLzjCwzU;$|H`|Kj!C&B%z5_`a*l`=0!T`&+RBx>M zl3cfCrqiykwSIvR&hR}J3}@F8sqRhhCMmiuEIJnR`wI+#URmgB-}zw0T}%Qbk+mV$ zSIUu5r?DQ;aM9)&6H?zW9+QFeRD5xH|Dg6uK6bp~`>EaRGOVU2?OF0Oe`w2$vD@`J+X} z;Lq?Xs)Of)A#5?lplwCf=Z5x1-$7T}V0u7#Lhf{n^;+Wn&wzVT#ipvM|{)#<|b_WoSnC>tHC?L)mX1KsBKgbaM++?3*yvg)^j3+ rIx`S;B(~hF!DjPtKqu{sUuYGeT7S)E+ua5J9RWf0VS4pC&QJdbZq!YU literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/background_button_about@2x.png b/plugins/editor/resources/background_button_about@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae71e8ff7233f56e7d6fa86b5acdc55438edb77 GIT binary patch literal 16768 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 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/icon_white.png b/plugins/editor/resources/icon_white.png new file mode 100644 index 0000000000000000000000000000000000000000..d63b6f3df8231c4369ab8befee3137cd43705069 GIT binary patch literal 3412 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 qIA{(BkAZo}Bu&yJP0}QdKl(ouPUnCMx<| + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/plugins/editor/resources/icon_white@2x.png b/plugins/editor/resources/icon_white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e5bc80a228e51e6c09cfa811cef11bd6d437c6b4 GIT binary patch literal 7336 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 literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/knob.knob b/plugins/editor/resources/knob.knob new file mode 100644 index 0000000000000000000000000000000000000000..093bd66cc323bb5dcd998d9a7fcb57004ed076b7 GIT binary patch literal 22266 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 KH}m0l0{;)j%WALy literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/knob48@2x.png b/plugins/editor/resources/knob48@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f064cfc3e5ef86c2b48ba69c07c210ece9b15dca GIT binary patch literal 92347 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 + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/plugins/editor/resources/logo_orange.png b/plugins/editor/resources/logo_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..e765347b81b1f79b0a971b5c5dbbeca7334fd541 GIT binary patch literal 27303 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

+   | Event 1 (size 6)                              | Event 2
+   |       |       |       |       |       |       |       |       |
+   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+   |FRAMES         |TYPE   |SIZE   |DATADATADATAPAD|FRAMES         |...
+   
+*/ +typedef struct { + uint32_t unit; /**< URID of unit of event time stamps. */ + uint32_t pad; /**< Currently unused. */ + /* Contents (a series of events) follow here. */ +} LV2_Atom_Sequence_Body; + +/** An atom:Sequence. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Sequence_Body body; /**< Body. */ +} LV2_Atom_Sequence; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_ATOM_H */ diff --git a/plugins/lv2/vendor/lv2/atom/forge.h b/plugins/lv2/vendor/lv2/atom/forge.h new file mode 100644 index 0000000..5e9bef8 --- /dev/null +++ b/plugins/lv2/vendor/lv2/atom/forge.h @@ -0,0 +1,670 @@ +// Copyright 2008-2016 David Robillard +// SPDX-License-Identifier: ISC + +/** + @file forge.h An API for constructing LV2 atoms. + + This file provides an API for constructing Atoms which makes it relatively + simple to build nested atoms of arbitrary complexity without requiring + dynamic memory allocation. + + The API is based on successively appending the appropriate pieces to build a + complete Atom. The size of containers is automatically updated. Functions + that begin a container return (via their frame argument) a stack frame which + must be popped when the container is finished. + + All output is written to a user-provided buffer or sink function. This + makes it possible to create atoms on the stack, on the heap, in LV2 port + buffers, in a ringbuffer, or elsewhere, all using the same API. + + This entire API is realtime safe if used with a buffer or a realtime safe + sink, except lv2_atom_forge_init() which is only realtime safe if the URI + map function is. + + Note these functions are all static inline, do not take their address. + + This header is non-normative, it is provided for convenience. +*/ + +#ifndef LV2_ATOM_FORGE_H +#define LV2_ATOM_FORGE_H + +/** + @defgroup forge Forge + @ingroup atom + + An API for constructing LV2 atoms. + + @{ +*/ + +#include "lv2/atom/atom.h" +#include "lv2/atom/util.h" +#include "lv2/core/attributes.h" +#include "lv2/urid/urid.h" + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Disable deprecation warnings for Blank and Resource +LV2_DISABLE_DEPRECATION_WARNINGS + +/** Handle for LV2_Atom_Forge_Sink. */ +typedef void* LV2_Atom_Forge_Sink_Handle; + +/** A reference to a chunk of written output. */ +typedef intptr_t LV2_Atom_Forge_Ref; + +/** Sink function for writing output. See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom_Forge_Ref (*LV2_Atom_Forge_Sink)( + LV2_Atom_Forge_Sink_Handle handle, + const void* buf, + uint32_t size); + +/** Function for resolving a reference. See lv2_atom_forge_set_sink(). */ +typedef LV2_Atom* (*LV2_Atom_Forge_Deref_Func)( + LV2_Atom_Forge_Sink_Handle handle, + LV2_Atom_Forge_Ref ref); + +/** A stack frame used for keeping track of nested Atom containers. */ +typedef struct LV2_Atom_Forge_Frame { + struct LV2_Atom_Forge_Frame* parent; + LV2_Atom_Forge_Ref ref; +} LV2_Atom_Forge_Frame; + +/** A "forge" for creating atoms by appending to a buffer. */ +typedef struct { + uint8_t* buf; + uint32_t offset; + uint32_t size; + + LV2_Atom_Forge_Sink sink; + LV2_Atom_Forge_Deref_Func deref; + LV2_Atom_Forge_Sink_Handle handle; + + LV2_Atom_Forge_Frame* stack; + + LV2_URID Blank LV2_DEPRECATED; + LV2_URID Bool; + LV2_URID Chunk; + LV2_URID Double; + LV2_URID Float; + LV2_URID Int; + LV2_URID Long; + LV2_URID Literal; + LV2_URID Object; + LV2_URID Path; + LV2_URID Property; + LV2_URID Resource LV2_DEPRECATED; + LV2_URID Sequence; + LV2_URID String; + LV2_URID Tuple; + LV2_URID URI; + LV2_URID URID; + LV2_URID Vector; +} LV2_Atom_Forge; + +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); + +/** + Initialise `forge`. + + URIs will be mapped using `map` and stored, a reference to `map` itself is + not held. +*/ +static inline void +lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) +{ + lv2_atom_forge_set_buffer(forge, NULL, 0); + forge->Blank = map->map(map->handle, LV2_ATOM__Blank); + forge->Bool = map->map(map->handle, LV2_ATOM__Bool); + forge->Chunk = map->map(map->handle, LV2_ATOM__Chunk); + forge->Double = map->map(map->handle, LV2_ATOM__Double); + forge->Float = map->map(map->handle, LV2_ATOM__Float); + forge->Int = map->map(map->handle, LV2_ATOM__Int); + forge->Long = map->map(map->handle, LV2_ATOM__Long); + forge->Literal = map->map(map->handle, LV2_ATOM__Literal); + forge->Object = map->map(map->handle, LV2_ATOM__Object); + forge->Path = map->map(map->handle, LV2_ATOM__Path); + forge->Property = map->map(map->handle, LV2_ATOM__Property); + forge->Resource = map->map(map->handle, LV2_ATOM__Resource); + forge->Sequence = map->map(map->handle, LV2_ATOM__Sequence); + forge->String = map->map(map->handle, LV2_ATOM__String); + forge->Tuple = map->map(map->handle, LV2_ATOM__Tuple); + forge->URI = map->map(map->handle, LV2_ATOM__URI); + forge->URID = map->map(map->handle, LV2_ATOM__URID); + forge->Vector = map->map(map->handle, LV2_ATOM__Vector); +} + +/** Access the Atom pointed to by a reference. */ +static inline LV2_Atom* +lv2_atom_forge_deref(LV2_Atom_Forge* forge, LV2_Atom_Forge_Ref ref) +{ + return forge->buf ? (LV2_Atom*)ref : forge->deref(forge->handle, ref); +} + +/** + @name Object Stack + @{ +*/ + +/** + Push a stack frame. + This is done automatically by container functions (which take a stack frame + pointer), but may be called by the user to push the top level container when + writing to an existing Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_push(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + LV2_Atom_Forge_Ref ref) +{ + frame->parent = forge->stack; + frame->ref = ref; + + if (ref) { + forge->stack = frame; // Don't push, so walking the stack is always safe + } + + return ref; +} + +/** Pop a stack frame. This must be called when a container is finished. */ +static inline void +lv2_atom_forge_pop(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ + if (frame->ref) { + // If frame has a valid ref, it must be the top of the stack + assert(frame == forge->stack); + forge->stack = frame->parent; + } + // Otherwise, frame was not pushed because of overflow, do nothing +} + +/** Return true iff the top of the stack has the given type. */ +static inline bool +lv2_atom_forge_top_is(LV2_Atom_Forge* forge, uint32_t type) +{ + return forge->stack && forge->stack->ref && + (lv2_atom_forge_deref(forge, forge->stack->ref)->type == type); +} + +/** Return true iff `type` is an atom:Object. */ +static inline bool +lv2_atom_forge_is_object_type(const LV2_Atom_Forge* forge, uint32_t type) +{ + return (type == forge->Object || type == forge->Blank || + type == forge->Resource); +} + +/** Return true iff `type` is an atom:Object with a blank ID. */ +static inline bool +lv2_atom_forge_is_blank(const LV2_Atom_Forge* forge, + uint32_t type, + const LV2_Atom_Object_Body* body) +{ + return (type == forge->Blank || (type == forge->Object && body->id == 0)); +} + +/** + @} + @name Output Configuration + @{ +*/ + +/** Set the output buffer where `forge` will write atoms. */ +static inline void +lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size) +{ + forge->buf = buf; + forge->size = (uint32_t)size; + forge->offset = 0; + forge->deref = NULL; + forge->sink = NULL; + forge->handle = NULL; + forge->stack = NULL; +} + +/** + Set the sink function where `forge` will write output. + + The return value of forge functions is an LV2_Atom_Forge_Ref which is an + integer type safe to use as a pointer but is otherwise opaque. The sink + function must return a ref that can be dereferenced to access as least + sizeof(LV2_Atom) bytes of the written data, so sizes can be updated. For + ringbuffers, this should be possible as long as the size of the buffer is a + multiple of sizeof(LV2_Atom), since atoms are always aligned. + + Note that 0 is an invalid reference, so if you are using a buffer offset be + sure to offset it such that 0 is never a valid reference. You will get + confusing errors otherwise. +*/ +static inline void +lv2_atom_forge_set_sink(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Sink sink, + LV2_Atom_Forge_Deref_Func deref, + LV2_Atom_Forge_Sink_Handle handle) +{ + forge->buf = NULL; + forge->size = forge->offset = 0; + forge->deref = deref; + forge->sink = sink; + forge->handle = handle; + forge->stack = NULL; +} + +/** + @} + @name Low Level Output + @{ +*/ + +/** + Write raw output. This is used internally, but is also useful for writing + atom types not explicitly supported by the forge API. Note the caller is + responsible for ensuring the output is appropriately padded. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ + LV2_Atom_Forge_Ref out = 0; + if (forge->sink) { + out = forge->sink(forge->handle, data, size); + } else { + out = (LV2_Atom_Forge_Ref)forge->buf + forge->offset; + uint8_t* mem = forge->buf + forge->offset; + if (forge->offset + size > forge->size) { + return 0; + } + forge->offset += size; + memcpy(mem, data, size); + } + for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { + lv2_atom_forge_deref(forge, f->ref)->size += size; + } + return out; +} + +/** Pad output accordingly so next write is 64-bit aligned. */ +static inline void +lv2_atom_forge_pad(LV2_Atom_Forge* forge, uint32_t written) +{ + const uint64_t pad = 0; + const uint32_t pad_size = lv2_atom_pad_size(written) - written; + lv2_atom_forge_raw(forge, &pad, pad_size); +} + +/** Write raw output, padding to 64-bits as necessary. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) +{ + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, data, size); + if (out) { + lv2_atom_forge_pad(forge, size); + } + return out; +} + +/** Write a null-terminated string body. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string_body(LV2_Atom_Forge* forge, const char* str, uint32_t len) +{ + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, str, len); + if (out && (out = lv2_atom_forge_raw(forge, "", 1))) { + lv2_atom_forge_pad(forge, len + 1); + } + return out; +} + +/** + @} + @name Atom Output + @{ +*/ + +/** Write an atom:Atom header. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type) +{ + const LV2_Atom a = {size, type}; + return lv2_atom_forge_raw(forge, &a, sizeof(a)); +} + +/** Write a primitive (fixed-size) atom. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_primitive(LV2_Atom_Forge* forge, const LV2_Atom* a) +{ + return ( + lv2_atom_forge_top_is(forge, forge->Vector) + ? lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(a), a->size) + : lv2_atom_forge_write(forge, a, (uint32_t)sizeof(LV2_Atom) + a->size)); +} + +/** Write an atom:Int. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_int(LV2_Atom_Forge* forge, int32_t val) +{ + const LV2_Atom_Int a = {{sizeof(val), forge->Int}, val}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom:Long. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_long(LV2_Atom_Forge* forge, int64_t val) +{ + const LV2_Atom_Long a = {{sizeof(val), forge->Long}, val}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom:Float. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_float(LV2_Atom_Forge* forge, float val) +{ + const LV2_Atom_Float a = {{sizeof(val), forge->Float}, val}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom:Double. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_double(LV2_Atom_Forge* forge, double val) +{ + const LV2_Atom_Double a = {{sizeof(val), forge->Double}, val}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom:Bool. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val) +{ + const LV2_Atom_Bool a = {{sizeof(int32_t), forge->Bool}, val ? 1 : 0}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom:URID. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id) +{ + const LV2_Atom_URID a = {{sizeof(id), forge->URID}, id}; + return lv2_atom_forge_primitive(forge, (const LV2_Atom*)&a); +} + +/** Write an atom compatible with atom:String. Used internally. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_typed_string(LV2_Atom_Forge* forge, + uint32_t type, + const char* str, + uint32_t len) +{ + const LV2_Atom_String a = {{len + 1, type}}; + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); + if (out) { + if (!lv2_atom_forge_string_body(forge, str, len)) { + LV2_Atom* atom = lv2_atom_forge_deref(forge, out); + atom->size = atom->type = 0; + out = 0; + } + } + return out; +} + +/** Write an atom:String. Note that `str` need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_string(LV2_Atom_Forge* forge, const char* str, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->String, str, len); +} + +/** + Write an atom:URI. Note that `uri` need not be NULL terminated. + This does not map the URI, but writes the complete URI string. To write + a mapped URI, use lv2_atom_forge_urid(). +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_uri(LV2_Atom_Forge* forge, const char* uri, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->URI, uri, len); +} + +/** Write an atom:Path. Note that `path` need not be NULL terminated. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_path(LV2_Atom_Forge* forge, const char* path, uint32_t len) +{ + return lv2_atom_forge_typed_string(forge, forge->Path, path, len); +} + +/** Write an atom:Literal. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_literal(LV2_Atom_Forge* forge, + const char* str, + uint32_t len, + uint32_t datatype, + uint32_t lang) +{ + const LV2_Atom_Literal a = { + {(uint32_t)(sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1), + forge->Literal}, + {datatype, lang}}; + LV2_Atom_Forge_Ref out = lv2_atom_forge_raw(forge, &a, sizeof(a)); + if (out) { + if (!lv2_atom_forge_string_body(forge, str, len)) { + LV2_Atom* atom = lv2_atom_forge_deref(forge, out); + atom->size = atom->type = 0; + out = 0; + } + } + return out; +} + +/** Start an atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector_head(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t child_size, + uint32_t child_type) +{ + const LV2_Atom_Vector a = {{sizeof(LV2_Atom_Vector_Body), forge->Vector}, + {child_size, child_type}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** Write a complete atom:Vector. */ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_vector(LV2_Atom_Forge* forge, + uint32_t child_size, + uint32_t child_type, + uint32_t n_elems, + const void* elems) +{ + const LV2_Atom_Vector a = { + {(uint32_t)sizeof(LV2_Atom_Vector_Body) + n_elems * child_size, + forge->Vector}, + {child_size, child_type}}; + LV2_Atom_Forge_Ref out = lv2_atom_forge_write(forge, &a, sizeof(a)); + if (out) { + lv2_atom_forge_write(forge, elems, child_size * n_elems); + } + return out; +} + +/** + Write the header of an atom:Tuple. + + The passed frame will be initialised to represent this tuple. To complete + the tuple, write a sequence of atoms, then pop the frame with + lv2_atom_forge_pop(). + + For example: + @code + // Write tuple (1, 2.0) + LV2_Atom_Forge_Frame frame; + LV2_Atom* tup = (LV2_Atom*)lv2_atom_forge_tuple(forge, &frame); + lv2_atom_forge_int(forge, 1); + lv2_atom_forge_float(forge, 2.0); + lv2_atom_forge_pop(forge, &frame); + @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame) +{ + const LV2_Atom_Tuple a = {{0, forge->Tuple}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + Write the header of an atom:Object. + + The passed frame will be initialised to represent this object. To complete + the object, write a sequence of properties, then pop the frame with + lv2_atom_forge_pop(). + + For example: + @code + LV2_URID eg_Cat = map("http://example.org/Cat"); + LV2_URID eg_name = map("http://example.org/name"); + + // Start object with type eg_Cat and blank ID + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(forge, &frame, 0, eg_Cat); + + // Append property eg:name = "Hobbes" + lv2_atom_forge_key(forge, eg_name); + lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes")); + + // Finish object + lv2_atom_forge_pop(forge, &frame); + @endcode +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_object(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + LV2_URID id, + LV2_URID otype) +{ + const LV2_Atom_Object a = { + {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Object}, {id, otype}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + The same as lv2_atom_forge_object(), but for object:Resource. + + This function is deprecated and should not be used in new code. + Use lv2_atom_forge_object() directly instead. +*/ +LV2_DEPRECATED +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_resource(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + LV2_URID id, + LV2_URID otype) +{ + const LV2_Atom_Object a = { + {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Resource}, {id, otype}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + The same as lv2_atom_forge_object(), but for object:Blank. + + This function is deprecated and should not be used in new code. + Use lv2_atom_forge_object() directly instead. +*/ +LV2_DEPRECATED +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_blank(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t id, + LV2_URID otype) +{ + const LV2_Atom_Object a = { + {(uint32_t)sizeof(LV2_Atom_Object_Body), forge->Blank}, {id, otype}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + Write a property key in an Object, to be followed by the value. + + See lv2_atom_forge_object() documentation for an example. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_key(LV2_Atom_Forge* forge, LV2_URID key) +{ + const LV2_Atom_Property_Body a = {key, 0, {0, 0}}; + return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); +} + +/** + Write the header for a property body in an object, with context. + + If you do not need the context, which is almost certainly the case, + use the simpler lv2_atom_forge_key() instead. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_property_head(LV2_Atom_Forge* forge, + LV2_URID key, + LV2_URID context) +{ + const LV2_Atom_Property_Body a = {key, context, {0, 0}}; + return lv2_atom_forge_write(forge, &a, 2 * (uint32_t)sizeof(uint32_t)); +} + +/** + Write the header for a Sequence. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_sequence_head(LV2_Atom_Forge* forge, + LV2_Atom_Forge_Frame* frame, + uint32_t unit) +{ + const LV2_Atom_Sequence a = { + {(uint32_t)sizeof(LV2_Atom_Sequence_Body), forge->Sequence}, {unit, 0}}; + return lv2_atom_forge_push( + forge, frame, lv2_atom_forge_write(forge, &a, sizeof(a))); +} + +/** + Write the time stamp header of an Event (in a Sequence) in audio frames. + After this, call the appropriate forge method(s) to write the body. Note + the returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) +{ + return lv2_atom_forge_write(forge, &frames, sizeof(frames)); +} + +/** + Write the time stamp header of an Event (in a Sequence) in beats. After + this, call the appropriate forge method(s) to write the body. Note the + returned reference is to an LV2_Event which is NOT an Atom. +*/ +static inline LV2_Atom_Forge_Ref +lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats) +{ + return lv2_atom_forge_write(forge, &beats, sizeof(beats)); +} + +LV2_RESTORE_WARNINGS + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} + @} +*/ + +#endif /* LV2_ATOM_FORGE_H */ diff --git a/plugins/lv2/vendor/lv2/atom/util.h b/plugins/lv2/vendor/lv2/atom/util.h new file mode 100644 index 0000000..f585f82 --- /dev/null +++ b/plugins/lv2/vendor/lv2/atom/util.h @@ -0,0 +1,510 @@ +// Copyright 2008-2015 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_ATOM_UTIL_H +#define LV2_ATOM_UTIL_H + +/** + @file util.h Helper functions for the LV2 Atom extension. + + Note these functions are all static inline, do not take their address. + + This header is non-normative, it is provided for convenience. +*/ + +/** + @defgroup util Utilities + @ingroup atom + + Utilities for working with atoms. + + @{ +*/ + +#include "lv2/atom/atom.h" + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Pad a size to 64 bits. */ +static inline uint32_t +lv2_atom_pad_size(uint32_t size) +{ + return (size + 7U) & (~7U); +} + +/** Return the total size of `atom`, including the header. */ +static inline uint32_t +lv2_atom_total_size(const LV2_Atom* atom) +{ + return (uint32_t)sizeof(LV2_Atom) + atom->size; +} + +/** Return true iff `atom` is null. */ +static inline bool +lv2_atom_is_null(const LV2_Atom* atom) +{ + return !atom || (atom->type == 0 && atom->size == 0); +} + +/** Return true iff `a` is equal to `b`. */ +static inline bool +lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b) +{ + return (a == b) || ((a->type == b->type) && (a->size == b->size) && + !memcmp(a + 1, b + 1, a->size)); +} + +/** + @name Sequence Iterator + @{ +*/ + +/** Get an iterator pointing to the first event in a Sequence body. */ +static inline LV2_Atom_Event* +lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) +{ + return (LV2_Atom_Event*)(body + 1); +} + +/** Get an iterator pointing to the end of a Sequence body. */ +static inline LV2_Atom_Event* +lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size) +{ + return (LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size)); +} + +/** Return true iff `i` has reached the end of `body`. */ +static inline bool +lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, + uint32_t size, + const LV2_Atom_Event* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following `i`. */ +static inline LV2_Atom_Event* +lv2_atom_sequence_next(const LV2_Atom_Event* i) +{ + return (LV2_Atom_Event*)((const uint8_t*)i + sizeof(LV2_Atom_Event) + + lv2_atom_pad_size(i->body.size)); +} + +/** + A macro for iterating over all events in a Sequence. + @param seq The sequence to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), for + example: + + @code + LV2_ATOM_SEQUENCE_FOREACH(sequence, ev) { + // Do something with ev (an LV2_Atom_Event*) here... + } + @endcode +*/ +#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ + for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(&(seq)->body); \ + !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ +#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom_Event * iter = lv2_atom_sequence_begin(body); \ + !lv2_atom_sequence_is_end(body, size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** + @} + @name Sequence Utilities + @{ +*/ + +/** + Clear all events from `sequence`. + + This simply resets the size field, the other fields are left untouched. +*/ +static inline void +lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) +{ + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); +} + +/** + Append an event at the end of `sequence`. + + @param seq Sequence to append to. + @param capacity Total capacity of the sequence atom + (as set by the host for sequence output ports). + @param event Event to write. + + @return A pointer to the newly written event in `seq`, + or NULL on failure (insufficient space). +*/ +static inline LV2_Atom_Event* +lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + if (capacity - seq->atom.size < total_size) { + return NULL; + } + + LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); + memcpy(e, event, total_size); + + seq->atom.size += lv2_atom_pad_size(total_size); + + return e; +} + +/** + @} + @name Tuple Iterator + @{ +*/ + +/** Get an iterator pointing to the first element in `tup`. */ +static inline LV2_Atom* +lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) +{ + return (LV2_Atom*)(LV2_ATOM_BODY(tup)); +} + +/** Return true iff `i` has reached the end of `body`. */ +static inline bool +lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following `i`. */ +static inline LV2_Atom* +lv2_atom_tuple_next(const LV2_Atom* i) +{ + return (LV2_Atom*)((const uint8_t*)i + sizeof(LV2_Atom) + + lv2_atom_pad_size(i->size)); +} + +/** + A macro for iterating over all properties of a Tuple. + @param tuple The tuple to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), for + example: + + @code + LV2_ATOM_TUPLE_FOREACH(tuple, elem) { + // Do something with elem (an LV2_Atom*) here... + } + @endcode +*/ +#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ + for (LV2_Atom * iter = lv2_atom_tuple_begin(tuple); \ + !lv2_atom_tuple_is_end( \ + LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ +#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom * iter = (LV2_Atom*)(body); \ + !lv2_atom_tuple_is_end(body, size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** + @} + @name Object Iterator + @{ +*/ + +/** Return a pointer to the first property in `body`. */ +static inline LV2_Atom_Property_Body* +lv2_atom_object_begin(const LV2_Atom_Object_Body* body) +{ + return (LV2_Atom_Property_Body*)(body + 1); +} + +/** Return true iff `i` has reached the end of `obj`. */ +static inline bool +lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, + uint32_t size, + const LV2_Atom_Property_Body* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the property following `i`. */ +static inline LV2_Atom_Property_Body* +lv2_atom_object_next(const LV2_Atom_Property_Body* i) +{ + const LV2_Atom* const value = + (const LV2_Atom*)((const uint8_t*)i + 2 * sizeof(uint32_t)); + return (LV2_Atom_Property_Body*)((const uint8_t*)i + + lv2_atom_pad_size( + (uint32_t)sizeof(LV2_Atom_Property_Body) + + value->size)); +} + +/** + A macro for iterating over all properties of an Object. + @param obj The object to iterate over + @param iter The name of the iterator + + This macro is used similarly to a for loop (which it expands to), for + example: + + @code + LV2_ATOM_OBJECT_FOREACH(object, i) { + // Do something with i (an LV2_Atom_Property_Body*) here... + } + @endcode +*/ +#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ + for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(&(obj)->body); \ + !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ + (iter) = lv2_atom_object_next(iter)) + +/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ +#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ + for (LV2_Atom_Property_Body * iter = lv2_atom_object_begin(body); \ + !lv2_atom_object_is_end(body, size, (iter)); \ + (iter) = lv2_atom_object_next(iter)) + +/** + @} + @name Object Query + @{ +*/ + +/** A single entry in an Object query. */ +typedef struct { + uint32_t key; /**< Key to query (input set by user) */ + const LV2_Atom** value; /**< Found value (output set by query function) */ +} LV2_Atom_Object_Query; + +/** Sentinel for lv2_atom_object_query(). */ +static const LV2_Atom_Object_Query LV2_ATOM_OBJECT_QUERY_END = {0, NULL}; + +/** + Get an object's values for various keys. + + The value pointer of each item in `query` will be set to the location of + the corresponding value in `object`. Every value pointer in `query` MUST + be initialised to NULL. This function reads `object` in a single linear + sweep. By allocating `query` on the stack, objects can be "queried" + quickly without allocating any memory. This function is realtime safe. + + This function can only do "flat" queries, it is not smart enough to match + variables in nested objects. + + For example: + @code + const LV2_Atom* name = NULL; + const LV2_Atom* age = NULL; + LV2_Atom_Object_Query q[] = { + { urids.eg_name, &name }, + { urids.eg_age, &age }, + LV2_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + // name and age are now set to the appropriate values in obj, or NULL. + @endcode +*/ +static inline int +lv2_atom_object_query(const LV2_Atom_Object* object, + LV2_Atom_Object_Query* query) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of query keys so we can short-circuit when done */ + for (LV2_Atom_Object_Query* q = query; q->key; ++q) { + ++n_queries; + } + + LV2_ATOM_OBJECT_FOREACH (object, prop) { + for (LV2_Atom_Object_Query* q = query; q->key; ++q) { + if (q->key == prop->key && !*q->value) { + *q->value = &prop->value; + if (++matches == n_queries) { + return matches; + } + break; + } + } + } + return matches; +} + +/** + Body only version of lv2_atom_object_get(). +*/ +static inline int +lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, body); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**)) { + va_end(args); + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_BODY_FOREACH (body, size, prop) { + va_start(args, body); + for (int i = 0; i < n_queries; ++i) { + uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + if (qkey == prop->key && !*qval) { + *qval = &prop->value; + if (++matches == n_queries) { + va_end(args); + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +/** + Variable argument version of lv2_atom_object_query(). + + This is nicer-looking in code, but a bit more error-prone since it is not + type safe and the argument list must be terminated. + + The arguments should be a series of uint32_t key and const LV2_Atom** value + pairs, terminated by a zero key. The value pointers MUST be initialized to + NULL. For example: + + @code + const LV2_Atom* name = NULL; + const LV2_Atom* age = NULL; + lv2_atom_object_get(obj, + uris.name_key, &name, + uris.age_key, &age, + 0); + @endcode +*/ +static inline int +lv2_atom_object_get(const LV2_Atom_Object* object, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, object); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**)) { + va_end(args); + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_FOREACH (object, prop) { + va_start(args, object); + for (int i = 0; i < n_queries; ++i) { + uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + if (qkey == prop->key && !*qval) { + *qval = &prop->value; + if (++matches == n_queries) { + va_end(args); + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +/** + Variable argument version of lv2_atom_object_query() with types. + + This is like lv2_atom_object_get(), but each entry has an additional + parameter to specify the required type. Only atoms with a matching type + will be selected. + + The arguments should be a series of uint32_t key, const LV2_Atom**, uint32_t + type triples, terminated by a zero key. The value pointers MUST be + initialized to NULL. For example: + + @code + const LV2_Atom_String* name = NULL; + const LV2_Atom_Int* age = NULL; + lv2_atom_object_get(obj, + uris.name_key, &name, uris.atom_String, + uris.age_key, &age, uris.atom_Int + 0); + @endcode +*/ +static inline int +lv2_atom_object_get_typed(const LV2_Atom_Object* object, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, object); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**) || !va_arg(args, uint32_t)) { + va_end(args); + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_FOREACH (object, prop) { + va_start(args, object); + for (int i = 0; i < n_queries; ++i) { + const uint32_t qkey = va_arg(args, uint32_t); + const LV2_Atom** qval = va_arg(args, const LV2_Atom**); + const uint32_t qtype = va_arg(args, uint32_t); + if (!*qval && qkey == prop->key && qtype == prop->value.type) { + *qval = &prop->value; + if (++matches == n_queries) { + va_end(args); + return matches; + } + break; + } + } + va_end(args); + } + return matches; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} + @} +*/ + +#endif /* LV2_ATOM_UTIL_H */ diff --git a/plugins/lv2/vendor/lv2/buf-size/buf-size.h b/plugins/lv2/vendor/lv2/buf-size/buf-size.h new file mode 100644 index 0000000..17be7bc --- /dev/null +++ b/plugins/lv2/vendor/lv2/buf-size/buf-size.h @@ -0,0 +1,38 @@ +// Copyright 2007-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_BUF_SIZE_H +#define LV2_BUF_SIZE_H + +/** + @defgroup buf-size Buffer Size + @ingroup lv2 + + Access to, and restrictions on, buffer sizes. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" ///< http://lv2plug.in/ns/ext/buf-size +#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" ///< http://lv2plug.in/ns/ext/buf-size# + +#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#boundedBlockLength +#define LV2_BUF_SIZE__coarseBlockLength LV2_BUF_SIZE_PREFIX "coarseBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#coarseBlockLength +#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#fixedBlockLength +#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#maxBlockLength +#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#minBlockLength +#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" ///< http://lv2plug.in/ns/ext/buf-size#nominalBlockLength +#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" ///< http://lv2plug.in/ns/ext/buf-size#powerOf2BlockLength +#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" ///< http://lv2plug.in/ns/ext/buf-size#sequenceSize + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_BUF_SIZE_H */ diff --git a/plugins/lv2/vendor/lv2/core/attributes.h b/plugins/lv2/vendor/lv2/core/attributes.h new file mode 100644 index 0000000..ab46931 --- /dev/null +++ b/plugins/lv2/vendor/lv2/core/attributes.h @@ -0,0 +1,46 @@ +// Copyright 2018 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_CORE_ATTRIBUTES_H +#define LV2_CORE_ATTRIBUTES_H + +/** + @defgroup attributes Attributes + @ingroup lv2 + + Macros for source code attributes. + + @{ +*/ + +#if defined(__GNUC__) && __GNUC__ > 3 +# define LV2_DEPRECATED __attribute__((__deprecated__)) +#else +# define LV2_DEPRECATED +#endif + +#if defined(__clang__) +# define LV2_DISABLE_DEPRECATION_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif defined(__GNUC__) && __GNUC__ > 4 +# define LV2_DISABLE_DEPRECATION_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#else +# define LV2_DISABLE_DEPRECATION_WARNINGS +#endif + +#if defined(__clang__) +# define LV2_RESTORE_WARNINGS _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) && __GNUC__ > 4 +# define LV2_RESTORE_WARNINGS _Pragma("GCC diagnostic pop") +#else +# define LV2_RESTORE_WARNINGS +#endif + +/** + @} +*/ + +#endif /* LV2_CORE_ATTRIBUTES_H */ diff --git a/plugins/lv2/vendor/lv2/core/lv2.h b/plugins/lv2/vendor/lv2/core/lv2.h new file mode 100644 index 0000000..06adea0 --- /dev/null +++ b/plugins/lv2/vendor/lv2/core/lv2.h @@ -0,0 +1,470 @@ +// Copyright 2006-2020 David Robillard +// Copyright 2006-2012 Steve Harris +// Copyright 2000-2002 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld. +// SPDX-License-Identifier: ISC + +#ifndef LV2_H_INCLUDED +#define LV2_H_INCLUDED + +/** + @defgroup lv2 LV2 + + The LV2 specification. + + @{ +*/ + +/** + @defgroup lv2core LV2 Core + + Core LV2 specification. + + See for details. + + @{ +*/ + +#include + +// clang-format off + +#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" ///< http://lv2plug.in/ns/lv2core +#define LV2_CORE_PREFIX LV2_CORE_URI "#" ///< http://lv2plug.in/ns/lv2core# + +#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" ///< http://lv2plug.in/ns/lv2core#AllpassPlugin +#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" ///< http://lv2plug.in/ns/lv2core#AmplifierPlugin +#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" ///< http://lv2plug.in/ns/lv2core#AnalyserPlugin +#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" ///< http://lv2plug.in/ns/lv2core#AudioPort +#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" ///< http://lv2plug.in/ns/lv2core#BandpassPlugin +#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" ///< http://lv2plug.in/ns/lv2core#CVPort +#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" ///< http://lv2plug.in/ns/lv2core#ChorusPlugin +#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" ///< http://lv2plug.in/ns/lv2core#CombPlugin +#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" ///< http://lv2plug.in/ns/lv2core#CompressorPlugin +#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" ///< http://lv2plug.in/ns/lv2core#ConstantPlugin +#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" ///< http://lv2plug.in/ns/lv2core#ControlPort +#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" ///< http://lv2plug.in/ns/lv2core#ConverterPlugin +#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" ///< http://lv2plug.in/ns/lv2core#DelayPlugin +#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" ///< http://lv2plug.in/ns/lv2core#DistortionPlugin +#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" ///< http://lv2plug.in/ns/lv2core#DynamicsPlugin +#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" ///< http://lv2plug.in/ns/lv2core#EQPlugin +#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" ///< http://lv2plug.in/ns/lv2core#EnvelopePlugin +#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" ///< http://lv2plug.in/ns/lv2core#ExpanderPlugin +#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" ///< http://lv2plug.in/ns/lv2core#ExtensionData +#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" ///< http://lv2plug.in/ns/lv2core#Feature +#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" ///< http://lv2plug.in/ns/lv2core#FilterPlugin +#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" ///< http://lv2plug.in/ns/lv2core#FlangerPlugin +#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" ///< http://lv2plug.in/ns/lv2core#FunctionPlugin +#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" ///< http://lv2plug.in/ns/lv2core#GatePlugin +#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" ///< http://lv2plug.in/ns/lv2core#GeneratorPlugin +#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" ///< http://lv2plug.in/ns/lv2core#HighpassPlugin +#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" ///< http://lv2plug.in/ns/lv2core#InputPort +#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" ///< http://lv2plug.in/ns/lv2core#InstrumentPlugin +#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" ///< http://lv2plug.in/ns/lv2core#LimiterPlugin +#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" ///< http://lv2plug.in/ns/lv2core#LowpassPlugin +#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" ///< http://lv2plug.in/ns/lv2core#MixerPlugin +#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" ///< http://lv2plug.in/ns/lv2core#ModulatorPlugin +#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" ///< http://lv2plug.in/ns/lv2core#MultiEQPlugin +#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" ///< http://lv2plug.in/ns/lv2core#OscillatorPlugin +#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" ///< http://lv2plug.in/ns/lv2core#OutputPort +#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" ///< http://lv2plug.in/ns/lv2core#ParaEQPlugin +#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" ///< http://lv2plug.in/ns/lv2core#PhaserPlugin +#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" ///< http://lv2plug.in/ns/lv2core#PitchPlugin +#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" ///< http://lv2plug.in/ns/lv2core#Plugin +#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" ///< http://lv2plug.in/ns/lv2core#PluginBase +#define LV2_CORE__Point LV2_CORE_PREFIX "Point" ///< http://lv2plug.in/ns/lv2core#Point +#define LV2_CORE__Port LV2_CORE_PREFIX "Port" ///< http://lv2plug.in/ns/lv2core#Port +#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" ///< http://lv2plug.in/ns/lv2core#PortProperty +#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" ///< http://lv2plug.in/ns/lv2core#Resource +#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" ///< http://lv2plug.in/ns/lv2core#ReverbPlugin +#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" ///< http://lv2plug.in/ns/lv2core#ScalePoint +#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" ///< http://lv2plug.in/ns/lv2core#SimulatorPlugin +#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" ///< http://lv2plug.in/ns/lv2core#SpatialPlugin +#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" ///< http://lv2plug.in/ns/lv2core#Specification +#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" ///< http://lv2plug.in/ns/lv2core#SpectralPlugin +#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" ///< http://lv2plug.in/ns/lv2core#UtilityPlugin +#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" ///< http://lv2plug.in/ns/lv2core#WaveshaperPlugin +#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" ///< http://lv2plug.in/ns/lv2core#appliesTo +#define LV2_CORE__binary LV2_CORE_PREFIX "binary" ///< http://lv2plug.in/ns/lv2core#binary +#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" ///< http://lv2plug.in/ns/lv2core#connectionOptional +#define LV2_CORE__control LV2_CORE_PREFIX "control" ///< http://lv2plug.in/ns/lv2core#control +#define LV2_CORE__default LV2_CORE_PREFIX "default" ///< http://lv2plug.in/ns/lv2core#default +#define LV2_CORE__designation LV2_CORE_PREFIX "designation" ///< http://lv2plug.in/ns/lv2core#designation +#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" ///< http://lv2plug.in/ns/lv2core#documentation +#define LV2_CORE__enabled LV2_CORE_PREFIX "enabled" ///< http://lv2plug.in/ns/lv2core#enabled +#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" ///< http://lv2plug.in/ns/lv2core#enumeration +#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" ///< http://lv2plug.in/ns/lv2core#extensionData +#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" ///< http://lv2plug.in/ns/lv2core#freeWheeling +#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" ///< http://lv2plug.in/ns/lv2core#hardRTCapable +#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" ///< http://lv2plug.in/ns/lv2core#inPlaceBroken +#define LV2_CORE__index LV2_CORE_PREFIX "index" ///< http://lv2plug.in/ns/lv2core#index +#define LV2_CORE__integer LV2_CORE_PREFIX "integer" ///< http://lv2plug.in/ns/lv2core#integer +#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" ///< http://lv2plug.in/ns/lv2core#isLive +#define LV2_CORE__latency LV2_CORE_PREFIX "latency" ///< http://lv2plug.in/ns/lv2core#latency +#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" ///< http://lv2plug.in/ns/lv2core#maximum +#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" ///< http://lv2plug.in/ns/lv2core#microVersion +#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" ///< http://lv2plug.in/ns/lv2core#minimum +#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" ///< http://lv2plug.in/ns/lv2core#minorVersion +#define LV2_CORE__name LV2_CORE_PREFIX "name" ///< http://lv2plug.in/ns/lv2core#name +#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" ///< http://lv2plug.in/ns/lv2core#optionalFeature +#define LV2_CORE__port LV2_CORE_PREFIX "port" ///< http://lv2plug.in/ns/lv2core#port +#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" ///< http://lv2plug.in/ns/lv2core#portProperty +#define LV2_CORE__project LV2_CORE_PREFIX "project" ///< http://lv2plug.in/ns/lv2core#project +#define LV2_CORE__prototype LV2_CORE_PREFIX "prototype" ///< http://lv2plug.in/ns/lv2core#prototype +#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" ///< http://lv2plug.in/ns/lv2core#reportsLatency +#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" ///< http://lv2plug.in/ns/lv2core#requiredFeature +#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" ///< http://lv2plug.in/ns/lv2core#sampleRate +#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" ///< http://lv2plug.in/ns/lv2core#scalePoint +#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" ///< http://lv2plug.in/ns/lv2core#symbol +#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" ///< http://lv2plug.in/ns/lv2core#toggled + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Plugin Instance Handle. + + This is a handle for one particular instance of a plugin. It is valid to + compare to NULL (or 0 for C++) but otherwise the host MUST NOT attempt to + interpret it. +*/ +typedef void* LV2_Handle; + +/** + Feature. + + Features allow hosts to make additional functionality available to plugins + without requiring modification to the LV2 API. Extensions may define new + features and specify the `URI` and `data` to be used if necessary. + Some features, such as lv2:isLive, do not require the host to pass data. +*/ +typedef struct { + /** + A globally unique, case-sensitive identifier (URI) for this feature. + + This MUST be a valid URI string as defined by RFC 3986. + */ + const char* URI; + + /** + Pointer to arbitrary data. + + The format of this data is defined by the extension which describes the + feature with the given `URI`. + */ + void* data; +} LV2_Feature; + +/** + Plugin Descriptor. + + This structure provides the core functions necessary to instantiate and use + a plugin. +*/ +typedef struct LV2_Descriptor { + /** + A globally unique, case-sensitive identifier for this plugin. + + This MUST be a valid URI string as defined by RFC 3986. All plugins with + the same URI MUST be compatible to some degree, see + http://lv2plug.in/ns/lv2core for details. + */ + const char* URI; + + /** + Instantiate the plugin. + + Note that instance initialisation should generally occur in activate() + rather than here. If a host calls instantiate(), it MUST call cleanup() + at some point in the future. + + @param descriptor Descriptor of the plugin to instantiate. + + @param sample_rate Sample rate, in Hz, for the new plugin instance. + + @param bundle_path Path to the LV2 bundle which contains this plugin + binary. It MUST include the trailing directory separator so that simply + appending a filename will yield the path to that file in the bundle. + + @param features A NULL terminated array of LV2_Feature structs which + represent the features the host supports. Plugins may refuse to + instantiate if required features are not found here. However, hosts MUST + NOT use this as a discovery mechanism: instead, use the RDF data to + determine which features are required and do not attempt to instantiate + unsupported plugins at all. This parameter MUST NOT be NULL, i.e. a host + that supports no features MUST pass a single element array containing + NULL. + + @return A handle for the new plugin instance, or NULL if instantiation + has failed. + */ + LV2_Handle (*instantiate)(const struct LV2_Descriptor* descriptor, + double sample_rate, + const char* bundle_path, + const LV2_Feature* const* features); + + /** + Connect a port on a plugin instance to a memory location. + + Plugin writers should be aware that the host may elect to use the same + buffer for more than one port and even use the same buffer for both + input and output (see lv2:inPlaceBroken in lv2.ttl). + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the connect_port() function; + see lv2core.ttl for details. + + connect_port() MUST be called at least once for each port before run() + is called, unless that port is lv2:connectionOptional. The plugin must + pay careful attention to the block size passed to run() since the block + allocated may only just be large enough to contain the data, and is not + guaranteed to remain constant between run() calls. + + connect_port() may be called more than once for a plugin instance to + allow the host to change the buffers that the plugin is reading or + writing. These calls may be made before or after activate() or + deactivate() calls. + + @param instance Plugin instance containing the port. + + @param port Index of the port to connect. The host MUST NOT try to + connect a port index that is not defined in the plugin's RDF data. If + it does, the plugin's behaviour is undefined (a crash is likely). + + @param data_location Pointer to data of the type defined by the port + type in the plugin's RDF data (for example, an array of float for an + lv2:AudioPort). This pointer must be stored by the plugin instance and + used to read/write data when run() is called. Data present at the time + of the connect_port() call MUST NOT be considered meaningful. + */ + void (*connect_port)(LV2_Handle instance, uint32_t port, void* data_location); + + /** + Initialise a plugin instance and activate it for use. + + This is separated from instantiate() to aid real-time support and so + that hosts can reinitialise a plugin instance by calling deactivate() + and then activate(). In this case the plugin instance MUST reset all + state information dependent on the history of the plugin instance except + for any data locations provided by connect_port(). If there is nothing + for activate() to do then this field may be NULL. + + When present, hosts MUST call this function once before run() is called + for the first time. This call SHOULD be made as close to the run() call + as possible and indicates to real-time plugins that they are now live, + however plugins MUST NOT rely on a prompt call to run() after + activate(). + + The host MUST NOT call activate() again until deactivate() has been + called first. If a host calls activate(), it MUST call deactivate() at + some point in the future. Note that connect_port() may be called before + or after activate(). + */ + void (*activate)(LV2_Handle instance); + + /** + Run a plugin instance for a block. + + Note that if an activate() function exists then it must be called before + run(). If deactivate() is called for a plugin instance then run() may + not be called until activate() has been called again. + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the run() function (see + lv2core.ttl for details). + + As a special case, when `sample_count` is 0, the plugin should update + any output ports that represent a single instant in time (for example, + control ports, but not audio ports). This is particularly useful for + latent plugins, which should update their latency output port so hosts + can pre-roll plugins to compute latency. Plugins MUST NOT crash when + `sample_count` is 0. + + @param instance Instance to be run. + + @param sample_count The block size (in samples) for which the plugin + instance must run. + */ + void (*run)(LV2_Handle instance, uint32_t sample_count); + + /** + Deactivate a plugin instance (counterpart to activate()). + + Hosts MUST deactivate all activated instances after they have been run() + for the last time. This call SHOULD be made as close to the last run() + call as possible and indicates to real-time plugins that they are no + longer live, however plugins MUST NOT rely on prompt deactivation. If + there is nothing for deactivate() to do then this field may be NULL + + Deactivation is not similar to pausing since the plugin instance will be + reinitialised by activate(). However, deactivate() itself MUST NOT fully + reset plugin state. For example, the host may deactivate a plugin, then + store its state (using some extension to do so). + + Hosts MUST NOT call deactivate() unless activate() was previously + called. Note that connect_port() may be called before or after + deactivate(). + */ + void (*deactivate)(LV2_Handle instance); + + /** + Clean up a plugin instance (counterpart to instantiate()). + + Once an instance of a plugin has been finished with it must be deleted + using this function. The instance handle passed ceases to be valid after + this call. + + If activate() was called for a plugin instance then a corresponding call + to deactivate() MUST be made before cleanup() is called. Hosts MUST NOT + call cleanup() unless instantiate() was previously called. + */ + void (*cleanup)(LV2_Handle instance); + + /** + Return additional plugin data defined by some extension. + + A typical use of this facility is to return a struct containing function + pointers to extend the LV2_Descriptor API. + + The actual type and meaning of the returned object MUST be specified + precisely by the extension. This function MUST return NULL for any + unsupported URI. If a plugin does not support any extension data, this + field may be NULL. + + The host is never responsible for freeing the returned value. + */ + const void* (*extension_data)(const char* uri); +} LV2_Descriptor; + +/** + Helper macro needed for LV2_SYMBOL_EXPORT when using C++. +*/ +#ifdef __cplusplus +# define LV2_SYMBOL_EXTERN extern "C" +#else +# define LV2_SYMBOL_EXTERN +#endif + +/** + Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded + by the host as a symbol from the dynamic library. +*/ +#ifdef _WIN32 +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) +#else +# define LV2_SYMBOL_EXPORT \ + LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) +#endif + +/** + Prototype for plugin accessor function. + + Plugins are discovered by hosts using RDF data (not by loading libraries). + See http://lv2plug.in for details on the discovery process, though most + hosts should use an existing library to implement this functionality. + + This is the simple plugin discovery API, suitable for most statically + defined plugins. Advanced plugins that need access to their bundle during + discovery can use lv2_lib_descriptor() instead. Plugin libraries MUST + include a function called "lv2_descriptor" or "lv2_lib_descriptor" with + C-style linkage, but SHOULD provide "lv2_descriptor" wherever possible. + + When it is time to load a plugin (designated by its URI), the host loads the + plugin's library, gets the lv2_descriptor() function from it, and uses this + function to find the LV2_Descriptor for the desired plugin. Plugins are + accessed by index using values from 0 upwards. This function MUST return + NULL for out of range indices, so the host can enumerate plugins by + increasing `index` until NULL is returned. + + Note that `index` has no meaning, hosts MUST NOT depend on it remaining + consistent between loads of the plugin library. +*/ +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index); + +/** + Type of the lv2_descriptor() function in a library (old discovery API). +*/ +typedef const LV2_Descriptor* (*LV2_Descriptor_Function)(uint32_t index); + +/** + Handle for a library descriptor. +*/ +typedef void* LV2_Lib_Handle; + +/** + Descriptor for a plugin library. + + To access a plugin library, the host creates an LV2_Lib_Descriptor via the + lv2_lib_descriptor() function in the shared object. +*/ +typedef struct { + /** + Opaque library data which must be passed as the first parameter to all + the methods of this struct. + */ + LV2_Lib_Handle handle; + + /** + The total size of this struct. This allows for this struct to be + expanded in the future if necessary. This MUST be set by the library to + sizeof(LV2_Lib_Descriptor). The host MUST NOT access any fields of this + struct beyond get_plugin() unless this field indicates they are present. + */ + uint32_t size; + + /** + Destroy this library descriptor and free all related resources. + */ + void (*cleanup)(LV2_Lib_Handle handle); + + /** + Plugin accessor. + + Plugins are accessed by index using values from 0 upwards. Out of range + indices MUST result in this function returning NULL, so the host can + enumerate plugins by increasing `index` until NULL is returned. + */ + const LV2_Descriptor* (*get_plugin)(LV2_Lib_Handle handle, uint32_t index); +} LV2_Lib_Descriptor; + +/** + Prototype for library accessor function. + + This is the more advanced discovery API, which allows plugin libraries to + access their bundles during discovery, which makes it possible for plugins to + be dynamically defined by files in their bundle. This API also has an + explicit cleanup function, removing any need for non-portable shared library + destructors. Simple plugins that do not require these features may use + lv2_descriptor() instead. + + This is the entry point for a plugin library. Hosts load this symbol from + the library and call this function to obtain a library descriptor which can + be used to access all the contained plugins. The returned object must not + be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded + from that library have been destroyed. +*/ +LV2_SYMBOL_EXPORT +const LV2_Lib_Descriptor* +lv2_lib_descriptor(const char* bundle_path, const LV2_Feature* const* features); + +/** + Type of the lv2_lib_descriptor() function in an LV2 library. +*/ +typedef const LV2_Lib_Descriptor* (*LV2_Lib_Descriptor_Function)( + const char* bundle_path, + const LV2_Feature* const* features); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} + @} +*/ + +#endif /* LV2_H_INCLUDED */ diff --git a/plugins/lv2/vendor/lv2/core/lv2_util.h b/plugins/lv2/vendor/lv2/core/lv2_util.h new file mode 100644 index 0000000..8cd2872 --- /dev/null +++ b/plugins/lv2/vendor/lv2/core/lv2_util.h @@ -0,0 +1,90 @@ +// Copyright 2016 David Robillard +// SPDX-License-Identifier: ISC + +/** + @defgroup util Utilities + @ingroup lv2core + @{ +*/ + +#include "lv2/core/lv2.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return the data for a feature in a features array. + + If the feature is not found, NULL is returned. Note that this function is + only useful for features with data, and can not detect features that are + present but have NULL data. +*/ +static inline void* +lv2_features_data(const LV2_Feature* const* features, const char* const uri) +{ + if (features) { + for (const LV2_Feature* const* f = features; *f; ++f) { + if (!strcmp(uri, (*f)->URI)) { + return (*f)->data; + } + } + } + return NULL; +} + +/** + Query a features array. + + This function allows getting several features in one call, and detect + missing required features, with the same caveat of lv2_features_data(). + + The arguments should be a series of const char* uri, void** data, bool + required, terminated by a NULL URI. The data pointers MUST be initialized + to NULL. For example: + + @code + LV2_URID_Log* log = NULL; + LV2_URID_Map* map = NULL; + const char* missing = lv2_features_query( + features, + LV2_LOG__log, &log, false, + LV2_URID__map, &map, true, + NULL); + @endcode + + @return NULL on success, otherwise the URI of this missing feature. +*/ +static inline const char* +lv2_features_query(const LV2_Feature* const* features, ...) +{ + va_list args; + va_start(args, features); + + const char* uri = NULL; + while ((uri = va_arg(args, const char*))) { + void** data = va_arg(args, void**); + bool required = (bool)va_arg(args, int); + + *data = lv2_features_data(features, uri); + if (required && !*data) { + va_end(args); + return uri; + } + } + + va_end(args); + return NULL; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ diff --git a/plugins/lv2/vendor/lv2/data-access/data-access.h b/plugins/lv2/vendor/lv2/data-access/data-access.h new file mode 100644 index 0000000..2809d93 --- /dev/null +++ b/plugins/lv2/vendor/lv2/data-access/data-access.h @@ -0,0 +1,60 @@ +// Copyright 2008-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_DATA_ACCESS_H +#define LV2_DATA_ACCESS_H + +/** + @defgroup data-access Data Access + @ingroup lv2 + + Access to plugin extension_data() for UIs. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_DATA_ACCESS_URI "http://lv2plug.in/ns/ext/data-access" ///< http://lv2plug.in/ns/ext/data-access +#define LV2_DATA_ACCESS_PREFIX LV2_DATA_ACCESS_URI "#" ///< http://lv2plug.in/ns/ext/data-access# + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The data field of the LV2_Feature for this extension. + + To support this feature the host must pass an LV2_Feature struct to the + instantiate method with URI "http://lv2plug.in/ns/ext/data-access" + and data pointed to an instance of this struct. +*/ +typedef struct { + /** + A pointer to a method the UI can call to get data (of a type specified + by some other extension) from the plugin. + + This call never is never guaranteed to return anything, UIs should + degrade gracefully if direct access to the plugin data is not possible + (in which case this function will return NULL). + + This is for access to large data that can only possibly work if the UI + and plugin are running in the same process. For all other things, use + the normal LV2 UI communication system. + */ + const void* (*data_access)(const char* uri); +} LV2_Extension_Data_Feature; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_DATA_ACCESS_H */ diff --git a/plugins/lv2/vendor/lv2/dynmanifest/dynmanifest.h b/plugins/lv2/vendor/lv2/dynmanifest/dynmanifest.h new file mode 100644 index 0000000..d67c750 --- /dev/null +++ b/plugins/lv2/vendor/lv2/dynmanifest/dynmanifest.h @@ -0,0 +1,146 @@ +// Copyright 2008-2011 Stefano D'Angelo +// SPDX-License-Identifier: ISC + +#ifndef LV2_DYN_MANIFEST_H_INCLUDED +#define LV2_DYN_MANIFEST_H_INCLUDED + +/** + @defgroup dynmanifest Dynamic Manifest + @ingroup lv2 + + Support for dynamic data generation. + + See for details. + + @{ +*/ + +#include "lv2/core/lv2.h" + +#include + +// clang-format off + +#define LV2_DYN_MANIFEST_URI "http://lv2plug.in/ns/ext/dynmanifest" ///< http://lv2plug.in/ns/ext/dynmanifest +#define LV2_DYN_MANIFEST_PREFIX LV2_DYN_MANIFEST_URI "#" ///< http://lv2plug.in/ns/ext/dynmanifest# + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Dynamic manifest generator handle. + + This handle indicates a particular status of a dynamic manifest generator. + The host MUST NOT attempt to interpret it and, unlikely LV2_Handle, it is + NOT even valid to compare this to NULL. The dynamic manifest generator MAY + use it to reference internal data. +*/ +typedef void* LV2_Dyn_Manifest_Handle; + +/** + Generate the dynamic manifest. + + @param handle Pointer to an uninitialized dynamic manifest generator handle. + + @param features NULL terminated array of LV2_Feature structs which represent + the features the host supports. The dynamic manifest generator may refuse to + (re)generate the dynamic manifest if required features are not found here + (however hosts SHOULD NOT use this as a discovery mechanism, instead of + reading the static manifest file). This array must always exist; if a host + has no features, it MUST pass a single element array containing NULL. + + @return 0 on success, otherwise a non-zero error code. The host SHOULD + evaluate the result of the operation by examining the returned value and + MUST NOT try to interpret the value of handle. +*/ +int +lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle* handle, + const LV2_Feature* const* features); + +/** + Fetch a "list" of subject URIs described in the dynamic manifest. + + The dynamic manifest generator has to fill the resource only with the needed + triples to make the host aware of the "objects" it wants to expose. For + example, if the plugin library exposes a regular LV2 plugin, it should + output only a triple like the following: + + a lv2:Plugin . + + The objects that are eligible for exposure are those that would need to be + represented by a subject node in a static manifest. + + @param handle Dynamic manifest generator handle. + + @param fp FILE * identifying the resource the host has to set up for the + dynamic manifest generator. The host MUST pass a writable, empty resource to + this function, and the dynamic manifest generator MUST ONLY perform write + operations on it at the end of the stream (for example, using only + fprintf(), fwrite() and similar). + + @return 0 on success, otherwise a non-zero error code. +*/ +int +lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, FILE* fp); + +/** + Function that fetches data related to a specific URI. + + The dynamic manifest generator has to fill the resource with data related to + object represented by the given URI. For example, if the library exposes a + regular LV2 plugin whose URI, as retrieved by the host using + lv2_dyn_manifest_get_subjects() is http://example.org/plugin then it + should output something like: + +
+   
+       a lv2:Plugin ;
+       doap:name "My Plugin" ;
+       lv2:binary  ;
+       etc:etc "..." .
+   
+ + @param handle Dynamic manifest generator handle. + + @param fp FILE * identifying the resource the host has to set up for the + dynamic manifest generator. The host MUST pass a writable resource to this + function, and the dynamic manifest generator MUST ONLY perform write + operations on it at the current position of the stream (for example, using + only fprintf(), fwrite() and similar). + + @param uri URI to get data about (in the "plain" form, i.e., absolute URI + without Turtle prefixes). + + @return 0 on success, otherwise a non-zero error code. +*/ +int +lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, + FILE* fp, + const char* uri); + +/** + Function that ends the operations on the dynamic manifest generator. + + This function SHOULD be used by the dynamic manifest generator to perform + cleanup operations, etc. + + Once this function is called, referring to handle will cause undefined + behavior. + + @param handle Dynamic manifest generator handle. +*/ +void +lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_DYN_MANIFEST_H_INCLUDED */ diff --git a/plugins/lv2/vendor/lv2/event/event-helpers.h b/plugins/lv2/vendor/lv2/event/event-helpers.h new file mode 100644 index 0000000..0bf352f --- /dev/null +++ b/plugins/lv2/vendor/lv2/event/event-helpers.h @@ -0,0 +1,242 @@ +// Copyright 2008-2015 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_EVENT_HELPERS_H +#define LV2_EVENT_HELPERS_H + +/** + @file event-helpers.h Helper functions for the LV2 Event extension + . +*/ + +#include "lv2/core/attributes.h" +#include "lv2/event/event.h" + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +LV2_DISABLE_DEPRECATION_WARNINGS + +/** @file + * Helper functions for the LV2 Event extension + * . + * + * These functions are provided for convenience only, use of them is not + * required for supporting lv2ev (i.e. the events extension is defined by the + * raw buffer format described in lv2_event.h and NOT by this API). + * + * Note that these functions are all static inline which basically means: + * do not take the address of these functions. */ + +/** Pad a size to 64 bits (for event sizes) */ +static inline uint16_t +lv2_event_pad_size(uint16_t size) +{ + return (uint16_t)((size + 7U) & ~7U); +} + +/** Initialize (empty, reset..) an existing event buffer. + * The contents of buf are ignored entirely and overwritten, except capacity + * which is unmodified. */ +static inline void +lv2_event_buffer_reset(LV2_Event_Buffer* buf, + uint16_t stamp_type, + uint8_t* data) +{ + buf->data = data; + buf->header_size = sizeof(LV2_Event_Buffer); + buf->stamp_type = stamp_type; + buf->event_count = 0; + buf->size = 0; +} + +/** Allocate a new, empty event buffer. */ +static inline LV2_Event_Buffer* +lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) +{ + const size_t size = sizeof(LV2_Event_Buffer) + capacity; + LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(size); + if (buf != NULL) { + buf->capacity = capacity; + lv2_event_buffer_reset(buf, stamp_type, (uint8_t*)(buf + 1)); + return buf; + } + return NULL; +} + +/** An iterator over an LV2_Event_Buffer. + * + * Multiple simultaneous read iterators over a single buffer is fine, + * but changing the buffer invalidates all iterators. */ +typedef struct { + LV2_Event_Buffer* buf; + uint32_t offset; +} LV2_Event_Iterator; + +/** Reset an iterator to point to the start of `buf`. + * @return True if `iter` is valid, otherwise false (buffer is empty) */ +static inline bool +lv2_event_begin(LV2_Event_Iterator* iter, LV2_Event_Buffer* buf) +{ + iter->buf = buf; + iter->offset = 0; + return (buf->size > 0); +} + +/** Check if `iter` is valid. + * @return True if `iter` is valid, otherwise false (past end of buffer) */ +static inline bool +lv2_event_is_valid(LV2_Event_Iterator* iter) +{ + return (iter->buf && (iter->offset < iter->buf->size)); +} + +/** Advance `iter` forward one event. + * `iter` must be valid. + * @return True if `iter` is valid, otherwise false (reached end of buffer) */ +static inline bool +lv2_event_increment(LV2_Event_Iterator* iter) +{ + if (!lv2_event_is_valid(iter)) { + return false; + } + + LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset); + + iter->offset += + lv2_event_pad_size((uint16_t)((uint16_t)sizeof(LV2_Event) + ev->size)); + + return true; +} + +/** Dereference an event iterator (get the event currently pointed at). + * `iter` must be valid. + * `data` if non-NULL, will be set to point to the contents of the event + * returned. + * @return A Pointer to the event `iter` is currently pointing at, or NULL + * if the end of the buffer is reached (in which case `data` is + * also set to NULL). */ +static inline LV2_Event* +lv2_event_get(LV2_Event_Iterator* iter, uint8_t** data) +{ + if (!lv2_event_is_valid(iter)) { + return NULL; + } + + LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset); + + if (data) { + *data = (uint8_t*)ev + sizeof(LV2_Event); + } + + return ev; +} + +/** Write an event at `iter`. + * The event (if any) pointed to by `iter` will be overwritten, and `iter` + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ + if (!iter->buf) { + return false; + } + + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) { + return false; + } + + LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); + ++iter->buf->event_count; + + size = lv2_event_pad_size((uint16_t)(sizeof(LV2_Event) + size)); + iter->buf->size += size; + iter->offset += size; + + return true; +} + +/** Reserve space for an event in the buffer and return a pointer to + the memory where the caller can write the event data, or NULL if there + is not enough room in the buffer. */ +static inline uint8_t* +lv2_event_reserve(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size) +{ + const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + size); + if (iter->buf->capacity - iter->buf->size < total_size) { + return NULL; + } + + LV2_Event* const ev = (LV2_Event*)(iter->buf->data + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + ++iter->buf->event_count; + + const uint16_t padded_size = lv2_event_pad_size(total_size); + iter->buf->size += padded_size; + iter->offset += padded_size; + + return (uint8_t*)ev + sizeof(LV2_Event); +} + +/** Write an event at `iter`. + * The event (if any) pointed to by `iter` will be overwritten, and `iter` + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write_event(LV2_Event_Iterator* iter, + const LV2_Event* ev, + const uint8_t* data) +{ + const uint16_t total_size = (uint16_t)(sizeof(LV2_Event) + ev->size); + if (iter->buf->capacity - iter->buf->size < total_size) { + return false; + } + + LV2_Event* const write_ev = (LV2_Event*)(iter->buf->data + iter->offset); + + *write_ev = *ev; + memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); + ++iter->buf->event_count; + + const uint16_t padded_size = lv2_event_pad_size(total_size); + iter->buf->size += padded_size; + iter->offset += padded_size; + + return true; +} + +LV2_RESTORE_WARNINGS + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EVENT_HELPERS_H */ diff --git a/plugins/lv2/vendor/lv2/event/event.h b/plugins/lv2/vendor/lv2/event/event.h new file mode 100644 index 0000000..b880de1 --- /dev/null +++ b/plugins/lv2/vendor/lv2/event/event.h @@ -0,0 +1,285 @@ +// Copyright 2008-2016 David Robillard +// Copyright 2006-2007 Lars Luthman +// SPDX-License-Identifier: ISC + +#ifndef LV2_EVENT_H +#define LV2_EVENT_H + +/** + @defgroup event Event + @ingroup lv2 + + Generic time-stamped events. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" ///< http://lv2plug.in/ns/ext/event +#define LV2_EVENT_PREFIX LV2_EVENT_URI "#" ///< http://lv2plug.in/ns/ext/event# + +#define LV2_EVENT__Event LV2_EVENT_PREFIX "Event" ///< http://lv2plug.in/ns/ext/event#Event +#define LV2_EVENT__EventPort LV2_EVENT_PREFIX "EventPort" ///< http://lv2plug.in/ns/ext/event#EventPort +#define LV2_EVENT__FrameStamp LV2_EVENT_PREFIX "FrameStamp" ///< http://lv2plug.in/ns/ext/event#FrameStamp +#define LV2_EVENT__TimeStamp LV2_EVENT_PREFIX "TimeStamp" ///< http://lv2plug.in/ns/ext/event#TimeStamp +#define LV2_EVENT__generatesTimeStamp LV2_EVENT_PREFIX "generatesTimeStamp" ///< http://lv2plug.in/ns/ext/event#generatesTimeStamp +#define LV2_EVENT__generic LV2_EVENT_PREFIX "generic" ///< http://lv2plug.in/ns/ext/event#generic +#define LV2_EVENT__inheritsEvent LV2_EVENT_PREFIX "inheritsEvent" ///< http://lv2plug.in/ns/ext/event#inheritsEvent +#define LV2_EVENT__inheritsTimeStamp LV2_EVENT_PREFIX "inheritsTimeStamp" ///< http://lv2plug.in/ns/ext/event#inheritsTimeStamp +#define LV2_EVENT__supportsEvent LV2_EVENT_PREFIX "supportsEvent" ///< http://lv2plug.in/ns/ext/event#supportsEvent +#define LV2_EVENT__supportsTimeStamp LV2_EVENT_PREFIX "supportsTimeStamp" ///< http://lv2plug.in/ns/ext/event#supportsTimeStamp + +// clang-format on + +#define LV2_EVENT_AUDIO_STAMP 0 ///< Special timestamp type for audio frames + +#include "lv2/core/attributes.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +LV2_DISABLE_DEPRECATION_WARNINGS + +/** + The best Pulses Per Quarter Note for tempo-based uint32_t timestamps. + Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisible + by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. +*/ +LV2_DEPRECATED +static const uint32_t LV2_EVENT_PPQN = 3136573440U; + +/** + An LV2 event (header only). + + LV2 events are generic time-stamped containers for any type of event. + The type field defines the format of a given event's contents. + + This struct defines the header of an LV2 event. An LV2 event is a single + chunk of POD (plain old data), usually contained in a flat buffer (see + LV2_EventBuffer below). Unless a required feature says otherwise, hosts may + assume a deep copy of an LV2 event can be created safely using a simple: + + memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) +*/ +LV2_DEPRECATED +typedef struct { + /** + The frames portion of timestamp. The units used here can optionally be + set for a port (with the lv2ev:timeUnits property), otherwise this is + audio frames, corresponding to the sample_count parameter of the LV2 run + method (frame 0 is the first frame for that call to run). + */ + uint32_t frames; + + /** + The sub-frames portion of timestamp. The units used here can optionally + be set for a port (with the lv2ev:timeUnits property), otherwise this is + 1/(2^32) of an audio frame. + */ + uint32_t subframes; + + /** + The type of this event, as a number which represents some URI + defining an event type. This value MUST be some value previously + returned from a call to the uri_to_id function defined in the LV2 + URI map extension (see lv2_uri_map.h). + There are special rules which must be followed depending on the type + of an event. If the plugin recognizes an event type, the definition + of that event type will describe how to interpret the event, and + any required behaviour. Otherwise, if the type is 0, this event is a + non-POD event and lv2_event_unref MUST be called if the event is + 'dropped' (see above). Even if the plugin does not understand an event, + it may pass the event through to an output by simply copying (and NOT + calling lv2_event_unref). These rules are designed to allow for generic + event handling plugins and large non-POD events, but with minimal hassle + on simple plugins that "don't care" about these more advanced features. + */ + uint16_t type; + + /** + The size of the data portion of this event in bytes, which immediately + follows. The header size (12 bytes) is not included in this value. + */ + uint16_t size; + + /* size bytes of data follow here */ +} LV2_Event; + +/** + A buffer of LV2 events (header only). + + Like events (which this contains) an event buffer is a single chunk of POD: + the entire buffer (including contents) can be copied with a single memcpy. + The first contained event begins sizeof(LV2_EventBuffer) bytes after the + start of this struct. + + After this header, the buffer contains an event header (defined by struct + LV2_Event), followed by that event's contents (padded to 64 bits), followed + by another header, etc: + + | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | + |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... +*/ +LV2_DEPRECATED +typedef struct { + /** + The contents of the event buffer. This may or may not reside in the + same block of memory as this header, plugins must not assume either. + The host guarantees this points to at least capacity bytes of allocated + memory (though only size bytes of that are valid events). + */ + uint8_t* data; + + /** + The size of this event header in bytes (including everything). + + This is to allow for extending this header in the future without + breaking binary compatibility. Whenever this header is copied, + it MUST be done using this field (and NOT the sizeof this struct). + */ + uint16_t header_size; + + /** + The type of the time stamps for events in this buffer. + As a special exception, '0' always means audio frames and subframes + (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. + + INPUTS: The host must set this field to the numeric ID of some URI + defining the meaning of the frames/subframes fields of contained events + (obtained by the LV2 URI Map uri_to_id function with the URI of this + extension as the 'map' argument, see lv2_uri_map.h). The host must + never pass a plugin a buffer which uses a stamp type the plugin does not + 'understand'. The value of this field must never change, except when + connect_port is called on the input port, at which time the host MUST + have set the stamp_type field to the value that will be used for all + subsequent run calls. + + OUTPUTS: The plugin may set this to any value that has been returned + from uri_to_id with the URI of this extension for a 'map' argument. + When connected to a buffer with connect_port, output ports MUST set this + field to the type of time stamp they will be writing. On any call to + connect_port on an event input port, the plugin may change this field on + any output port, it is the responsibility of the host to check if any of + these values have changed and act accordingly. + */ + uint16_t stamp_type; + + /** + The number of events in this buffer. + + INPUTS: The host must set this field to the number of events contained + in the data buffer before calling run(). The plugin must not change + this field. + + OUTPUTS: The plugin must set this field to the number of events it has + written to the buffer before returning from run(). Any initial value + should be ignored by the plugin. + */ + uint32_t event_count; + + /** + The size of the data buffer in bytes. + This is set by the host and must not be changed by the plugin. + The host is allowed to change this between run() calls. + */ + uint32_t capacity; + + /** + The size of the initial portion of the data buffer containing data. + + INPUTS: The host must set this field to the number of bytes used + by all events it has written to the buffer (including headers) + before calling the plugin's run(). + The plugin must not change this field. + + OUTPUTS: The plugin must set this field to the number of bytes + used by all events it has written to the buffer (including headers) + before returning from run(). + Any initial value should be ignored by the plugin. + */ + uint32_t size; +} LV2_Event_Buffer; + +/** + Opaque pointer to host data. +*/ +LV2_DEPRECATED +typedef void* LV2_Event_Callback_Data; + +/** + Non-POD events feature. + + To support this feature the host must pass an LV2_Feature struct to the + plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" + and data pointed to an instance of this struct. Note this feature + is not mandatory to support the event extension. +*/ +LV2_DEPRECATED +typedef struct { + /** + Opaque pointer to host data. + + The plugin MUST pass this to any call to functions in this struct. + Otherwise, it must not be interpreted in any way. + */ + LV2_Event_Callback_Data callback_data; + + /** + Take a reference to a non-POD event. + + If a plugin receives an event with type 0, it means the event is a + pointer to some object in memory and not a flat sequence of bytes + in the buffer. When receiving a non-POD event, the plugin already + has an implicit reference to the event. If the event is stored AND + passed to an output, lv2_event_ref MUST be called on that event. + If the event is only stored OR passed through, this is not necessary + (as the plugin already has 1 implicit reference). + + @param callback_data The callback_data field of this struct. + + @param event An event received at an input that will not be copied to + an output or stored in any way. + + PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + + /** + Drop a reference to a non-POD event. + + If a plugin receives an event with type 0, it means the event is a + pointer to some object in memory and not a flat sequence of bytes + in the buffer. If the plugin does not pass the event through to + an output or store it internally somehow, it MUST call this function + on the event (more information on using non-POD events below). + + @param callback_data The callback_data field of this struct. + + @param event An event received at an input that will not be copied to an + output or stored in any way. + + PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); +} LV2_Event_Feature; + +LV2_RESTORE_WARNINGS + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_EVENT_H */ diff --git a/plugins/lv2/vendor/lv2/instance-access/instance-access.h b/plugins/lv2/vendor/lv2/instance-access/instance-access.h new file mode 100644 index 0000000..a87ecaf --- /dev/null +++ b/plugins/lv2/vendor/lv2/instance-access/instance-access.h @@ -0,0 +1,28 @@ +// Copyright 2008-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_INSTANCE_ACCESS_H +#define LV2_INSTANCE_ACCESS_H + +/** + @defgroup instance-access Instance Access + @ingroup lv2 + + Access to the LV2_Handle of a plugin for UIs. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_INSTANCE_ACCESS_URI "http://lv2plug.in/ns/ext/instance-access" ///< http://lv2plug.in/ns/ext/instance-access + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_INSTANCE_ACCESS_H */ diff --git a/plugins/lv2/vendor/lv2/log/log.h b/plugins/lv2/vendor/lv2/log/log.h new file mode 100644 index 0000000..cdaa6d3 --- /dev/null +++ b/plugins/lv2/vendor/lv2/log/log.h @@ -0,0 +1,100 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_LOG_H +#define LV2_LOG_H + +/** + @defgroup log Log + @ingroup lv2 + + Interface for plugins to log via the host. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_LOG_URI "http://lv2plug.in/ns/ext/log" ///< http://lv2plug.in/ns/ext/log +#define LV2_LOG_PREFIX LV2_LOG_URI "#" ///< http://lv2plug.in/ns/ext/log# + +#define LV2_LOG__Entry LV2_LOG_PREFIX "Entry" ///< http://lv2plug.in/ns/ext/log#Entry +#define LV2_LOG__Error LV2_LOG_PREFIX "Error" ///< http://lv2plug.in/ns/ext/log#Error +#define LV2_LOG__Note LV2_LOG_PREFIX "Note" ///< http://lv2plug.in/ns/ext/log#Note +#define LV2_LOG__Trace LV2_LOG_PREFIX "Trace" ///< http://lv2plug.in/ns/ext/log#Trace +#define LV2_LOG__Warning LV2_LOG_PREFIX "Warning" ///< http://lv2plug.in/ns/ext/log#Warning +#define LV2_LOG__log LV2_LOG_PREFIX "log" ///< http://lv2plug.in/ns/ext/log#log + +// clang-format on + +#include "lv2/urid/urid.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond */ +#ifdef __GNUC__ +/** Allow type checking of printf-like functions. */ +# define LV2_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) +#else +# define LV2_LOG_FUNC(fmt, arg1) +#endif +/** @endcond */ + +/** + Opaque data to host data for LV2_Log_Log. +*/ +typedef void* LV2_Log_Handle; + +/** + Log feature (LV2_LOG__log) +*/ +typedef struct { + /** + Opaque pointer to host data. + + This MUST be passed to methods in this struct whenever they are called. + Otherwise, it must not be interpreted in any way. + */ + LV2_Log_Handle handle; + + /** + Log a message, passing format parameters directly. + + The API of this function matches that of the standard C printf function, + except for the addition of the first two parameters. This function may + be called from any non-realtime context, or from any context if `type` + is @ref LV2_LOG__Trace. + */ + LV2_LOG_FUNC(3, 4) + int (*printf)(LV2_Log_Handle handle, LV2_URID type, const char* fmt, ...); + + /** + Log a message, passing format parameters in a va_list. + + The API of this function matches that of the standard C vprintf + function, except for the addition of the first two parameters. This + function may be called from any non-realtime context, or from any + context if `type` is @ref LV2_LOG__Trace. + */ + LV2_LOG_FUNC(3, 0) + int (*vprintf)(LV2_Log_Handle handle, + LV2_URID type, + const char* fmt, + va_list ap); +} LV2_Log_Log; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_LOG_H */ diff --git a/plugins/lv2/vendor/lv2/log/logger.h b/plugins/lv2/vendor/lv2/log/logger.h new file mode 100644 index 0000000..b7d2856 --- /dev/null +++ b/plugins/lv2/vendor/lv2/log/logger.h @@ -0,0 +1,144 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_ATOM_LOGGER_H +#define LV2_ATOM_LOGGER_H + +/** + @defgroup logger Logger + @ingroup log + + Convenience API for easy logging in plugin code. This API provides simple + wrappers for logging from a plugin, which automatically fall back to + printing to stderr if host support is unavailable. + + @{ +*/ + +#include "lv2/log/log.h" +#include "lv2/urid/urid.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Logger convenience API state. +*/ +typedef struct { + LV2_Log_Log* log; + + LV2_URID Error; + LV2_URID Note; + LV2_URID Trace; + LV2_URID Warning; +} LV2_Log_Logger; + +/** + Set `map` as the URI map for `logger`. + + This affects the message type URIDs (Error, Warning, etc) which are passed + to the log's print functions. +*/ +static inline void +lv2_log_logger_set_map(LV2_Log_Logger* logger, LV2_URID_Map* map) +{ + if (map) { + logger->Error = map->map(map->handle, LV2_LOG__Error); + logger->Note = map->map(map->handle, LV2_LOG__Note); + logger->Trace = map->map(map->handle, LV2_LOG__Trace); + logger->Warning = map->map(map->handle, LV2_LOG__Warning); + } else { + logger->Error = logger->Note = logger->Trace = logger->Warning = 0; + } +} + +/** + Initialise `logger`. + + URIs will be mapped using `map` and stored, a reference to `map` itself is + not held. Both `map` and `log` may be NULL when unsupported by the host, + in which case the implementation will fall back to printing to stderr. +*/ +static inline void +lv2_log_logger_init(LV2_Log_Logger* logger, LV2_URID_Map* map, LV2_Log_Log* log) +{ + logger->log = log; + lv2_log_logger_set_map(logger, map); +} + +/** + Log a message to the host, or stderr if support is unavailable. +*/ +LV2_LOG_FUNC(3, 0) +static inline int +lv2_log_vprintf(LV2_Log_Logger* logger, + LV2_URID type, + const char* fmt, + va_list args) +{ + return ((logger && logger->log) + ? logger->log->vprintf(logger->log->handle, type, fmt, args) + : vfprintf(stderr, fmt, args)); +} + +/** Log an error via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_error(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Error, fmt, args); + va_end(args); + return ret; +} + +/** Log a note via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_note(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Note, fmt, args); + va_end(args); + return ret; +} + +/** Log a trace via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_trace(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Trace, fmt, args); + va_end(args); + return ret; +} + +/** Log a warning via lv2_log_vprintf(). */ +LV2_LOG_FUNC(2, 3) +static inline int +lv2_log_warning(LV2_Log_Logger* logger, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + const int ret = lv2_log_vprintf(logger, logger->Warning, fmt, args); + va_end(args); + return ret; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_LOG_LOGGER_H */ diff --git a/plugins/lv2/vendor/lv2/midi/midi.h b/plugins/lv2/vendor/lv2/midi/midi.h new file mode 100644 index 0000000..cef7bc8 --- /dev/null +++ b/plugins/lv2/vendor/lv2/midi/midi.h @@ -0,0 +1,235 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_MIDI_H +#define LV2_MIDI_H + +/** + @defgroup midi MIDI + @ingroup lv2 + + Definitions of standard MIDI messages. + + See for details. + + @{ +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// clang-format off + +#define LV2_MIDI_URI "http://lv2plug.in/ns/ext/midi" ///< http://lv2plug.in/ns/ext/midi +#define LV2_MIDI_PREFIX LV2_MIDI_URI "#" ///< http://lv2plug.in/ns/ext/midi# + +#define LV2_MIDI__ActiveSense LV2_MIDI_PREFIX "ActiveSense" ///< http://lv2plug.in/ns/ext/midi#ActiveSense +#define LV2_MIDI__Aftertouch LV2_MIDI_PREFIX "Aftertouch" ///< http://lv2plug.in/ns/ext/midi#Aftertouch +#define LV2_MIDI__Bender LV2_MIDI_PREFIX "Bender" ///< http://lv2plug.in/ns/ext/midi#Bender +#define LV2_MIDI__ChannelPressure LV2_MIDI_PREFIX "ChannelPressure" ///< http://lv2plug.in/ns/ext/midi#ChannelPressure +#define LV2_MIDI__Chunk LV2_MIDI_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/midi#Chunk +#define LV2_MIDI__Clock LV2_MIDI_PREFIX "Clock" ///< http://lv2plug.in/ns/ext/midi#Clock +#define LV2_MIDI__Continue LV2_MIDI_PREFIX "Continue" ///< http://lv2plug.in/ns/ext/midi#Continue +#define LV2_MIDI__Controller LV2_MIDI_PREFIX "Controller" ///< http://lv2plug.in/ns/ext/midi#Controller +#define LV2_MIDI__MidiEvent LV2_MIDI_PREFIX "MidiEvent" ///< http://lv2plug.in/ns/ext/midi#MidiEvent +#define LV2_MIDI__NoteOff LV2_MIDI_PREFIX "NoteOff" ///< http://lv2plug.in/ns/ext/midi#NoteOff +#define LV2_MIDI__NoteOn LV2_MIDI_PREFIX "NoteOn" ///< http://lv2plug.in/ns/ext/midi#NoteOn +#define LV2_MIDI__ProgramChange LV2_MIDI_PREFIX "ProgramChange" ///< http://lv2plug.in/ns/ext/midi#ProgramChange +#define LV2_MIDI__QuarterFrame LV2_MIDI_PREFIX "QuarterFrame" ///< http://lv2plug.in/ns/ext/midi#QuarterFrame +#define LV2_MIDI__Reset LV2_MIDI_PREFIX "Reset" ///< http://lv2plug.in/ns/ext/midi#Reset +#define LV2_MIDI__SongPosition LV2_MIDI_PREFIX "SongPosition" ///< http://lv2plug.in/ns/ext/midi#SongPosition +#define LV2_MIDI__SongSelect LV2_MIDI_PREFIX "SongSelect" ///< http://lv2plug.in/ns/ext/midi#SongSelect +#define LV2_MIDI__Start LV2_MIDI_PREFIX "Start" ///< http://lv2plug.in/ns/ext/midi#Start +#define LV2_MIDI__Stop LV2_MIDI_PREFIX "Stop" ///< http://lv2plug.in/ns/ext/midi#Stop +#define LV2_MIDI__SystemCommon LV2_MIDI_PREFIX "SystemCommon" ///< http://lv2plug.in/ns/ext/midi#SystemCommon +#define LV2_MIDI__SystemExclusive LV2_MIDI_PREFIX "SystemExclusive" ///< http://lv2plug.in/ns/ext/midi#SystemExclusive +#define LV2_MIDI__SystemMessage LV2_MIDI_PREFIX "SystemMessage" ///< http://lv2plug.in/ns/ext/midi#SystemMessage +#define LV2_MIDI__SystemRealtime LV2_MIDI_PREFIX "SystemRealtime" ///< http://lv2plug.in/ns/ext/midi#SystemRealtime +#define LV2_MIDI__Tick LV2_MIDI_PREFIX "Tick" ///< http://lv2plug.in/ns/ext/midi#Tick +#define LV2_MIDI__TuneRequest LV2_MIDI_PREFIX "TuneRequest" ///< http://lv2plug.in/ns/ext/midi#TuneRequest +#define LV2_MIDI__VoiceMessage LV2_MIDI_PREFIX "VoiceMessage" ///< http://lv2plug.in/ns/ext/midi#VoiceMessage +#define LV2_MIDI__benderValue LV2_MIDI_PREFIX "benderValue" ///< http://lv2plug.in/ns/ext/midi#benderValue +#define LV2_MIDI__binding LV2_MIDI_PREFIX "binding" ///< http://lv2plug.in/ns/ext/midi#binding +#define LV2_MIDI__byteNumber LV2_MIDI_PREFIX "byteNumber" ///< http://lv2plug.in/ns/ext/midi#byteNumber +#define LV2_MIDI__channel LV2_MIDI_PREFIX "channel" ///< http://lv2plug.in/ns/ext/midi#channel +#define LV2_MIDI__chunk LV2_MIDI_PREFIX "chunk" ///< http://lv2plug.in/ns/ext/midi#chunk +#define LV2_MIDI__controllerNumber LV2_MIDI_PREFIX "controllerNumber" ///< http://lv2plug.in/ns/ext/midi#controllerNumber +#define LV2_MIDI__controllerValue LV2_MIDI_PREFIX "controllerValue" ///< http://lv2plug.in/ns/ext/midi#controllerValue +#define LV2_MIDI__noteNumber LV2_MIDI_PREFIX "noteNumber" ///< http://lv2plug.in/ns/ext/midi#noteNumber +#define LV2_MIDI__pressure LV2_MIDI_PREFIX "pressure" ///< http://lv2plug.in/ns/ext/midi#pressure +#define LV2_MIDI__programNumber LV2_MIDI_PREFIX "programNumber" ///< http://lv2plug.in/ns/ext/midi#programNumber +#define LV2_MIDI__property LV2_MIDI_PREFIX "property" ///< http://lv2plug.in/ns/ext/midi#property +#define LV2_MIDI__songNumber LV2_MIDI_PREFIX "songNumber" ///< http://lv2plug.in/ns/ext/midi#songNumber +#define LV2_MIDI__songPosition LV2_MIDI_PREFIX "songPosition" ///< http://lv2plug.in/ns/ext/midi#songPosition +#define LV2_MIDI__status LV2_MIDI_PREFIX "status" ///< http://lv2plug.in/ns/ext/midi#status +#define LV2_MIDI__statusMask LV2_MIDI_PREFIX "statusMask" ///< http://lv2plug.in/ns/ext/midi#statusMask +#define LV2_MIDI__velocity LV2_MIDI_PREFIX "velocity" ///< http://lv2plug.in/ns/ext/midi#velocity + +// clang-format on + +/** + MIDI Message Type. + + This includes both voice messages (which have a channel) and system messages + (which do not), as well as a sentinel value for invalid messages. To get + the type of a message suitable for use in a switch statement, use + lv2_midi_get_type() on the status byte. +*/ +typedef enum { + LV2_MIDI_MSG_INVALID = 0, /**< Invalid Message */ + LV2_MIDI_MSG_NOTE_OFF = 0x80, /**< Note Off */ + LV2_MIDI_MSG_NOTE_ON = 0x90, /**< Note On */ + LV2_MIDI_MSG_NOTE_PRESSURE = 0xA0, /**< Note Pressure */ + LV2_MIDI_MSG_CONTROLLER = 0xB0, /**< Controller */ + LV2_MIDI_MSG_PGM_CHANGE = 0xC0, /**< Program Change */ + LV2_MIDI_MSG_CHANNEL_PRESSURE = 0xD0, /**< Channel Pressure */ + LV2_MIDI_MSG_BENDER = 0xE0, /**< Pitch Bender */ + LV2_MIDI_MSG_SYSTEM_EXCLUSIVE = 0xF0, /**< System Exclusive Begin */ + LV2_MIDI_MSG_MTC_QUARTER = 0xF1, /**< MTC Quarter Frame */ + LV2_MIDI_MSG_SONG_POS = 0xF2, /**< Song Position */ + LV2_MIDI_MSG_SONG_SELECT = 0xF3, /**< Song Select */ + LV2_MIDI_MSG_TUNE_REQUEST = 0xF6, /**< Tune Request */ + LV2_MIDI_MSG_CLOCK = 0xF8, /**< Clock */ + LV2_MIDI_MSG_START = 0xFA, /**< Start */ + LV2_MIDI_MSG_CONTINUE = 0xFB, /**< Continue */ + LV2_MIDI_MSG_STOP = 0xFC, /**< Stop */ + LV2_MIDI_MSG_ACTIVE_SENSE = 0xFE, /**< Active Sensing */ + LV2_MIDI_MSG_RESET = 0xFF /**< Reset */ +} LV2_Midi_Message_Type; + +/** + Standard MIDI Controller Numbers. +*/ +typedef enum { + LV2_MIDI_CTL_MSB_BANK = 0x00, /**< Bank Selection */ + LV2_MIDI_CTL_MSB_MODWHEEL = 0x01, /**< Modulation */ + LV2_MIDI_CTL_MSB_BREATH = 0x02, /**< Breath */ + LV2_MIDI_CTL_MSB_FOOT = 0x04, /**< Foot */ + LV2_MIDI_CTL_MSB_PORTAMENTO_TIME = 0x05, /**< Portamento Time */ + LV2_MIDI_CTL_MSB_DATA_ENTRY = 0x06, /**< Data Entry */ + LV2_MIDI_CTL_MSB_MAIN_VOLUME = 0x07, /**< Main Volume */ + LV2_MIDI_CTL_MSB_BALANCE = 0x08, /**< Balance */ + LV2_MIDI_CTL_MSB_PAN = 0x0A, /**< Panpot */ + LV2_MIDI_CTL_MSB_EXPRESSION = 0x0B, /**< Expression */ + LV2_MIDI_CTL_MSB_EFFECT1 = 0x0C, /**< Effect1 */ + LV2_MIDI_CTL_MSB_EFFECT2 = 0x0D, /**< Effect2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 = 0x10, /**< General Purpose 1 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 = 0x11, /**< General Purpose 2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 = 0x12, /**< General Purpose 3 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 = 0x13, /**< General Purpose 4 */ + LV2_MIDI_CTL_LSB_BANK = 0x20, /**< Bank Selection */ + LV2_MIDI_CTL_LSB_MODWHEEL = 0x21, /**< Modulation */ + LV2_MIDI_CTL_LSB_BREATH = 0x22, /**< Breath */ + LV2_MIDI_CTL_LSB_FOOT = 0x24, /**< Foot */ + LV2_MIDI_CTL_LSB_PORTAMENTO_TIME = 0x25, /**< Portamento Time */ + LV2_MIDI_CTL_LSB_DATA_ENTRY = 0x26, /**< Data Entry */ + LV2_MIDI_CTL_LSB_MAIN_VOLUME = 0x27, /**< Main Volume */ + LV2_MIDI_CTL_LSB_BALANCE = 0x28, /**< Balance */ + LV2_MIDI_CTL_LSB_PAN = 0x2A, /**< Panpot */ + LV2_MIDI_CTL_LSB_EXPRESSION = 0x2B, /**< Expression */ + LV2_MIDI_CTL_LSB_EFFECT1 = 0x2C, /**< Effect1 */ + LV2_MIDI_CTL_LSB_EFFECT2 = 0x2D, /**< Effect2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 = 0x30, /**< General Purpose 1 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 = 0x31, /**< General Purpose 2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 = 0x32, /**< General Purpose 3 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 = 0x33, /**< General Purpose 4 */ + LV2_MIDI_CTL_SUSTAIN = 0x40, /**< Sustain Pedal */ + LV2_MIDI_CTL_PORTAMENTO = 0x41, /**< Portamento */ + LV2_MIDI_CTL_SOSTENUTO = 0x42, /**< Sostenuto */ + LV2_MIDI_CTL_SOFT_PEDAL = 0x43, /**< Soft Pedal */ + LV2_MIDI_CTL_LEGATO_FOOTSWITCH = 0x44, /**< Legato Foot Switch */ + LV2_MIDI_CTL_HOLD2 = 0x45, /**< Hold2 */ + LV2_MIDI_CTL_SC1_SOUND_VARIATION = 0x46, /**< SC1 Sound Variation */ + LV2_MIDI_CTL_SC2_TIMBRE = 0x47, /**< SC2 Timbre */ + LV2_MIDI_CTL_SC3_RELEASE_TIME = 0x48, /**< SC3 Release Time */ + LV2_MIDI_CTL_SC4_ATTACK_TIME = 0x49, /**< SC4 Attack Time */ + LV2_MIDI_CTL_SC5_BRIGHTNESS = 0x4A, /**< SC5 Brightness */ + LV2_MIDI_CTL_SC6 = 0x4B, /**< SC6 */ + LV2_MIDI_CTL_SC7 = 0x4C, /**< SC7 */ + LV2_MIDI_CTL_SC8 = 0x4D, /**< SC8 */ + LV2_MIDI_CTL_SC9 = 0x4E, /**< SC9 */ + LV2_MIDI_CTL_SC10 = 0x4F, /**< SC10 */ + LV2_MIDI_CTL_GENERAL_PURPOSE5 = 0x50, /**< General Purpose 5 */ + LV2_MIDI_CTL_GENERAL_PURPOSE6 = 0x51, /**< General Purpose 6 */ + LV2_MIDI_CTL_GENERAL_PURPOSE7 = 0x52, /**< General Purpose 7 */ + LV2_MIDI_CTL_GENERAL_PURPOSE8 = 0x53, /**< General Purpose 8 */ + LV2_MIDI_CTL_PORTAMENTO_CONTROL = 0x54, /**< Portamento Control */ + LV2_MIDI_CTL_E1_REVERB_DEPTH = 0x5B, /**< E1 Reverb Depth */ + LV2_MIDI_CTL_E2_TREMOLO_DEPTH = 0x5C, /**< E2 Tremolo Depth */ + LV2_MIDI_CTL_E3_CHORUS_DEPTH = 0x5D, /**< E3 Chorus Depth */ + LV2_MIDI_CTL_E4_DETUNE_DEPTH = 0x5E, /**< E4 Detune Depth */ + LV2_MIDI_CTL_E5_PHASER_DEPTH = 0x5F, /**< E5 Phaser Depth */ + LV2_MIDI_CTL_DATA_INCREMENT = 0x60, /**< Data Increment */ + LV2_MIDI_CTL_DATA_DECREMENT = 0x61, /**< Data Decrement */ + LV2_MIDI_CTL_NRPN_LSB = 0x62, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_NRPN_MSB = 0x63, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_RPN_LSB = 0x64, /**< Registered Parameter Number */ + LV2_MIDI_CTL_RPN_MSB = 0x65, /**< Registered Parameter Number */ + LV2_MIDI_CTL_ALL_SOUNDS_OFF = 0x78, /**< All Sounds Off */ + LV2_MIDI_CTL_RESET_CONTROLLERS = 0x79, /**< Reset Controllers */ + LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH = 0x7A, /**< Local Control Switch */ + LV2_MIDI_CTL_ALL_NOTES_OFF = 0x7B, /**< All Notes Off */ + LV2_MIDI_CTL_OMNI_OFF = 0x7C, /**< Omni Off */ + LV2_MIDI_CTL_OMNI_ON = 0x7D, /**< Omni On */ + LV2_MIDI_CTL_MONO1 = 0x7E, /**< Mono1 */ + LV2_MIDI_CTL_MONO2 = 0x7F /**< Mono2 */ +} LV2_Midi_Controller; + +/** + Return true iff `msg` is a MIDI voice message (which has a channel). +*/ +static inline bool +lv2_midi_is_voice_message(const uint8_t* msg) +{ + return msg[0] >= 0x80 && msg[0] < 0xF0; +} + +/** + Return true iff `msg` is a MIDI system message (which has no channel). +*/ +static inline bool +lv2_midi_is_system_message(const uint8_t* msg) +{ + switch (msg[0]) { + case 0xF4: + case 0xF5: + case 0xF7: + case 0xF9: + case 0xFD: + return false; + default: + return (msg[0] & 0xF0U) == 0xF0U; + } +} + +/** + Return the type of a MIDI message. + @param msg Pointer to the start (status byte) of a MIDI message. +*/ +static inline LV2_Midi_Message_Type +lv2_midi_message_type(const uint8_t* msg) +{ + if (lv2_midi_is_voice_message(msg)) { + return (LV2_Midi_Message_Type)(msg[0] & 0xF0U); + } + + if (lv2_midi_is_system_message(msg)) { + return (LV2_Midi_Message_Type)msg[0]; + } + + return LV2_MIDI_MSG_INVALID; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_MIDI_H */ diff --git a/plugins/lv2/vendor/lv2/morph/morph.h b/plugins/lv2/vendor/lv2/morph/morph.h new file mode 100644 index 0000000..e60a7d5 --- /dev/null +++ b/plugins/lv2/vendor/lv2/morph/morph.h @@ -0,0 +1,35 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_MORPH_H +#define LV2_MORPH_H + +/** + @defgroup morph Morph + @ingroup lv2 + + Ports that can dynamically change type. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_MORPH_URI "http://lv2plug.in/ns/ext/morph" ///< http://lv2plug.in/ns/ext/morph +#define LV2_MORPH_PREFIX LV2_MORPH_URI "#" ///< http://lv2plug.in/ns/ext/morph# + +#define LV2_MORPH__AutoMorphPort LV2_MORPH_PREFIX "AutoMorphPort" ///< http://lv2plug.in/ns/ext/morph#AutoMorphPort +#define LV2_MORPH__MorphPort LV2_MORPH_PREFIX "MorphPort" ///< http://lv2plug.in/ns/ext/morph#MorphPort +#define LV2_MORPH__interface LV2_MORPH_PREFIX "interface" ///< http://lv2plug.in/ns/ext/morph#interface +#define LV2_MORPH__supportsType LV2_MORPH_PREFIX "supportsType" ///< http://lv2plug.in/ns/ext/morph#supportsType +#define LV2_MORPH__currentType LV2_MORPH_PREFIX "currentType" ///< http://lv2plug.in/ns/ext/morph#currentType + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_MORPH_H */ diff --git a/plugins/lv2/vendor/lv2/options/options.h b/plugins/lv2/vendor/lv2/options/options.h new file mode 100644 index 0000000..48f7bfb --- /dev/null +++ b/plugins/lv2/vendor/lv2/options/options.h @@ -0,0 +1,136 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_OPTIONS_H +#define LV2_OPTIONS_H + +/** + @defgroup options Options + @ingroup lv2 + + Instantiation time options. + + See for details. + + @{ +*/ + +#include "lv2/core/lv2.h" +#include "lv2/urid/urid.h" + +#include + +// clang-format off + +#define LV2_OPTIONS_URI "http://lv2plug.in/ns/ext/options" ///< http://lv2plug.in/ns/ext/options +#define LV2_OPTIONS_PREFIX LV2_OPTIONS_URI "#" ///< http://lv2plug.in/ns/ext/options# + +#define LV2_OPTIONS__Option LV2_OPTIONS_PREFIX "Option" ///< http://lv2plug.in/ns/ext/options#Option +#define LV2_OPTIONS__interface LV2_OPTIONS_PREFIX "interface" ///< http://lv2plug.in/ns/ext/options#interface +#define LV2_OPTIONS__options LV2_OPTIONS_PREFIX "options" ///< http://lv2plug.in/ns/ext/options#options +#define LV2_OPTIONS__requiredOption LV2_OPTIONS_PREFIX "requiredOption" ///< http://lv2plug.in/ns/ext/options#requiredOption +#define LV2_OPTIONS__supportedOption LV2_OPTIONS_PREFIX "supportedOption" ///< http://lv2plug.in/ns/ext/options#supportedOption + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The context of an Option, which defines the subject it applies to. +*/ +typedef enum { + /** + This option applies to the instance itself. The subject must be + ignored. + */ + LV2_OPTIONS_INSTANCE, + + /** + This option applies to some named resource. The subject is a URI mapped + to an integer (a LV2_URID, like the key) + */ + LV2_OPTIONS_RESOURCE, + + /** + This option applies to some blank node. The subject is a blank node + identifier, which is valid only within the current local scope. + */ + LV2_OPTIONS_BLANK, + + /** + This option applies to a port on the instance. The subject is the + port's index. + */ + LV2_OPTIONS_PORT +} LV2_Options_Context; + +/** + An option. + + This is a property with a subject, also known as a triple or statement. + + This struct is useful anywhere a statement needs to be passed where no + memory ownership issues are present (since the value is a const pointer). + + Options can be passed to an instance via the feature LV2_OPTIONS__options + with data pointed to an array of options terminated by a zeroed option, or + accessed/manipulated using LV2_Options_Interface. +*/ +typedef struct { + LV2_Options_Context context; /**< Context (type of subject). */ + uint32_t subject; /**< Subject. */ + LV2_URID key; /**< Key (property). */ + uint32_t size; /**< Size of value in bytes. */ + LV2_URID type; /**< Type of value (datatype). */ + const void* value; /**< Pointer to value (object). */ +} LV2_Options_Option; + +/** A status code for option functions. */ +typedef enum { + LV2_OPTIONS_SUCCESS = 0U, /**< Completed successfully. */ + LV2_OPTIONS_ERR_UNKNOWN = 1U, /**< Unknown error. */ + LV2_OPTIONS_ERR_BAD_SUBJECT = 1U << 1U, /**< Invalid/unsupported subject. */ + LV2_OPTIONS_ERR_BAD_KEY = 1U << 2U, /**< Invalid/unsupported key. */ + LV2_OPTIONS_ERR_BAD_VALUE = 1U << 3U /**< Invalid/unsupported value. */ +} LV2_Options_Status; + +/** + Interface for dynamically setting options (LV2_OPTIONS__interface). +*/ +typedef struct { + /** + Get the given options. + + Each element of the passed options array MUST have type, subject, and + key set. All other fields (size, type, value) MUST be initialised to + zero, and are set to the option value if such an option is found. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*get)(LV2_Handle instance, LV2_Options_Option* options); + + /** + Set the given options. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*set)(LV2_Handle instance, const LV2_Options_Option* options); +} LV2_Options_Interface; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_OPTIONS_H */ diff --git a/plugins/lv2/vendor/lv2/parameters/parameters.h b/plugins/lv2/vendor/lv2/parameters/parameters.h new file mode 100644 index 0000000..6c17a1c --- /dev/null +++ b/plugins/lv2/vendor/lv2/parameters/parameters.h @@ -0,0 +1,55 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_PARAMETERS_H +#define LV2_PARAMETERS_H + +/** + @defgroup parameters Parameters + @ingroup lv2 + + Common parameters for audio processing. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_PARAMETERS_URI "http://lv2plug.in/ns/ext/parameters" ///< http://lv2plug.in/ns/ext/parameters +#define LV2_PARAMETERS_PREFIX LV2_PARAMETERS_URI "#" ///< http://lv2plug.in/ns/ext/parameters# + +#define LV2_PARAMETERS__CompressorControls LV2_PARAMETERS_PREFIX "CompressorControls" ///< http://lv2plug.in/ns/ext/parameters#CompressorControls +#define LV2_PARAMETERS__ControlGroup LV2_PARAMETERS_PREFIX "ControlGroup" ///< http://lv2plug.in/ns/ext/parameters#ControlGroup +#define LV2_PARAMETERS__EnvelopeControls LV2_PARAMETERS_PREFIX "EnvelopeControls" ///< http://lv2plug.in/ns/ext/parameters#EnvelopeControls +#define LV2_PARAMETERS__FilterControls LV2_PARAMETERS_PREFIX "FilterControls" ///< http://lv2plug.in/ns/ext/parameters#FilterControls +#define LV2_PARAMETERS__OscillatorControls LV2_PARAMETERS_PREFIX "OscillatorControls" ///< http://lv2plug.in/ns/ext/parameters#OscillatorControls +#define LV2_PARAMETERS__amplitude LV2_PARAMETERS_PREFIX "amplitude" ///< http://lv2plug.in/ns/ext/parameters#amplitude +#define LV2_PARAMETERS__attack LV2_PARAMETERS_PREFIX "attack" ///< http://lv2plug.in/ns/ext/parameters#attack +#define LV2_PARAMETERS__bypass LV2_PARAMETERS_PREFIX "bypass" ///< http://lv2plug.in/ns/ext/parameters#bypass +#define LV2_PARAMETERS__cutoffFrequency LV2_PARAMETERS_PREFIX "cutoffFrequency" ///< http://lv2plug.in/ns/ext/parameters#cutoffFrequency +#define LV2_PARAMETERS__decay LV2_PARAMETERS_PREFIX "decay" ///< http://lv2plug.in/ns/ext/parameters#decay +#define LV2_PARAMETERS__delay LV2_PARAMETERS_PREFIX "delay" ///< http://lv2plug.in/ns/ext/parameters#delay +#define LV2_PARAMETERS__dryLevel LV2_PARAMETERS_PREFIX "dryLevel" ///< http://lv2plug.in/ns/ext/parameters#dryLevel +#define LV2_PARAMETERS__frequency LV2_PARAMETERS_PREFIX "frequency" ///< http://lv2plug.in/ns/ext/parameters#frequency +#define LV2_PARAMETERS__gain LV2_PARAMETERS_PREFIX "gain" ///< http://lv2plug.in/ns/ext/parameters#gain +#define LV2_PARAMETERS__hold LV2_PARAMETERS_PREFIX "hold" ///< http://lv2plug.in/ns/ext/parameters#hold +#define LV2_PARAMETERS__pulseWidth LV2_PARAMETERS_PREFIX "pulseWidth" ///< http://lv2plug.in/ns/ext/parameters#pulseWidth +#define LV2_PARAMETERS__ratio LV2_PARAMETERS_PREFIX "ratio" ///< http://lv2plug.in/ns/ext/parameters#ratio +#define LV2_PARAMETERS__release LV2_PARAMETERS_PREFIX "release" ///< http://lv2plug.in/ns/ext/parameters#release +#define LV2_PARAMETERS__resonance LV2_PARAMETERS_PREFIX "resonance" ///< http://lv2plug.in/ns/ext/parameters#resonance +#define LV2_PARAMETERS__sampleRate LV2_PARAMETERS_PREFIX "sampleRate" ///< http://lv2plug.in/ns/ext/parameters#sampleRate +#define LV2_PARAMETERS__sustain LV2_PARAMETERS_PREFIX "sustain" ///< http://lv2plug.in/ns/ext/parameters#sustain +#define LV2_PARAMETERS__threshold LV2_PARAMETERS_PREFIX "threshold" ///< http://lv2plug.in/ns/ext/parameters#threshold +#define LV2_PARAMETERS__waveform LV2_PARAMETERS_PREFIX "waveform" ///< http://lv2plug.in/ns/ext/parameters#waveform +#define LV2_PARAMETERS__wetDryRatio LV2_PARAMETERS_PREFIX "wetDryRatio" ///< http://lv2plug.in/ns/ext/parameters#wetDryRatio +#define LV2_PARAMETERS__wetLevel LV2_PARAMETERS_PREFIX "wetLevel" ///< http://lv2plug.in/ns/ext/parameters#wetLevel + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_PARAMETERS_H */ diff --git a/plugins/lv2/vendor/lv2/patch/patch.h b/plugins/lv2/vendor/lv2/patch/patch.h new file mode 100644 index 0000000..16b7889 --- /dev/null +++ b/plugins/lv2/vendor/lv2/patch/patch.h @@ -0,0 +1,60 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_PATCH_H +#define LV2_PATCH_H + +/** + @defgroup patch Patch + @ingroup lv2 + + Messages for accessing and manipulating properties. + + Note the patch extension is purely data, this header merely defines URIs for + convenience. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_PATCH_URI "http://lv2plug.in/ns/ext/patch" ///< http://lv2plug.in/ns/ext/patch +#define LV2_PATCH_PREFIX LV2_PATCH_URI "#" ///< http://lv2plug.in/ns/ext/patch# + +#define LV2_PATCH__Ack LV2_PATCH_PREFIX "Ack" ///< http://lv2plug.in/ns/ext/patch#Ack +#define LV2_PATCH__Delete LV2_PATCH_PREFIX "Delete" ///< http://lv2plug.in/ns/ext/patch#Delete +#define LV2_PATCH__Copy LV2_PATCH_PREFIX "Copy" ///< http://lv2plug.in/ns/ext/patch#Copy +#define LV2_PATCH__Error LV2_PATCH_PREFIX "Error" ///< http://lv2plug.in/ns/ext/patch#Error +#define LV2_PATCH__Get LV2_PATCH_PREFIX "Get" ///< http://lv2plug.in/ns/ext/patch#Get +#define LV2_PATCH__Message LV2_PATCH_PREFIX "Message" ///< http://lv2plug.in/ns/ext/patch#Message +#define LV2_PATCH__Move LV2_PATCH_PREFIX "Move" ///< http://lv2plug.in/ns/ext/patch#Move +#define LV2_PATCH__Patch LV2_PATCH_PREFIX "Patch" ///< http://lv2plug.in/ns/ext/patch#Patch +#define LV2_PATCH__Post LV2_PATCH_PREFIX "Post" ///< http://lv2plug.in/ns/ext/patch#Post +#define LV2_PATCH__Put LV2_PATCH_PREFIX "Put" ///< http://lv2plug.in/ns/ext/patch#Put +#define LV2_PATCH__Request LV2_PATCH_PREFIX "Request" ///< http://lv2plug.in/ns/ext/patch#Request +#define LV2_PATCH__Response LV2_PATCH_PREFIX "Response" ///< http://lv2plug.in/ns/ext/patch#Response +#define LV2_PATCH__Set LV2_PATCH_PREFIX "Set" ///< http://lv2plug.in/ns/ext/patch#Set +#define LV2_PATCH__accept LV2_PATCH_PREFIX "accept" ///< http://lv2plug.in/ns/ext/patch#accept +#define LV2_PATCH__add LV2_PATCH_PREFIX "add" ///< http://lv2plug.in/ns/ext/patch#add +#define LV2_PATCH__body LV2_PATCH_PREFIX "body" ///< http://lv2plug.in/ns/ext/patch#body +#define LV2_PATCH__context LV2_PATCH_PREFIX "context" ///< http://lv2plug.in/ns/ext/patch#context +#define LV2_PATCH__destination LV2_PATCH_PREFIX "destination" ///< http://lv2plug.in/ns/ext/patch#destination +#define LV2_PATCH__property LV2_PATCH_PREFIX "property" ///< http://lv2plug.in/ns/ext/patch#property +#define LV2_PATCH__readable LV2_PATCH_PREFIX "readable" ///< http://lv2plug.in/ns/ext/patch#readable +#define LV2_PATCH__remove LV2_PATCH_PREFIX "remove" ///< http://lv2plug.in/ns/ext/patch#remove +#define LV2_PATCH__request LV2_PATCH_PREFIX "request" ///< http://lv2plug.in/ns/ext/patch#request +#define LV2_PATCH__subject LV2_PATCH_PREFIX "subject" ///< http://lv2plug.in/ns/ext/patch#subject +#define LV2_PATCH__sequenceNumber LV2_PATCH_PREFIX "sequenceNumber" ///< http://lv2plug.in/ns/ext/patch#sequenceNumber +#define LV2_PATCH__value LV2_PATCH_PREFIX "value" ///< http://lv2plug.in/ns/ext/patch#value +#define LV2_PATCH__wildcard LV2_PATCH_PREFIX "wildcard" ///< http://lv2plug.in/ns/ext/patch#wildcard +#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable" ///< http://lv2plug.in/ns/ext/patch#writable + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_PATCH_H */ diff --git a/plugins/lv2/vendor/lv2/port-groups/port-groups.h b/plugins/lv2/vendor/lv2/port-groups/port-groups.h new file mode 100644 index 0000000..303890a --- /dev/null +++ b/plugins/lv2/vendor/lv2/port-groups/port-groups.h @@ -0,0 +1,64 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_PORT_GROUPS_H +#define LV2_PORT_GROUPS_H + +/** + @defgroup port-groups Port Groups + @ingroup lv2 + + Multi-channel groups of LV2 ports. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_PORT_GROUPS_URI "http://lv2plug.in/ns/ext/port-groups" ///< http://lv2plug.in/ns/ext/port-groups +#define LV2_PORT_GROUPS_PREFIX LV2_PORT_GROUPS_URI "#" ///< http://lv2plug.in/ns/ext/port-groups# + +#define LV2_PORT_GROUPS__DiscreteGroup LV2_PORT_GROUPS_PREFIX "DiscreteGroup" ///< http://lv2plug.in/ns/ext/port-groups#DiscreteGroup +#define LV2_PORT_GROUPS__Element LV2_PORT_GROUPS_PREFIX "Element" ///< http://lv2plug.in/ns/ext/port-groups#Element +#define LV2_PORT_GROUPS__FivePointOneGroup LV2_PORT_GROUPS_PREFIX "FivePointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#FivePointOneGroup +#define LV2_PORT_GROUPS__FivePointZeroGroup LV2_PORT_GROUPS_PREFIX "FivePointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#FivePointZeroGroup +#define LV2_PORT_GROUPS__FourPointZeroGroup LV2_PORT_GROUPS_PREFIX "FourPointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#FourPointZeroGroup +#define LV2_PORT_GROUPS__Group LV2_PORT_GROUPS_PREFIX "Group" ///< http://lv2plug.in/ns/ext/port-groups#Group +#define LV2_PORT_GROUPS__InputGroup LV2_PORT_GROUPS_PREFIX "InputGroup" ///< http://lv2plug.in/ns/ext/port-groups#InputGroup +#define LV2_PORT_GROUPS__MidSideGroup LV2_PORT_GROUPS_PREFIX "MidSideGroup" ///< http://lv2plug.in/ns/ext/port-groups#MidSideGroup +#define LV2_PORT_GROUPS__MonoGroup LV2_PORT_GROUPS_PREFIX "MonoGroup" ///< http://lv2plug.in/ns/ext/port-groups#MonoGroup +#define LV2_PORT_GROUPS__OutputGroup LV2_PORT_GROUPS_PREFIX "OutputGroup" ///< http://lv2plug.in/ns/ext/port-groups#OutputGroup +#define LV2_PORT_GROUPS__SevenPointOneGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#SevenPointOneGroup +#define LV2_PORT_GROUPS__SevenPointOneWideGroup LV2_PORT_GROUPS_PREFIX "SevenPointOneWideGroup" ///< http://lv2plug.in/ns/ext/port-groups#SevenPointOneWideGroup +#define LV2_PORT_GROUPS__SixPointOneGroup LV2_PORT_GROUPS_PREFIX "SixPointOneGroup" ///< http://lv2plug.in/ns/ext/port-groups#SixPointOneGroup +#define LV2_PORT_GROUPS__StereoGroup LV2_PORT_GROUPS_PREFIX "StereoGroup" ///< http://lv2plug.in/ns/ext/port-groups#StereoGroup +#define LV2_PORT_GROUPS__ThreePointZeroGroup LV2_PORT_GROUPS_PREFIX "ThreePointZeroGroup" ///< http://lv2plug.in/ns/ext/port-groups#ThreePointZeroGroup +#define LV2_PORT_GROUPS__center LV2_PORT_GROUPS_PREFIX "center" ///< http://lv2plug.in/ns/ext/port-groups#center +#define LV2_PORT_GROUPS__centerLeft LV2_PORT_GROUPS_PREFIX "centerLeft" ///< http://lv2plug.in/ns/ext/port-groups#centerLeft +#define LV2_PORT_GROUPS__centerRight LV2_PORT_GROUPS_PREFIX "centerRight" ///< http://lv2plug.in/ns/ext/port-groups#centerRight +#define LV2_PORT_GROUPS__element LV2_PORT_GROUPS_PREFIX "element" ///< http://lv2plug.in/ns/ext/port-groups#element +#define LV2_PORT_GROUPS__group LV2_PORT_GROUPS_PREFIX "group" ///< http://lv2plug.in/ns/ext/port-groups#group +#define LV2_PORT_GROUPS__left LV2_PORT_GROUPS_PREFIX "left" ///< http://lv2plug.in/ns/ext/port-groups#left +#define LV2_PORT_GROUPS__lowFrequencyEffects LV2_PORT_GROUPS_PREFIX "lowFrequencyEffects" ///< http://lv2plug.in/ns/ext/port-groups#lowFrequencyEffects +#define LV2_PORT_GROUPS__mainInput LV2_PORT_GROUPS_PREFIX "mainInput" ///< http://lv2plug.in/ns/ext/port-groups#mainInput +#define LV2_PORT_GROUPS__mainOutput LV2_PORT_GROUPS_PREFIX "mainOutput" ///< http://lv2plug.in/ns/ext/port-groups#mainOutput +#define LV2_PORT_GROUPS__rearCenter LV2_PORT_GROUPS_PREFIX "rearCenter" ///< http://lv2plug.in/ns/ext/port-groups#rearCenter +#define LV2_PORT_GROUPS__rearLeft LV2_PORT_GROUPS_PREFIX "rearLeft" ///< http://lv2plug.in/ns/ext/port-groups#rearLeft +#define LV2_PORT_GROUPS__rearRight LV2_PORT_GROUPS_PREFIX "rearRight" ///< http://lv2plug.in/ns/ext/port-groups#rearRight +#define LV2_PORT_GROUPS__right LV2_PORT_GROUPS_PREFIX "right" ///< http://lv2plug.in/ns/ext/port-groups#right +#define LV2_PORT_GROUPS__side LV2_PORT_GROUPS_PREFIX "side" ///< http://lv2plug.in/ns/ext/port-groups#side +#define LV2_PORT_GROUPS__sideChainOf LV2_PORT_GROUPS_PREFIX "sideChainOf" ///< http://lv2plug.in/ns/ext/port-groups#sideChainOf +#define LV2_PORT_GROUPS__sideLeft LV2_PORT_GROUPS_PREFIX "sideLeft" ///< http://lv2plug.in/ns/ext/port-groups#sideLeft +#define LV2_PORT_GROUPS__sideRight LV2_PORT_GROUPS_PREFIX "sideRight" ///< http://lv2plug.in/ns/ext/port-groups#sideRight +#define LV2_PORT_GROUPS__source LV2_PORT_GROUPS_PREFIX "source" ///< http://lv2plug.in/ns/ext/port-groups#source +#define LV2_PORT_GROUPS__subGroupOf LV2_PORT_GROUPS_PREFIX "subGroupOf" ///< http://lv2plug.in/ns/ext/port-groups#subGroupOf + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_PORT_GROUPS_H */ diff --git a/plugins/lv2/vendor/lv2/port-props/port-props.h b/plugins/lv2/vendor/lv2/port-props/port-props.h new file mode 100644 index 0000000..ff45140 --- /dev/null +++ b/plugins/lv2/vendor/lv2/port-props/port-props.h @@ -0,0 +1,40 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_PORT_PROPS_H +#define LV2_PORT_PROPS_H + +/** + @defgroup port-props Port Properties + @ingroup lv2 + + Various port properties. + + @{ +*/ + +// clang-format off + +#define LV2_PORT_PROPS_URI "http://lv2plug.in/ns/ext/port-props" ///< http://lv2plug.in/ns/ext/port-props +#define LV2_PORT_PROPS_PREFIX LV2_PORT_PROPS_URI "#" ///< http://lv2plug.in/ns/ext/port-props# + +#define LV2_PORT_PROPS__causesArtifacts LV2_PORT_PROPS_PREFIX "causesArtifacts" ///< http://lv2plug.in/ns/ext/port-props#causesArtifacts +#define LV2_PORT_PROPS__continuousCV LV2_PORT_PROPS_PREFIX "continuousCV" ///< http://lv2plug.in/ns/ext/port-props#continuousCV +#define LV2_PORT_PROPS__discreteCV LV2_PORT_PROPS_PREFIX "discreteCV" ///< http://lv2plug.in/ns/ext/port-props#discreteCV +#define LV2_PORT_PROPS__displayPriority LV2_PORT_PROPS_PREFIX "displayPriority" ///< http://lv2plug.in/ns/ext/port-props#displayPriority +#define LV2_PORT_PROPS__expensive LV2_PORT_PROPS_PREFIX "expensive" ///< http://lv2plug.in/ns/ext/port-props#expensive +#define LV2_PORT_PROPS__hasStrictBounds LV2_PORT_PROPS_PREFIX "hasStrictBounds" ///< http://lv2plug.in/ns/ext/port-props#hasStrictBounds +#define LV2_PORT_PROPS__logarithmic LV2_PORT_PROPS_PREFIX "logarithmic" ///< http://lv2plug.in/ns/ext/port-props#logarithmic +#define LV2_PORT_PROPS__notAutomatic LV2_PORT_PROPS_PREFIX "notAutomatic" ///< http://lv2plug.in/ns/ext/port-props#notAutomatic +#define LV2_PORT_PROPS__notOnGUI LV2_PORT_PROPS_PREFIX "notOnGUI" ///< http://lv2plug.in/ns/ext/port-props#notOnGUI +#define LV2_PORT_PROPS__rangeSteps LV2_PORT_PROPS_PREFIX "rangeSteps" ///< http://lv2plug.in/ns/ext/port-props#rangeSteps +#define LV2_PORT_PROPS__supportsStrictBounds LV2_PORT_PROPS_PREFIX "supportsStrictBounds" ///< http://lv2plug.in/ns/ext/port-props#supportsStrictBounds +#define LV2_PORT_PROPS__trigger LV2_PORT_PROPS_PREFIX "trigger" ///< http://lv2plug.in/ns/ext/port-props#trigger + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_PORT_PROPS_H */ diff --git a/plugins/lv2/vendor/lv2/presets/presets.h b/plugins/lv2/vendor/lv2/presets/presets.h new file mode 100644 index 0000000..7100605 --- /dev/null +++ b/plugins/lv2/vendor/lv2/presets/presets.h @@ -0,0 +1,35 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_PRESETS_H +#define LV2_PRESETS_H + +/** + @defgroup presets Presets + @ingroup lv2 + + Presets for plugins. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_PRESETS_URI "http://lv2plug.in/ns/ext/presets" ///< http://lv2plug.in/ns/ext/presets +#define LV2_PRESETS_PREFIX LV2_PRESETS_URI "#" ///< http://lv2plug.in/ns/ext/presets# + +#define LV2_PRESETS__Bank LV2_PRESETS_PREFIX "Bank" ///< http://lv2plug.in/ns/ext/presets#Bank +#define LV2_PRESETS__Preset LV2_PRESETS_PREFIX "Preset" ///< http://lv2plug.in/ns/ext/presets#Preset +#define LV2_PRESETS__bank LV2_PRESETS_PREFIX "bank" ///< http://lv2plug.in/ns/ext/presets#bank +#define LV2_PRESETS__preset LV2_PRESETS_PREFIX "preset" ///< http://lv2plug.in/ns/ext/presets#preset +#define LV2_PRESETS__value LV2_PRESETS_PREFIX "value" ///< http://lv2plug.in/ns/ext/presets#value + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_PRESETS_H */ diff --git a/plugins/lv2/vendor/lv2/resize-port/resize-port.h b/plugins/lv2/vendor/lv2/resize-port/resize-port.h new file mode 100644 index 0000000..cd4828e --- /dev/null +++ b/plugins/lv2/vendor/lv2/resize-port/resize-port.h @@ -0,0 +1,76 @@ +// Copyright 2007-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_RESIZE_PORT_H +#define LV2_RESIZE_PORT_H + +/** + @defgroup resize-port Resize Port + @ingroup lv2 + + Dynamically sized LV2 port buffers. + + @{ +*/ + +#include +#include + +// clang-format off + +#define LV2_RESIZE_PORT_URI "http://lv2plug.in/ns/ext/resize-port" ///< http://lv2plug.in/ns/ext/resize-port +#define LV2_RESIZE_PORT_PREFIX LV2_RESIZE_PORT_URI "#" ///< http://lv2plug.in/ns/ext/resize-port# + +#define LV2_RESIZE_PORT__asLargeAs LV2_RESIZE_PORT_PREFIX "asLargeAs" ///< http://lv2plug.in/ns/ext/resize-port#asLargeAs +#define LV2_RESIZE_PORT__minimumSize LV2_RESIZE_PORT_PREFIX "minimumSize" ///< http://lv2plug.in/ns/ext/resize-port#minimumSize +#define LV2_RESIZE_PORT__resize LV2_RESIZE_PORT_PREFIX "resize" ///< http://lv2plug.in/ns/ext/resize-port#resize + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** A status code for state functions. */ +typedef enum { + LV2_RESIZE_PORT_SUCCESS = 0, /**< Completed successfully. */ + LV2_RESIZE_PORT_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_RESIZE_PORT_ERR_NO_SPACE = 2 /**< Insufficient space. */ +} LV2_Resize_Port_Status; + +/** Opaque data for resize method. */ +typedef void* LV2_Resize_Port_Feature_Data; + +/** Host feature to allow plugins to resize their port buffers. */ +typedef struct { + /** Opaque data for resize method. */ + LV2_Resize_Port_Feature_Data data; + + /** + Resize a port buffer to at least `size` bytes. + + This function MAY return an error, in which case the port buffer was not + resized and the port is still connected to the same location. Plugins + MUST gracefully handle this situation. + + This function is in the audio threading class. + + The host MUST preserve the contents of the port buffer when resizing. + + Plugins MAY resize a port many times in a single run callback. Hosts + SHOULD make this as inexpensive as possible. + */ + LV2_Resize_Port_Status (*resize)(LV2_Resize_Port_Feature_Data data, + uint32_t index, + size_t size); +} LV2_Resize_Port_Resize; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_RESIZE_PORT_H */ diff --git a/plugins/lv2/vendor/lv2/state/state.h b/plugins/lv2/vendor/lv2/state/state.h new file mode 100644 index 0000000..9c5d9d8 --- /dev/null +++ b/plugins/lv2/vendor/lv2/state/state.h @@ -0,0 +1,379 @@ +// Copyright 2010-2016 David Robillard +// Copyright 2010 Leonard Ritter +// SPDX-License-Identifier: ISC + +#ifndef LV2_STATE_H +#define LV2_STATE_H + +/** + @defgroup state State + @ingroup lv2 + + An interface for LV2 plugins to save and restore state. + + See for details. + + @{ +*/ + +#include "lv2/core/lv2.h" + +#include +#include + +// clang-format off + +#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" ///< http://lv2plug.in/ns/ext/state +#define LV2_STATE_PREFIX LV2_STATE_URI "#" ///< http://lv2plug.in/ns/ext/state# + +#define LV2_STATE__State LV2_STATE_PREFIX "State" ///< http://lv2plug.in/ns/ext/state#State +#define LV2_STATE__interface LV2_STATE_PREFIX "interface" ///< http://lv2plug.in/ns/ext/state#interface +#define LV2_STATE__loadDefaultState LV2_STATE_PREFIX "loadDefaultState" ///< http://lv2plug.in/ns/ext/state#loadDefaultState +#define LV2_STATE__freePath LV2_STATE_PREFIX "freePath" ///< http://lv2plug.in/ns/ext/state#freePath +#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" ///< http://lv2plug.in/ns/ext/state#makePath +#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" ///< http://lv2plug.in/ns/ext/state#mapPath +#define LV2_STATE__state LV2_STATE_PREFIX "state" ///< http://lv2plug.in/ns/ext/state#state +#define LV2_STATE__threadSafeRestore LV2_STATE_PREFIX "threadSafeRestore" ///< http://lv2plug.in/ns/ext/state#threadSafeRestore +#define LV2_STATE__StateChanged LV2_STATE_PREFIX "StateChanged" ///< http://lv2plug.in/ns/ext/state#StateChanged + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* LV2_State_Handle; ///< Opaque handle for state save/restore +typedef void* + LV2_State_Free_Path_Handle; ///< Opaque handle for state:freePath feature +typedef void* + LV2_State_Map_Path_Handle; ///< Opaque handle for state:mapPath feature +typedef void* + LV2_State_Make_Path_Handle; ///< Opaque handle for state:makePath feature + +/** + Flags describing value characteristics. + + These flags are used along with the value's type URI to determine how to + (de-)serialise the value data, or whether it is even possible to do so. +*/ +typedef enum { + /** + Plain Old Data. + + Values with this flag contain no pointers or references to other areas + of memory. It is safe to copy POD values with a simple memcpy and store + them for the duration of the process. A POD value is not necessarily + safe to transmit between processes or machines (for example, filenames + are POD), see LV2_STATE_IS_PORTABLE for details. + + Implementations MUST NOT attempt to copy or serialise a non-POD value if + they do not understand its type (and thus know how to correctly do so). + */ + LV2_STATE_IS_POD = 1U << 0U, + + /** + Portable (architecture independent) data. + + Values with this flag are in a format that is usable on any + architecture. A portable value saved on one machine can be restored on + another machine regardless of architecture. The format of portable + values MUST NOT depend on architecture-specific properties like + endianness or alignment. Portable values MUST NOT contain filenames. + */ + LV2_STATE_IS_PORTABLE = 1U << 1U, + + /** + Native data. + + This flag is used by the host to indicate that the saved data is only + going to be used locally in the currently running process (for things + like instance duplication or snapshots), so the plugin should use the + most efficient representation possible and not worry about serialisation + and portability. + */ + LV2_STATE_IS_NATIVE = 1U << 2U +} LV2_State_Flags; + +/** A status code for state functions. */ +typedef enum { + LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ + LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ + LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ + LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ + LV2_STATE_ERR_NO_PROPERTY = 5, /**< Failed due to missing property. */ + LV2_STATE_ERR_NO_SPACE = 6 /**< Failed due to insufficient space. */ +} LV2_State_Status; + +/** + A host-provided function to store a property. + @param handle Must be the handle passed to LV2_State_Interface.save(). + @param key The key to store `value` under (URID). + @param value Pointer to the value to be stored. + @param size The size of `value` in bytes. + @param type The type of `value` (URID). + @param flags LV2_State_Flags for `value`. + @return 0 on success, otherwise a non-zero error code. + + The host passes a callback of this type to LV2_State_Interface.save(). This + callback is called repeatedly by the plugin to store all the properties that + describe its current state. + + DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from + existing vocabularies. If nothing appropriate is available, use http URIs + that point to somewhere you can host documents so documentation can be made + resolvable (typically a child of the plugin or project URI). If this is not + possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST + NOT pass an invalid URI key. + + The host MAY fail to store a property for whatever reason, but SHOULD + store any property that is LV2_STATE_IS_POD and LV2_STATE_IS_PORTABLE. + Implementations SHOULD use the types from the LV2 Atom extension + (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD + attempt to fall-back and avoid the error if possible. + + Note that `size` MUST be > 0, and `value` MUST point to a valid region of + memory `size` bytes long (this is required to make restore unambiguous). + + The plugin MUST NOT attempt to use this function outside of the + LV2_State_Interface.restore() context. +*/ +typedef LV2_State_Status (*LV2_State_Store_Function)(LV2_State_Handle handle, + uint32_t key, + const void* value, + size_t size, + uint32_t type, + uint32_t flags); + +/** + A host-provided function to retrieve a property. + @param handle Must be the handle passed to LV2_State_Interface.restore(). + @param key The key of the property to retrieve (URID). + @param size (Output) If non-NULL, set to the size of the restored value. + @param type (Output) If non-NULL, set to the type of the restored value. + @param flags (Output) If non-NULL, set to the flags for the restored value. + @return A pointer to the restored value (object), or NULL if no value + has been stored under `key`. + + A callback of this type is passed by the host to + LV2_State_Interface.restore(). This callback is called repeatedly by the + plugin to retrieve any properties it requires to restore its state. + + The returned value MUST remain valid until LV2_State_Interface.restore() + returns. The plugin MUST NOT attempt to use this function, or any value + returned from it, outside of the LV2_State_Interface.restore() context. +*/ +typedef const void* (*LV2_State_Retrieve_Function)(LV2_State_Handle handle, + uint32_t key, + size_t* size, + uint32_t* type, + uint32_t* flags); + +/** + LV2 Plugin State Interface. + + When the plugin's extension_data is called with argument + LV2_STATE__interface, the plugin MUST return an LV2_State_Interface + structure, which remains valid for the lifetime of the plugin. + + The host can use the contained function pointers to save and restore the + state of a plugin instance at any time, provided the threading restrictions + of the functions are met. + + Stored data is only guaranteed to be compatible between instances of plugins + with the same URI (i.e. if a change to a plugin would cause a fatal error + when restoring state saved by a previous version of that plugin, the plugin + URI MUST change just as it must when ports change incompatibly). Plugin + authors should consider this possibility, and always store sensible data + with meaningful types to avoid such problems in the future. +*/ +typedef struct { + /** + Save plugin state using a host-provided `store` callback. + + @param instance The instance handle of the plugin. + @param store The host-provided store callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to `store` if it is called. + @param flags Flags describing desired properties of this save. These + flags may be used to determine the most appropriate values to store. + @param features Extensible parameter for passing any additional + features to be used for this save. + + The plugin is expected to store everything necessary to completely + restore its state later. Plugins SHOULD store simple POD data whenever + possible, and consider the possibility of state being restored much + later on a different machine. + + The `handle` pointer and `store` function MUST NOT be used + beyond the scope of save(). + + This function has its own special threading class: it may not be called + concurrently with any "Instantiation" function, but it may be called + concurrently with functions in any other class, unless the definition of + that class prohibits it (for example, it may not be called concurrently + with a "Discovery" function, but it may be called concurrently with an + "Audio" function. The plugin is responsible for any locking or + lock-free techniques necessary to make this possible. + + Note that in the simple case where state is only modified by restore(), + there are no synchronization issues since save() is never called + concurrently with restore() (though run() may read it during a save). + + Plugins that dynamically modify state while running, however, must take + care to do so in such a way that a concurrent call to save() will save a + consistent representation of plugin state for a single instant in time. + */ + LV2_State_Status (*save)(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature* const* features); + + /** + Restore plugin state using a host-provided `retrieve` callback. + + @param instance The instance handle of the plugin. + @param retrieve The host-provided retrieve callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to `retrieve` if it is called. + @param flags Currently unused. + @param features Extensible parameter for passing any additional + features to be used for this restore. + + The plugin MAY assume a restored value was set by a previous call to + LV2_State_Interface.save() by a plugin with the same URI. + + The plugin MUST gracefully fall back to a default value when a value can + not be retrieved. This allows the host to reset the plugin state with + an empty map. + + The `handle` pointer and `store` function MUST NOT be used + beyond the scope of restore(). + + This function is in the "Instantiation" threading class as defined by + LV2. This means it MUST NOT be called concurrently with any other + function on the same plugin instance. + */ + LV2_State_Status (*restore)(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature* const* features); +} LV2_State_Interface; + +/** + Feature data for state:mapPath (@ref LV2_STATE__mapPath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Map_Path_Handle handle; + + /** + Map an absolute path to an abstract path for use in plugin state. + @param handle MUST be the `handle` member of this struct. + @param absolute_path The absolute path of a file. + @return An abstract path suitable for use in plugin state. + + The plugin MUST use this function to map any paths that will be stored + in plugin state. The returned value is an abstract path which MAY not + be an actual file system path; absolute_path() MUST be used to map + it to an actual path in order to use the file. + + Plugins MUST NOT make any assumptions about abstract paths except that + they can be mapped back to the absolute path of the "same" file (though + not necessarily the same original path) using absolute_path(). + + This function may only be called within the context of + LV2_State_Interface methods. The caller must free the returned value + with LV2_State_Free_Path.free_path(). + */ + char* (*abstract_path)(LV2_State_Map_Path_Handle handle, + const char* absolute_path); + + /** + Map an abstract path from plugin state to an absolute path. + @param handle MUST be the `handle` member of this struct. + @param abstract_path An abstract path (typically from plugin state). + @return An absolute file system path. + + The plugin MUST use this function in order to actually open or otherwise + use any paths loaded from plugin state. + + This function may only be called within the context of + LV2_State_Interface methods. The caller must free the returned value + with LV2_State_Free_Path.free_path(). + */ + char* (*absolute_path)(LV2_State_Map_Path_Handle handle, + const char* abstract_path); +} LV2_State_Map_Path; + +/** + Feature data for state:makePath (@ref LV2_STATE__makePath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Make_Path_Handle handle; + + /** + Return a path the plugin may use to create a new file. + @param handle MUST be the `handle` member of this struct. + @param path The path of the new file within a namespace unique to this + plugin instance. + @return The absolute path to use for the new file. + + This function can be used by plugins to create files and directories, + either at state saving time (if this feature is passed to + LV2_State_Interface.save()) or any time (if this feature is passed to + LV2_Descriptor.instantiate()). + + The host MUST do whatever is necessary for the plugin to be able to + create a file at the returned path (for example, using fopen()), + including creating any leading directories. + + If this function is passed to LV2_Descriptor.instantiate(), it may be + called from any non-realtime context. If it is passed to + LV2_State_Interface.save(), it may only be called within the dynamic + scope of that function call. + + The caller must free the returned value with + LV2_State_Free_Path.free_path(). + */ + char* (*path)(LV2_State_Make_Path_Handle handle, const char* path); +} LV2_State_Make_Path; + +/** + Feature data for state:freePath (@ref LV2_STATE__freePath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Free_Path_Handle handle; + + /** + Free a path returned by a state feature. + + @param handle MUST be the `handle` member of this struct. + @param path The path previously returned by a state feature. + + This function can be used by plugins to free paths allocated by the host + and returned by state features (LV2_State_Map_Path.abstract_path(), + LV2_State_Map_Path.absolute_path(), and LV2_State_Make_Path.path()). + */ + void (*free_path)(LV2_State_Free_Path_Handle handle, char* path); +} LV2_State_Free_Path; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_STATE_H */ diff --git a/plugins/lv2/vendor/lv2/time/time.h b/plugins/lv2/vendor/lv2/time/time.h new file mode 100644 index 0000000..83408c9 --- /dev/null +++ b/plugins/lv2/vendor/lv2/time/time.h @@ -0,0 +1,46 @@ +// Copyright 2011-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_TIME_H +#define LV2_TIME_H + +/** + @defgroup time Time + @ingroup lv2 + + Properties for describing time. + + Note the time extension is purely data, this header merely defines URIs for + convenience. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_TIME_URI "http://lv2plug.in/ns/ext/time" ///< http://lv2plug.in/ns/ext/time +#define LV2_TIME_PREFIX LV2_TIME_URI "#" ///< http://lv2plug.in/ns/ext/time# + +#define LV2_TIME__Time LV2_TIME_PREFIX "Time" ///< http://lv2plug.in/ns/ext/time#Time +#define LV2_TIME__Position LV2_TIME_PREFIX "Position" ///< http://lv2plug.in/ns/ext/time#Position +#define LV2_TIME__Rate LV2_TIME_PREFIX "Rate" ///< http://lv2plug.in/ns/ext/time#Rate +#define LV2_TIME__position LV2_TIME_PREFIX "position" ///< http://lv2plug.in/ns/ext/time#position +#define LV2_TIME__barBeat LV2_TIME_PREFIX "barBeat" ///< http://lv2plug.in/ns/ext/time#barBeat +#define LV2_TIME__bar LV2_TIME_PREFIX "bar" ///< http://lv2plug.in/ns/ext/time#bar +#define LV2_TIME__beat LV2_TIME_PREFIX "beat" ///< http://lv2plug.in/ns/ext/time#beat +#define LV2_TIME__beatUnit LV2_TIME_PREFIX "beatUnit" ///< http://lv2plug.in/ns/ext/time#beatUnit +#define LV2_TIME__beatsPerBar LV2_TIME_PREFIX "beatsPerBar" ///< http://lv2plug.in/ns/ext/time#beatsPerBar +#define LV2_TIME__beatsPerMinute LV2_TIME_PREFIX "beatsPerMinute" ///< http://lv2plug.in/ns/ext/time#beatsPerMinute +#define LV2_TIME__frame LV2_TIME_PREFIX "frame" ///< http://lv2plug.in/ns/ext/time#frame +#define LV2_TIME__framesPerSecond LV2_TIME_PREFIX "framesPerSecond" ///< http://lv2plug.in/ns/ext/time#framesPerSecond +#define LV2_TIME__speed LV2_TIME_PREFIX "speed" ///< http://lv2plug.in/ns/ext/time#speed + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_TIME_H */ diff --git a/plugins/lv2/vendor/lv2/ui/ui.h b/plugins/lv2/vendor/lv2/ui/ui.h new file mode 100644 index 0000000..5ef75a3 --- /dev/null +++ b/plugins/lv2/vendor/lv2/ui/ui.h @@ -0,0 +1,529 @@ +// Copyright 2009-2016 David Robillard +// Copyright 2006-2011 Lars Luthman +// SPDX-License-Identifier: ISC + +#ifndef LV2_UI_H +#define LV2_UI_H + +/** + @defgroup ui User Interfaces + @ingroup lv2 + + User interfaces of any type for plugins. + + See for details. + + @{ +*/ + +#include "lv2/core/lv2.h" +#include "lv2/urid/urid.h" + +#include +#include + +// clang-format off + +#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" ///< http://lv2plug.in/ns/extensions/ui +#define LV2_UI_PREFIX LV2_UI_URI "#" ///< http://lv2plug.in/ns/extensions/ui# + +#define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" ///< http://lv2plug.in/ns/extensions/ui#CocoaUI +#define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" ///< http://lv2plug.in/ns/extensions/ui#Gtk3UI +#define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" ///< http://lv2plug.in/ns/extensions/ui#GtkUI +#define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" ///< http://lv2plug.in/ns/extensions/ui#PortNotification +#define LV2_UI__PortProtocol LV2_UI_PREFIX "PortProtocol" ///< http://lv2plug.in/ns/extensions/ui#PortProtocol +#define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" ///< http://lv2plug.in/ns/extensions/ui#Qt4UI +#define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" ///< http://lv2plug.in/ns/extensions/ui#Qt5UI +#define LV2_UI__UI LV2_UI_PREFIX "UI" ///< http://lv2plug.in/ns/extensions/ui#UI +#define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" ///< http://lv2plug.in/ns/extensions/ui#WindowsUI +#define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" ///< http://lv2plug.in/ns/extensions/ui#X11UI +#define LV2_UI__binary LV2_UI_PREFIX "binary" ///< http://lv2plug.in/ns/extensions/ui#binary +#define LV2_UI__fixedSize LV2_UI_PREFIX "fixedSize" ///< http://lv2plug.in/ns/extensions/ui#fixedSize +#define LV2_UI__idleInterface LV2_UI_PREFIX "idleInterface" ///< http://lv2plug.in/ns/extensions/ui#idleInterface +#define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" ///< http://lv2plug.in/ns/extensions/ui#noUserResize +#define LV2_UI__notifyType LV2_UI_PREFIX "notifyType" ///< http://lv2plug.in/ns/extensions/ui#notifyType +#define LV2_UI__parent LV2_UI_PREFIX "parent" ///< http://lv2plug.in/ns/extensions/ui#parent +#define LV2_UI__plugin LV2_UI_PREFIX "plugin" ///< http://lv2plug.in/ns/extensions/ui#plugin +#define LV2_UI__portIndex LV2_UI_PREFIX "portIndex" ///< http://lv2plug.in/ns/extensions/ui#portIndex +#define LV2_UI__portMap LV2_UI_PREFIX "portMap" ///< http://lv2plug.in/ns/extensions/ui#portMap +#define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" ///< http://lv2plug.in/ns/extensions/ui#portNotification +#define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" ///< http://lv2plug.in/ns/extensions/ui#portSubscribe +#define LV2_UI__protocol LV2_UI_PREFIX "protocol" ///< http://lv2plug.in/ns/extensions/ui#protocol +#define LV2_UI__requestValue LV2_UI_PREFIX "requestValue" ///< http://lv2plug.in/ns/extensions/ui#requestValue +#define LV2_UI__floatProtocol LV2_UI_PREFIX "floatProtocol" ///< http://lv2plug.in/ns/extensions/ui#floatProtocol +#define LV2_UI__peakProtocol LV2_UI_PREFIX "peakProtocol" ///< http://lv2plug.in/ns/extensions/ui#peakProtocol +#define LV2_UI__resize LV2_UI_PREFIX "resize" ///< http://lv2plug.in/ns/extensions/ui#resize +#define LV2_UI__showInterface LV2_UI_PREFIX "showInterface" ///< http://lv2plug.in/ns/extensions/ui#showInterface +#define LV2_UI__touch LV2_UI_PREFIX "touch" ///< http://lv2plug.in/ns/extensions/ui#touch +#define LV2_UI__ui LV2_UI_PREFIX "ui" ///< http://lv2plug.in/ns/extensions/ui#ui +#define LV2_UI__updateRate LV2_UI_PREFIX "updateRate" ///< http://lv2plug.in/ns/extensions/ui#updateRate +#define LV2_UI__windowTitle LV2_UI_PREFIX "windowTitle" ///< http://lv2plug.in/ns/extensions/ui#windowTitle +#define LV2_UI__scaleFactor LV2_UI_PREFIX "scaleFactor" ///< http://lv2plug.in/ns/extensions/ui#scaleFactor +#define LV2_UI__foregroundColor LV2_UI_PREFIX "foregroundColor" ///< http://lv2plug.in/ns/extensions/ui#foregroundColor +#define LV2_UI__backgroundColor LV2_UI_PREFIX "backgroundColor" ///< http://lv2plug.in/ns/extensions/ui#backgroundColor + +// clang-format on + +/** + The index returned by LV2UI_Port_Map::port_index() for unknown ports. +*/ +#define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + A pointer to some widget or other type of UI handle. + + The actual type is defined by the type of the UI. +*/ +typedef void* LV2UI_Widget; + +/** + A pointer to UI instance internals. + + The host may compare this to NULL, but otherwise MUST NOT interpret it. +*/ +typedef void* LV2UI_Handle; + +/** + A pointer to a controller provided by the host. + + The UI may compare this to NULL, but otherwise MUST NOT interpret it. +*/ +typedef void* LV2UI_Controller; + +/** + A pointer to opaque data for a feature. +*/ +typedef void* LV2UI_Feature_Handle; + +/** + A host-provided function that sends data to a plugin's input ports. + + @param controller The opaque controller pointer passed to + LV2UI_Descriptor::instantiate(). + + @param port_index Index of the port to update. + + @param buffer Buffer containing `buffer_size` bytes of data. + + @param buffer_size Size of `buffer` in bytes. + + @param port_protocol Either 0 or the URID for a ui:PortProtocol. If 0, the + protocol is implicitly ui:floatProtocol, the port MUST be an lv2:ControlPort + input, `buffer` MUST point to a single float value, and `buffer_size` MUST + be sizeof(float). The UI SHOULD NOT use a protocol not supported by the + host, but the host MUST gracefully ignore any protocol it does not + understand. +*/ +typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t port_protocol, + const void* buffer); + +/** + A plugin UI. + + A pointer to an object of this type is returned by the lv2ui_descriptor() + function. +*/ +typedef struct LV2UI_Descriptor { + /** + The URI for this UI (not for the plugin it controls). + */ + const char* URI; + + /** + Create a new UI and return a handle to it. This function works + similarly to LV2_Descriptor::instantiate(). + + @param descriptor The descriptor for the UI to instantiate. + + @param plugin_uri The URI of the plugin that this UI will control. + + @param bundle_path The path to the bundle containing this UI, including + the trailing directory separator. + + @param write_function A function that the UI can use to send data to the + plugin's input ports. + + @param controller A handle for the UI instance to be passed as the + first parameter of UI methods. + + @param widget (output) widget pointer. The UI points this at its main + widget, which has the type defined by the UI type in the data file. + + @param features An array of LV2_Feature pointers. The host must pass + all feature URIs that it and the UI supports and any additional data, as + in LV2_Descriptor::instantiate(). Note that UI features and plugin + features are not necessarily the same. + + */ + LV2UI_Handle (*instantiate)(const struct LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features); + + /** + Destroy the UI. The host must not try to access the widget after + calling this function. + */ + void (*cleanup)(LV2UI_Handle ui); + + /** + Tell the UI that something interesting has happened at a plugin port. + + What is "interesting" and how it is written to `buffer` is defined by + `format`, which has the same meaning as in LV2UI_Write_Function(). + Format 0 is a special case for lv2:ControlPort, where this function + should be called when the port value changes (but not necessarily for + every change), `buffer_size` must be sizeof(float), and `buffer` + points to a single IEEE-754 float. + + By default, the host should only call this function for lv2:ControlPort + inputs. However, the UI can request updates for other ports statically + with ui:portNotification or dynamically with ui:portSubscribe. + + The UI MUST NOT retain any reference to `buffer` after this function + returns, it is only valid for the duration of the call. + + This member may be NULL if the UI is not interested in any port events. + */ + void (*port_event)(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + /** + Return a data structure associated with an extension URI, typically an + interface struct with additional function pointers + + This member may be set to NULL if the UI is not interested in supporting + any extensions. This is similar to LV2_Descriptor::extension_data(). + + */ + const void* (*extension_data)(const char* uri); +} LV2UI_Descriptor; + +/** + Feature/interface for resizable UIs (LV2_UI__resize). + + This structure is used in two ways: as a feature passed by the host via + LV2UI_Descriptor::instantiate(), or as an interface provided by a UI via + LV2UI_Descriptor::extension_data()). +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Request/advertise a size change. + + When provided by the host, the UI may call this function to inform the + host about the size of the UI. + + When provided by the UI, the host may call this function to notify the + UI that it should change its size accordingly. In this case, the host + must pass the LV2UI_Handle to provide access to the UI instance. + + @return 0 on success. + */ + int (*ui_resize)(LV2UI_Feature_Handle handle, int width, int height); +} LV2UI_Resize; + +/** + Feature to map port symbols to UIs. + + This can be used by the UI to get the index for a port with the given + symbol. This makes it possible to implement and distribute a UI separately + from the plugin (since symbol, unlike index, is a stable port identifier). +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to port_index(). + */ + LV2UI_Feature_Handle handle; + + /** + Get the index for the port with the given `symbol`. + + @return The index of the port, or LV2UI_INVALID_PORT_INDEX if no such + port is found. + */ + uint32_t (*port_index)(LV2UI_Feature_Handle handle, const char* symbol); +} LV2UI_Port_Map; + +/** + Feature to subscribe to port updates (LV2_UI__portSubscribe). +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to subscribe() and + unsubscribe(). + */ + LV2UI_Feature_Handle handle; + + /** + Subscribe to updates for a port. + + This means that the host will call the UI's port_event() function when + the port value changes (as defined by protocol). + + Calling this function with the same `port_index` and `port_protocol` + as an already active subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*subscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); + + /** + Unsubscribe from updates for a port. + + This means that the host will cease calling calling port_event() when + the port value changes. + + Calling this function with a `port_index` and `port_protocol` that + does not refer to an active port subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*unsubscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); +} LV2UI_Port_Subscribe; + +/** + A feature to notify the host that the user has grabbed a UI control. +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to touch(). + */ + LV2UI_Feature_Handle handle; + + /** + Notify the host that a control has been grabbed or released. + + The host should cease automating the port or otherwise manipulating the + port value until the control has been ungrabbed. + + @param handle The handle field of this struct. + @param port_index The index of the port associated with the control. + @param grabbed If true, the control has been grabbed, otherwise the + control has been released. + */ + void (*touch)(LV2UI_Feature_Handle handle, uint32_t port_index, bool grabbed); +} LV2UI_Touch; + +/** + A status code for LV2UI_Request_Value::request(). +*/ +typedef enum { + /** + Completed successfully. + + The host will set the parameter later if the user chooses a new value. + */ + LV2UI_REQUEST_VALUE_SUCCESS, + + /** + Parameter already being requested. + + The host is already requesting a parameter from the user (for example, a + dialog is visible), or the UI is otherwise busy and can not make this + request. + */ + LV2UI_REQUEST_VALUE_BUSY, + + /** + Unknown parameter. + + The host is not aware of this parameter, and is not able to set a new + value for it. + */ + LV2UI_REQUEST_VALUE_ERR_UNKNOWN, + + /** + Unsupported parameter. + + The host knows about this parameter, but does not support requesting a + new value for it from the user. This is likely because the host does + not have UI support for choosing a value with the appropriate type. + */ + LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED +} LV2UI_Request_Value_Status; + +/** + A feature to request a new parameter value from the host. +*/ +typedef struct { + /** + Pointer to opaque data which must be passed to request(). + */ + LV2UI_Feature_Handle handle; + + /** + Request a value for a parameter from the host. + + This is mainly used by UIs to request values for complex parameters that + don't change often, such as file paths, but it may be used to request + any parameter value. + + This function returns immediately, and the return value indicates + whether the host can fulfil the request. The host may notify the + plugin about the new parameter value, for example when a file is + selected by the user, via the usual mechanism. Typically, the host will + send a message to the plugin that sets the new parameter value, and the + plugin will notify the UI via a message as usual for any other parameter + change. + + To provide an appropriate UI, the host can determine details about the + parameter from the plugin data as usual. The additional parameters of + this function provide support for more advanced use cases, but in the + simple common case, the plugin will simply pass the key of the desired + parameter and zero for everything else. + + @param handle The handle field of this struct. + + @param key The URID of the parameter. + + @param type The optional type of the value to request. This can be used + to request a specific value type for parameters that support several. + If non-zero, it must be the URID of an instance of rdfs:Class or + rdfs:Datatype. + + @param features Additional features for this request, or NULL. + + @return A status code which is 0 on success. + */ + LV2UI_Request_Value_Status (*request)(LV2UI_Feature_Handle handle, + LV2_URID key, + LV2_URID type, + const LV2_Feature* const* features); + +} LV2UI_Request_Value; + +/** + UI Idle Interface (LV2_UI__idleInterface) + + UIs can provide this interface to have an idle() callback called by the host + rapidly to update the UI. +*/ +typedef struct { + /** + Run a single iteration of the UI's idle loop. + + This will be called rapidly in the UI thread at a rate appropriate + for a toolkit main loop. There are no precise timing guarantees, but + the host should attempt to call idle() at a high enough rate for smooth + animation, at least 30Hz. + + @return non-zero if the UI has been closed, in which case the host + should stop calling idle(), and can either completely destroy the UI, or + re-show it and resume calling idle(). + */ + int (*idle)(LV2UI_Handle ui); +} LV2UI_Idle_Interface; + +/** + UI Show Interface (LV2_UI__showInterface) + + UIs can provide this interface to show and hide a window, which allows them + to function in hosts unable to embed their widget. This allows any UI to + provide a fallback for embedding that works in any host. + + If used: + - The host MUST use LV2UI_Idle_Interface to drive the UI. + - The UI MUST return non-zero from LV2UI_Idle_Interface::idle() when it has + been closed. + - If idle() returns non-zero, the host MUST call hide() and stop calling + idle(). It MAY later call show() then resume calling idle(). +*/ +typedef struct { + /** + Show a window for this UI. + + The window title MAY have been passed by the host to + LV2UI_Descriptor::instantiate() as an LV2_Options_Option with key + LV2_UI__windowTitle. + + @return 0 on success, or anything else to stop being called. + */ + int (*show)(LV2UI_Handle ui); + + /** + Hide the window for this UI. + + @return 0 on success, or anything else to stop being called. + */ + int (*hide)(LV2UI_Handle ui); +} LV2UI_Show_Interface; + +/** + Peak data for a slice of time, the update format for ui:peakProtocol. +*/ +typedef struct { + /** + The start of the measurement period. This is just a running counter + that is only meaningful in comparison to previous values and must not be + interpreted as an absolute time. + */ + uint32_t period_start; + + /** + The size of the measurement period, in the same units as period_start. + */ + uint32_t period_size; + + /** + The peak value for the measurement period. This should be the maximal + value for abs(sample) over all the samples in the period. + */ + float peak; +} LV2UI_Peak_Data; + +/** + Prototype for UI accessor function. + + This is the entry point to a UI library, which works in the same way as + lv2_descriptor() but for UIs rather than plugins. +*/ +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor* +lv2ui_descriptor(uint32_t index); + +/** + The type of the lv2ui_descriptor() function. +*/ +typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); + +#ifdef __cplusplus +} +#endif + +/** + @} +*/ + +#endif /* LV2_UI_H */ diff --git a/plugins/lv2/vendor/lv2/units/units.h b/plugins/lv2/vendor/lv2/units/units.h new file mode 100644 index 0000000..0b8d416 --- /dev/null +++ b/plugins/lv2/vendor/lv2/units/units.h @@ -0,0 +1,62 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_UNITS_H +#define LV2_UNITS_H + +/** + @defgroup units Units + @ingroup lv2 + + Units for LV2 values. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_UNITS_URI "http://lv2plug.in/ns/extensions/units" ///< http://lv2plug.in/ns/extensions/units +#define LV2_UNITS_PREFIX LV2_UNITS_URI "#" ///< http://lv2plug.in/ns/extensions/units# + +#define LV2_UNITS__Conversion LV2_UNITS_PREFIX "Conversion" ///< http://lv2plug.in/ns/extensions/units#Conversion +#define LV2_UNITS__Unit LV2_UNITS_PREFIX "Unit" ///< http://lv2plug.in/ns/extensions/units#Unit +#define LV2_UNITS__bar LV2_UNITS_PREFIX "bar" ///< http://lv2plug.in/ns/extensions/units#bar +#define LV2_UNITS__beat LV2_UNITS_PREFIX "beat" ///< http://lv2plug.in/ns/extensions/units#beat +#define LV2_UNITS__bpm LV2_UNITS_PREFIX "bpm" ///< http://lv2plug.in/ns/extensions/units#bpm +#define LV2_UNITS__cent LV2_UNITS_PREFIX "cent" ///< http://lv2plug.in/ns/extensions/units#cent +#define LV2_UNITS__cm LV2_UNITS_PREFIX "cm" ///< http://lv2plug.in/ns/extensions/units#cm +#define LV2_UNITS__coef LV2_UNITS_PREFIX "coef" ///< http://lv2plug.in/ns/extensions/units#coef +#define LV2_UNITS__conversion LV2_UNITS_PREFIX "conversion" ///< http://lv2plug.in/ns/extensions/units#conversion +#define LV2_UNITS__db LV2_UNITS_PREFIX "db" ///< http://lv2plug.in/ns/extensions/units#db +#define LV2_UNITS__degree LV2_UNITS_PREFIX "degree" ///< http://lv2plug.in/ns/extensions/units#degree +#define LV2_UNITS__frame LV2_UNITS_PREFIX "frame" ///< http://lv2plug.in/ns/extensions/units#frame +#define LV2_UNITS__hz LV2_UNITS_PREFIX "hz" ///< http://lv2plug.in/ns/extensions/units#hz +#define LV2_UNITS__inch LV2_UNITS_PREFIX "inch" ///< http://lv2plug.in/ns/extensions/units#inch +#define LV2_UNITS__khz LV2_UNITS_PREFIX "khz" ///< http://lv2plug.in/ns/extensions/units#khz +#define LV2_UNITS__km LV2_UNITS_PREFIX "km" ///< http://lv2plug.in/ns/extensions/units#km +#define LV2_UNITS__m LV2_UNITS_PREFIX "m" ///< http://lv2plug.in/ns/extensions/units#m +#define LV2_UNITS__mhz LV2_UNITS_PREFIX "mhz" ///< http://lv2plug.in/ns/extensions/units#mhz +#define LV2_UNITS__midiNote LV2_UNITS_PREFIX "midiNote" ///< http://lv2plug.in/ns/extensions/units#midiNote +#define LV2_UNITS__mile LV2_UNITS_PREFIX "mile" ///< http://lv2plug.in/ns/extensions/units#mile +#define LV2_UNITS__min LV2_UNITS_PREFIX "min" ///< http://lv2plug.in/ns/extensions/units#min +#define LV2_UNITS__mm LV2_UNITS_PREFIX "mm" ///< http://lv2plug.in/ns/extensions/units#mm +#define LV2_UNITS__ms LV2_UNITS_PREFIX "ms" ///< http://lv2plug.in/ns/extensions/units#ms +#define LV2_UNITS__name LV2_UNITS_PREFIX "name" ///< http://lv2plug.in/ns/extensions/units#name +#define LV2_UNITS__oct LV2_UNITS_PREFIX "oct" ///< http://lv2plug.in/ns/extensions/units#oct +#define LV2_UNITS__pc LV2_UNITS_PREFIX "pc" ///< http://lv2plug.in/ns/extensions/units#pc +#define LV2_UNITS__prefixConversion LV2_UNITS_PREFIX "prefixConversion" ///< http://lv2plug.in/ns/extensions/units#prefixConversion +#define LV2_UNITS__render LV2_UNITS_PREFIX "render" ///< http://lv2plug.in/ns/extensions/units#render +#define LV2_UNITS__s LV2_UNITS_PREFIX "s" ///< http://lv2plug.in/ns/extensions/units#s +#define LV2_UNITS__semitone12TET LV2_UNITS_PREFIX "semitone12TET" ///< http://lv2plug.in/ns/extensions/units#semitone12TET +#define LV2_UNITS__symbol LV2_UNITS_PREFIX "symbol" ///< http://lv2plug.in/ns/extensions/units#symbol +#define LV2_UNITS__unit LV2_UNITS_PREFIX "unit" ///< http://lv2plug.in/ns/extensions/units#unit + +// clang-format on + +/** + @} +*/ + +#endif /* LV2_UNITS_H */ diff --git a/plugins/lv2/vendor/lv2/uri-map/uri-map.h b/plugins/lv2/vendor/lv2/uri-map/uri-map.h new file mode 100644 index 0000000..0c683d0 --- /dev/null +++ b/plugins/lv2/vendor/lv2/uri-map/uri-map.h @@ -0,0 +1,108 @@ +// Copyright 2008-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_URI_MAP_H +#define LV2_URI_MAP_H + +/** + @defgroup uri-map URI Map + @ingroup lv2 + + A feature for mapping URIs to integers. + + This extension defines a simple mechanism for plugins to map URIs to + integers, usually for performance reasons (e.g. processing events typed by + URIs in real time). The expected use case is for plugins to map URIs to + integers for things they 'understand' at instantiation time, and store those + values for use in the audio thread without doing any string comparison. + This allows the extensibility of RDF with the performance of integers (or + centrally defined enumerations). + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" ///< http://lv2plug.in/ns/ext/uri-map +#define LV2_URI_MAP_PREFIX LV2_URI_MAP_URI "#" ///< http://lv2plug.in/ns/ext/uri-map# + +// clang-format on + +#include "lv2/core/attributes.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +LV2_DISABLE_DEPRECATION_WARNINGS + +/** + Opaque pointer to host data. +*/ +LV2_DEPRECATED +typedef void* LV2_URI_Map_Callback_Data; + +/** + URI Map Feature. + + To support this feature the host must pass an LV2_Feature struct to the + plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" + and data pointed to an instance of this struct. +*/ +LV2_DEPRECATED +typedef struct { + /** + Opaque pointer to host data. + + The plugin MUST pass this to any call to functions in this struct. + Otherwise, it must not be interpreted in any way. + */ + LV2_URI_Map_Callback_Data callback_data; + + /** + Get the numeric ID of a URI from the host. + + @param callback_data Must be the callback_data member of this struct. + @param map The 'context' of this URI. Certain extensions may define a + URI that must be passed here with certain restrictions on the return + value (e.g. limited range). This value may be NULL if the plugin needs + an ID for a URI in general. Extensions SHOULD NOT define a context + unless there is a specific need to do so, e.g. to restrict the range of + the returned value. + @param uri The URI to be mapped to an integer ID. + + This function is referentially transparent; any number of calls with the + same arguments is guaranteed to return the same value over the life of a + plugin instance (though the same URI may return different values with a + different map parameter). However, this function is not necessarily very + fast: plugins SHOULD cache any IDs they might need in performance + critical situations. + + The return value 0 is reserved and indicates that an ID for that URI + could not be created for whatever reason. Extensions MAY define more + precisely what this means in a certain context, but in general plugins + SHOULD handle this situation as gracefully as possible. However, hosts + SHOULD NOT return 0 from this function in non-exceptional circumstances + (e.g. the URI map SHOULD be dynamic). Hosts that statically support only + a fixed set of URIs should not expect plugins to function correctly. + */ + uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); +} LV2_URI_Map_Feature; + +LV2_RESTORE_WARNINGS + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_URI_MAP_H */ diff --git a/plugins/lv2/vendor/lv2/urid/urid.h b/plugins/lv2/vendor/lv2/urid/urid.h new file mode 100644 index 0000000..5890307 --- /dev/null +++ b/plugins/lv2/vendor/lv2/urid/urid.h @@ -0,0 +1,127 @@ +// Copyright 2008-2016 David Robillard +// Copyright 2011 Gabriel M. Beddingfield +// SPDX-License-Identifier: ISC + +#ifndef LV2_URID_H +#define LV2_URID_H + +/** + @defgroup urid URID + @ingroup lv2 + + Features for mapping URIs to and from integers. + + See for details. + + @{ +*/ + +// clang-format off + +#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" ///< http://lv2plug.in/ns/ext/urid +#define LV2_URID_PREFIX LV2_URID_URI "#" ///< http://lv2plug.in/ns/ext/urid# + +#define LV2_URID__map LV2_URID_PREFIX "map" ///< http://lv2plug.in/ns/ext/urid#map +#define LV2_URID__unmap LV2_URID_PREFIX "unmap" ///< http://lv2plug.in/ns/ext/urid#unmap + +#define LV2_URID_MAP_URI LV2_URID__map ///< Legacy +#define LV2_URID_UNMAP_URI LV2_URID__unmap ///< Legacy + +// clang-format on + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Opaque pointer to host data for LV2_URID_Map. +*/ +typedef void* LV2_URID_Map_Handle; + +/** + Opaque pointer to host data for LV2_URID_Unmap. +*/ +typedef void* LV2_URID_Unmap_Handle; + +/** + URI mapped to an integer. +*/ +typedef uint32_t LV2_URID; + +/** + URID Map Feature (LV2_URID__map) +*/ +typedef struct { + /** + Opaque pointer to host data. + + This MUST be passed to map_uri() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Map_Handle handle; + + /** + Get the numeric ID of a URI. + + If the ID does not already exist, it will be created. + + This function is referentially transparent; any number of calls with the + same arguments is guaranteed to return the same value over the life of a + plugin instance. Note, however, that several URIs MAY resolve to the + same ID if the host considers those URIs equivalent. + + This function is not necessarily very fast or RT-safe: plugins SHOULD + cache any IDs they might need in performance critical situations. + + The return value 0 is reserved and indicates that an ID for that URI + could not be created for whatever reason. However, hosts SHOULD NOT + return 0 from this function in non-exceptional circumstances (i.e. the + URI map SHOULD be dynamic). + + @param handle Must be the callback_data member of this struct. + @param uri The URI to be mapped to an integer ID. + */ + LV2_URID (*map)(LV2_URID_Map_Handle handle, const char* uri); +} LV2_URID_Map; + +/** + URI Unmap Feature (LV2_URID__unmap) +*/ +typedef struct { + /** + Opaque pointer to host data. + + This MUST be passed to unmap() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Unmap_Handle handle; + + /** + Get the URI for a previously mapped numeric ID. + + Returns NULL if `urid` is not yet mapped. Otherwise, the corresponding + URI is returned in a canonical form. This MAY not be the exact same + string that was originally passed to LV2_URID_Map::map(), but it MUST be + an identical URI according to the URI syntax specification (RFC3986). A + non-NULL return for a given `urid` will always be the same for the life + of the plugin. Plugins that intend to perform string comparison on + unmapped URIs SHOULD first canonicalise URI strings with a call to + map_uri() followed by a call to unmap_uri(). + + @param handle Must be the callback_data member of this struct. + @param urid The ID to be mapped back to the URI string. + */ + const char* (*unmap)(LV2_URID_Unmap_Handle handle, LV2_URID urid); +} LV2_URID_Unmap; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_URID_H */ diff --git a/plugins/lv2/vendor/lv2/worker/worker.h b/plugins/lv2/vendor/lv2/worker/worker.h new file mode 100644 index 0000000..0b0e792 --- /dev/null +++ b/plugins/lv2/vendor/lv2/worker/worker.h @@ -0,0 +1,170 @@ +// Copyright 2012-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_WORKER_H +#define LV2_WORKER_H + +/** + @defgroup worker Worker + @ingroup lv2 + + Support for non-realtime plugin operations. + + See for details. + + @{ +*/ + +#include "lv2/core/lv2.h" + +#include + +// clang-format off + +#define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker" ///< http://lv2plug.in/ns/ext/worker +#define LV2_WORKER_PREFIX LV2_WORKER_URI "#" ///< http://lv2plug.in/ns/ext/worker# + +#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface" ///< http://lv2plug.in/ns/ext/worker#interface +#define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule" ///< http://lv2plug.in/ns/ext/worker#schedule + +// clang-format on + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Status code for worker functions. +*/ +typedef enum { + LV2_WORKER_SUCCESS = 0, /**< Completed successfully. */ + LV2_WORKER_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_WORKER_ERR_NO_SPACE = 2 /**< Failed due to lack of space. */ +} LV2_Worker_Status; + +/** Opaque handle for LV2_Worker_Interface::work(). */ +typedef void* LV2_Worker_Respond_Handle; + +/** + A function to respond to run() from the worker method. + + The `data` MUST be safe for the host to copy and later pass to + work_response(), and the host MUST guarantee that it will be eventually + passed to work_response() if this function returns LV2_WORKER_SUCCESS. +*/ +typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)( + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data); + +/** + Plugin Worker Interface. + + This is the interface provided by the plugin to implement a worker method. + The plugin's extension_data() method should return an LV2_Worker_Interface + when called with LV2_WORKER__interface as its argument. +*/ +typedef struct { + /** + The worker method. This is called by the host in a non-realtime context + as requested, possibly with an arbitrary message to handle. + + A response can be sent to run() using `respond`. The plugin MUST NOT + make any assumptions about which thread calls this method, except that + there are no real-time requirements and only one call may be executed at + a time. That is, the host MAY call this method from any non-real-time + thread, but MUST NOT make concurrent calls to this method from several + threads. + + @param instance The LV2 instance this is a method on. + @param respond A function for sending a response to run(). + @param handle Must be passed to `respond` if it is called. + @param size The size of `data`. + @param data Data from run(), or NULL. + */ + LV2_Worker_Status (*work)(LV2_Handle instance, + LV2_Worker_Respond_Function respond, + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data); + + /** + Handle a response from the worker. This is called by the host in the + run() context when a response from the worker is ready. + + @param instance The LV2 instance this is a method on. + @param size The size of `body`. + @param body Message body, or NULL. + */ + LV2_Worker_Status (*work_response)(LV2_Handle instance, + uint32_t size, + const void* body); + + /** + Called when all responses for this cycle have been delivered. + + Since work_response() may be called after run() finished, this provides + a hook for code that must run after the cycle is completed. + + This field may be NULL if the plugin has no use for it. Otherwise, the + host MUST call it after every run(), regardless of whether or not any + responses were sent that cycle. + */ + LV2_Worker_Status (*end_run)(LV2_Handle instance); +} LV2_Worker_Interface; + +/** Opaque handle for LV2_Worker_Schedule. */ +typedef void* LV2_Worker_Schedule_Handle; + +/** + Schedule Worker Host Feature. + + The host passes this feature to provide a schedule_work() function, which + the plugin can use to schedule a worker call from run(). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_Worker_Schedule_Handle handle; + + /** + Request from run() that the host call the worker. + + This function is in the audio threading class. It should be called from + run() to request that the host call the work() method in a non-realtime + context with the given arguments. + + This function is always safe to call from run(), but it is not + guaranteed that the worker is actually called from a different thread. + In particular, when free-wheeling (for example, during offline + rendering), the worker may be executed immediately. This allows + single-threaded processing with sample accuracy and avoids timing + problems when run() is executing much faster or slower than real-time. + + Plugins SHOULD be written in such a way that if the worker runs + immediately, and responses from the worker are delivered immediately, + the effect of the work takes place immediately with sample accuracy. + + The `data` MUST be safe for the host to copy and later pass to work(), + and the host MUST guarantee that it will be eventually passed to work() + if this function returns LV2_WORKER_SUCCESS. + + @param handle The handle field of this struct. + @param size The size of `data`. + @param data Message to pass to work(), or NULL. + */ + LV2_Worker_Status (*schedule_work)(LV2_Worker_Schedule_Handle handle, + uint32_t size, + const void* data); +} LV2_Worker_Schedule; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/** + @} +*/ + +#endif /* LV2_WORKER_H */ diff --git a/plugins/lv2/vstgui_helpers.cpp b/plugins/lv2/vstgui_helpers.cpp new file mode 100644 index 0000000..02a2b4d --- /dev/null +++ b/plugins/lv2/vstgui_helpers.cpp @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +// Note(jpc) same code as used in Surge LV2, I am the original author + +#include "vstgui_helpers.h" +#include +#include +#include +#include +#include +#if LINUX +#include +#include +#endif +#if WINDOWS +#include +#endif +#if MAC +#include "vstgui/plugin-bindings/getpluginbundle.h" +#endif +#include "vstgui/lib/vstguiinit.h" + +#if LINUX +void Lv2IdleRunLoop::execIdle() +{ + std::chrono::steady_clock::time_point tick = std::chrono::steady_clock::now(); + + for (Event& ev : _events) + { + if (!ev.alive) + continue; + +// TODO LV2: fix me, XCB descriptor polling not working at this point +#if 0 + pollfd pfd = {}; + pfd.fd = ev.fd; + pfd.events = POLLIN|POLLERR|POLLHUP; + if (poll(&pfd, 1, 0) > 0) +#endif + { + ev.handler->onEvent(); + } + } + + for (Timer& tm : _timers) + { + if (!tm.alive) + continue; + + if (tm.lastTickValid) + { + std::chrono::steady_clock::duration duration = tick - tm.lastTick; + tm.counter += std::chrono::duration_cast(duration); + if (tm.counter >= tm.interval) + { + tm.handler->onTimer(); + tm.counter = std::min(tm.counter - tm.interval, tm.interval); + } + } + tm.lastTick = tick; + tm.lastTickValid = true; + } + + garbageCollectDeadHandlers(_events); + garbageCollectDeadHandlers(_timers); +} + +bool Lv2IdleRunLoop::registerEventHandler(int fd, VSTGUI::X11::IEventHandler* handler) +{ + // fprintf(stderr, "registerEventHandler %d %p\n", fd, handler); + + Event ev; + ev.fd = fd; + ev.handler = handler; + ev.alive = true; + _events.push_back(ev); + + return true; +} + +bool Lv2IdleRunLoop::unregisterEventHandler(VSTGUI::X11::IEventHandler* handler) +{ + // fprintf(stderr, "unregisterEventHandler %p\n", handler); + + auto it = std::find_if(_events.begin(), _events.end(), [handler](const Event& ev) -> bool { + return ev.handler == handler && ev.alive; + }); + + if (it != _events.end()) + it->alive = false; + + return true; +} + +bool Lv2IdleRunLoop::registerTimer(uint64_t interval, VSTGUI::X11::ITimerHandler* handler) +{ + // fprintf(stderr, "registerTimer %lu %p\n", interval, handler); + + Timer tm; + tm.interval = std::chrono::milliseconds(interval); + tm.counter = std::chrono::microseconds(0); + tm.lastTickValid = false; + tm.handler = handler; + tm.alive = true; + _timers.push_back(tm); + + return true; +} + +bool Lv2IdleRunLoop::unregisterTimer(VSTGUI::X11::ITimerHandler* handler) +{ + // fprintf(stderr, "unregisterTimer %p\n", handler); + + auto it = std::find_if(_timers.begin(), _timers.end(), [handler](const Timer& tm) -> bool { + return tm.handler == handler && tm.alive; + }); + + if (it != _timers.end()) + it->alive = false; + + return true; +} + +template void Lv2IdleRunLoop::garbageCollectDeadHandlers(std::list& handlers) +{ + auto pos = handlers.begin(); + auto end = handlers.end(); + + while (pos != end) + { + auto curPos = pos++; + if (!curPos->alive) + handlers.erase(curPos); + } +} +#endif + +/// +#if LINUX +namespace VSTGUI +{ +void* soHandle = nullptr; + +static volatile size_t soHandleCount = 0; +static std::mutex soHandleMutex; + +SoHandleInitializer::SoHandleInitializer() +{ + std::lock_guard lock(soHandleMutex); + if (soHandleCount++ == 0) { + Dl_info info; + if (dladdr((void*)&lv2ui_descriptor, &info)) + soHandle = dlopen(info.dli_fname, RTLD_LAZY); + if (!soHandle) + throw std::runtime_error("SoHandleInitializer"); + } +} + +SoHandleInitializer::~SoHandleInitializer() +{ + std::lock_guard lock(soHandleMutex); + if (--soHandleCount == 0) { + dlclose(soHandle); + soHandle = nullptr; + } +} + +} // namespace VSTGUI +#endif + +/// +#if WINDOWS +void* hInstance = nullptr; + +__declspec(dllexport) +BOOL WINAPI DllMain(HINSTANCE dllInstance, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) + hInstance = dllInstance; + return TRUE; +} +#endif + +/// +#if MAC +namespace VSTGUI +{ +void* gBundleRef = nullptr; + +static volatile size_t gBundleRefCount = 0; +static std::mutex gBundleRefMutex; + +BundleRefInitializer::BundleRefInitializer() +{ + std::lock_guard lock(gBundleRefMutex); + if (gBundleRefCount++ == 0) { + gBundleRef = GetPluginBundle(); + if (!gBundleRef) + throw std::runtime_error("BundleRefInitializer"); + } +} + +BundleRefInitializer::~BundleRefInitializer() +{ + std::lock_guard lock(gBundleRefMutex); + if (--gBundleRefCount == 0) { + CFRelease((CFBundleRef)gBundleRef); + gBundleRef = nullptr; + } +} + +} // namespace VSTGUI +#endif + +namespace VSTGUI { + +static volatile size_t gVstguiInitCount = 0; +static std::mutex gVstguiInitMutex; + +VSTGUIInitializer::VSTGUIInitializer() +{ + std::lock_guard lock(gVstguiInitMutex); + if (gVstguiInitCount++ == 0) { +#if WINDOWS + VSTGUI::init((HINSTANCE)hInstance); +#elif MAC + VSTGUI::init((CFBundleRef)gBundleRef); +#else + VSTGUI::init(soHandle); +#endif + } +} + +VSTGUIInitializer::~VSTGUIInitializer() +{ + std::lock_guard lock(gVstguiInitMutex); + if (--gVstguiInitCount == 0) { + VSTGUI::exit(); + } +} + +} // namespace VSTGUI diff --git a/plugins/lv2/vstgui_helpers.h b/plugins/lv2/vstgui_helpers.h new file mode 100644 index 0000000..165ee45 --- /dev/null +++ b/plugins/lv2/vstgui_helpers.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +// Note(jpc) same code as used in Surge LV2, I am the original author + +#pragma once +#include +#include + +#include "editor/utility/vstgui_before.h" +#include "vstgui/lib/vstguibase.h" +#if LINUX +#include "vstgui/lib/platform/platform_x11.h" +#include "vstgui/lib/platform/linux/x11platform.h" +#endif +#include "editor/utility/vstgui_after.h" + +#if LINUX +class Lv2IdleRunLoop : public VSTGUI::X11::IRunLoop +{ +public: + void execIdle(); + + bool registerEventHandler(int fd, VSTGUI::X11::IEventHandler* handler) override; + bool unregisterEventHandler(VSTGUI::X11::IEventHandler* handler) override; + bool registerTimer(uint64_t interval, VSTGUI::X11::ITimerHandler* handler) override; + bool unregisterTimer(VSTGUI::X11::ITimerHandler* handler) override; + + void forget() override + {} + void remember() override + {} + +private: + struct Event + { + int fd; + VSTGUI::X11::IEventHandler* handler; + bool alive; + }; + struct Timer + { + std::chrono::microseconds interval; + std::chrono::microseconds counter; + bool lastTickValid; + std::chrono::steady_clock::time_point lastTick; + VSTGUI::X11::ITimerHandler* handler; + bool alive; + }; + +private: + template static void garbageCollectDeadHandlers(std::list& handlers); + +private: + std::list _events; + std::list _timers; +}; +#endif + +#if LINUX +namespace VSTGUI +{ +class SoHandleInitializer { +public: + SoHandleInitializer(); + ~SoHandleInitializer(); +private: + SoHandleInitializer(const SoHandleInitializer&) = delete; + SoHandleInitializer& operator=(const SoHandleInitializer&) = delete; +}; +} +#endif + +#if MAC +namespace VSTGUI +{ +class BundleRefInitializer { +public: + BundleRefInitializer(); + ~BundleRefInitializer(); +private: + BundleRefInitializer(const BundleRefInitializer&) = delete; + BundleRefInitializer& operator=(const BundleRefInitializer&) = delete; +}; +} +#endif + +namespace VSTGUI +{ +class VSTGUIInitializer { +public: + VSTGUIInitializer(); + ~VSTGUIInitializer(); +private: + VSTGUIInitializer(const VSTGUIInitializer&) = delete; + VSTGUIInitializer& operator=(const VSTGUIInitializer&) = delete; +}; +} diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt new file mode 100644 index 0000000..0177a75 --- /dev/null +++ b/plugins/puredata/CMakeLists.txt @@ -0,0 +1,57 @@ +# Puredata plugin specific settings +include(PuredataConfig) + +set(PUREDATA_BINARY_DIR "${PROJECT_BINARY_DIR}/pd/sfizz") + +set(PUREDATA_RESOURCES + "sfizz~-help.pd" + "example.sfz") + +function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) + set(_deps) + foreach(res ${PUREDATA_RESOURCES}) + get_filename_component(_dir "${res}" DIRECTORY) + file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}") + add_custom_command( + OUTPUT "${DESTINATION_DIR}/${res}" + COMMAND "${CMAKE_COMMAND}" "-E" "copy" + "${SOURCE_DIR}/${res}" "${DESTINATION_DIR}/${res}" + DEPENDS "${SOURCE_DIR}/${res}") + list(APPEND _deps "${DESTINATION_DIR}/${res}") + endforeach() + add_custom_target("${TARGET}_puredata_resources" DEPENDS ${_deps}) + add_dependencies("${TARGET}" "${TARGET}_puredata_resources") +endfunction() + +add_pd_external(sfizz_puredata "sfizz_puredata.c") +target_compile_definitions(sfizz_puredata PRIVATE + "MIDI_CC_MAX=${MIDI_CC_MAX}" + "SFIZZ_VERSION=\"${CMAKE_PROJECT_VERSION}\"") +target_link_libraries(sfizz_puredata PRIVATE sfizz::import sfizz::sfizz) + +set_target_properties(sfizz_puredata PROPERTIES + OUTPUT_NAME "sfizz" + LIBRARY_OUTPUT_DIRECTORY "${PUREDATA_BINARY_DIR}/$<0:>") + +if(MINGW) + set_property(TARGET sfizz_puredata APPEND_STRING + PROPERTY LINK_FLAGS " -static") +endif() + +# Git build identifier +target_link_libraries(sfizz_puredata PRIVATE sfizz-git-build-id) + +# Copy resources +copy_puredata_resources(sfizz_puredata + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PUREDATA_BINARY_DIR}") + +# Installation +if(NOT MSVC) + install(DIRECTORY "${PUREDATA_BINARY_DIR}" DESTINATION "${PDPLUGIN_INSTALL_DIR}" + COMPONENT "puredata" + USE_SOURCE_PERMISSIONS) + bundle_dylibs(puredata + "${PDPLUGIN_INSTALL_DIR}/sfizz/sfizz${PUREDATA_SUFFIX}" + COMPONENT "puredata") +endif() diff --git a/plugins/puredata/example.sfz b/plugins/puredata/example.sfz new file mode 100644 index 0000000..6e64db9 --- /dev/null +++ b/plugins/puredata/example.sfz @@ -0,0 +1,22 @@ + +ampeg_attack=0.25 +ampeg_decay=5 +ampeg_sustain=50 +ampeg_release=2 +fil_type=lpf_4p +resonance=3 +cutoff=3000 +oscillator_phase=-1 +cutoff_oncc300=3600 +cutoff_curvecc300=1 + + +sample=*saw +oscillator_multi=3 +oscillator_detune=10 +lfo1_freq=5 +lfo1_pitch=30 + + +sample=*triangle +transpose=-12 diff --git a/plugins/puredata/external/pd/LICENSE.txt b/plugins/puredata/external/pd/LICENSE.txt new file mode 100644 index 0000000..a56a51e --- /dev/null +++ b/plugins/puredata/external/pd/LICENSE.txt @@ -0,0 +1,30 @@ +This software is copyrighted by Miller Puckette and others. The following +terms (the "Standard Improved BSD License") apply to all files associated with +the software unless explicitly disclaimed in individual files: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/puredata/external/pd/bin/pd.def b/plugins/puredata/external/pd/bin/pd.def new file mode 100644 index 0000000..607bd99 --- /dev/null +++ b/plugins/puredata/external/pd/bin/pd.def @@ -0,0 +1,601 @@ +LIBRARY pd +EXPORTS + ABORT + array_free + array_getcoordinate + array_getfields + array_new + array_redraw + array_resize + array_resize_and_redraw + atom_gensym + atom_getfloat + atom_getfloatarg + atom_getint + atom_getintarg + atom_getsymbol + atom_getsymbolarg + atom_string + audio_isopen + audio_shouldkeepopen + binbuf_add + binbuf_addbinbuf + binbuf_addsemi + binbuf_addv + binbuf_clear + binbuf_duplicate + binbuf_eval + binbuf_evalfile + binbuf_free + binbuf_getnatom + binbuf_gettext + binbuf_getvec + binbuf_new + binbuf_print + binbuf_read + binbuf_read_via_canvas + binbuf_read_via_path + binbuf_realizedollsym + binbuf_resize + binbuf_restore + binbuf_text + binbuf_write + bug + canvas_addinlet + canvas_addoutlet + canvas_class DATA + canvas_closebang + canvas_connect + canvas_create_editor + canvas_dataproperties + canvas_deletelinesfor + canvas_deletelinesforio + canvas_destroy_editor + canvas_dirty + canvas_disconnect + canvas_dspstate DATA + canvas_editmode + canvas_fixlinesfor + canvas_free + canvas_getargs + canvas_getcurrent + canvas_getcurrentdir + canvas_getdir + canvas_getdollarzero + canvas_getenv + canvas_getindex + canvas_getrootfor + canvas_hitbox + canvas_initbang + canvas_isabstraction + canvas_isconnected + canvas_istable + canvas_loadbang + canvas_makebindsym + canvas_makefilename + canvas_new + canvas_noundo + canvas_open + canvas_path_iterate + canvas_readscalar + canvas_realizedollar + canvas_redraw + canvas_redrawallfortemplate + canvas_redrawallfortemplatecanvas + canvas_rename + canvas_resortinlets + canvas_resortoutlets + canvas_restoreconnections + canvas_resume_dsp + canvas_rminlet + canvas_rmoutlet + canvas_selectinrect + canvas_setargs + canvas_setcurrent + canvas_setcursor + canvas_setdeleting + canvas_setundo + canvas_showtext + canvas_stowconnections + canvas_suspend_dsp + canvas_unsetcurrent + canvas_update_dsp + canvas_updatewindowlist + canvas_vis + canvas_whichfind DATA + canvas_writescalar + class_addanything + class_addbang + class_addcreator + class_addlist + class_addmethod + class_addpointer + class_addsymbol + class_doaddfloat + class_domainsignalin + class_gethelpdir + class_gethelpname + class_getname + class_getpropertiesfn + class_getsavefn + class_isdrawcommand + class_new + class_new64 + class_set_extern_dir + class_setdrawcommand + class_sethelpsymbol + class_setparentwidget + class_setpropertiesfn + class_setsavefn + class_setwidget + clock_delay + clock_free + clock_getlogicaltime + clock_getsystime + clock_getsystimeafter + clock_gettimesince + clock_gettimesincewithunits + clock_new + clock_set + clock_setunit + clock_unset + clone_class DATA + copy_perform + copybytes + cos_table DATA + dbtopow + dbtorms + dsp_add + dsp_add_copy + dsp_add_plus + dsp_add_scalarcopy + dsp_add_zero + dsp_addv + endpost + error + ex_Avg + ex_Sum + ex_avg + ex_funcs DATA + ex_getsym + ex_mkvector + ex_size + ex_store + ex_sum + ex_symname + fielddesc_cvtfromcoord + fielddesc_cvttocoord + fielddesc_getcoord + fielddesc_setcoord + floatinlet_new + freebytes + ftom + g_editor_freepdinstance + g_editor_newpdinstance + g_template_freepdinstance + g_template_newpdinstance + garray_class DATA + garray_getarray + garray_getfloatarray + garray_getfloatwords + garray_getglist + garray_npoints + garray_redraw + garray_resize + garray_resize_long + garray_setsaveit + garray_template + garray_usedindsp + garray_vec + gensym + get_sys_dacsr + get_sys_main_advance + get_sys_schedadvance + get_sys_schedblocksize + get_sys_sleepgrain + get_sys_soundin + get_sys_soundout + get_sys_time + get_sys_time_per_dsp_tick + getbytes + getfn + getzbytes + gfxstub_deleteforkey + gfxstub_new + glist_add + glist_addglist + glist_arraydialog + glist_clear + glist_delete + glist_deselect + glist_dpixtodx + glist_dpixtody + glist_drawiofor + glist_eraseiofor + glist_findgraph + glist_findrtext + glist_fontheight + glist_fontwidth + glist_getcanvas + glist_getfont + glist_getnextxy + glist_getzoom + glist_glist + glist_grab + glist_init + glist_isgraph + glist_isselected + glist_istoplevel + glist_isvisible + glist_mergefile + glist_noselect + glist_pixelstox + glist_pixelstoy + glist_read + glist_redraw + glist_retext + glist_select + glist_selectall + glist_sort + glist_valid DATA + glist_writetobinbuf + glist_xtopixels + glist_ytopixels + glob_evalfile + glob_initfromgui + glob_pdobject DATA + glob_quit + glob_setfilename + gobj_activate + gobj_click + gobj_delete + gobj_displace + gobj_getrect + gobj_save + gobj_select + gobj_shouldvis + gobj_vis + gpointer_check + gpointer_copy + gpointer_init + gpointer_setarray + gpointer_setglist + gpointer_unset + graph_array + gstub_cutoff + gstub_new + gtemplate_get + guiconnect_new + guiconnect_notarget + iem_fstyletoint + iem_inttofstyle + iem_inttosymargs + iem_symargstoint + iemgui_all_dollar2raute + iemgui_all_dollararg2sym + iemgui_all_loadcolors + iemgui_all_raute2dollar + iemgui_all_sym2dollararg + iemgui_clip_font + iemgui_clip_size + iemgui_color + iemgui_color_hex DATA + iemgui_delete + iemgui_delta + iemgui_dialog + iemgui_displace + iemgui_dollar2raute + iemgui_label + iemgui_label_font + iemgui_label_pos + iemgui_new_dogetname + iemgui_new_getnames + iemgui_newzoom + iemgui_pos + iemgui_properties + iemgui_raute2dollar + iemgui_receive + iemgui_save + iemgui_select + iemgui_send + iemgui_size + iemgui_verify_snd_ne_rcv + iemgui_vis + iemgui_vu_col DATA + iemgui_vu_db2i DATA + iemgui_vu_scale_str DATA + iemgui_zoom + ilog2 + inlet_free + inlet_new + inmidi_aftertouch + inmidi_byte + inmidi_controlchange + inmidi_noteon + inmidi_pitchbend + inmidi_polyaftertouch + inmidi_programchange + inmidi_realtimein + inmidi_sysex + linetraverser_next + linetraverser_skipobject + linetraverser_start + logpost + max_ex_tab + max_ex_tab_store + max_ex_var + max_ex_var_store + mayer_fft + mayer_fht + mayer_ifft + mayer_realfft + mayer_realifft + midi_getdevs + mmio_close_audio + mmio_getdevs + mmio_open_audio + mmio_reportidle + mmio_send_dacs + mtof + namelist_append + namelist_append_files + namelist_free + namelist_get + nullfn + obj_connect + obj_disconnect + obj_issignalinlet + obj_issignaloutlet + obj_list + obj_nexttraverseoutlet + obj_ninlets + obj_noutlets + obj_nsiginlets + obj_nsigoutlets + obj_saveformat + obj_siginletindex + obj_sigoutletindex + obj_starttraverseoutlet + open_via_helppath + open_via_path + outlet_anything + outlet_bang + outlet_float + outlet_free + outlet_getsymbol + outlet_list + outlet_new + outlet_pointer + outlet_setstacklim + outlet_symbol + pa_close_audio + pa_getdevs + pa_open_audio + pa_send_dacs + pd_bang + pd_bind + pd_canvasmaker DATA + pd_checkglist + pd_checkobject + pd_compatibilitylevel DATA + pd_emptylist + pd_error + pd_fft + pd_findbyclass + pd_float + pd_forwardmess + pd_free + pd_getcanvaslist + pd_getdspstate + pd_getparentwidget + pd_globallock + pd_globalunlock + pd_list + pd_maininstance DATA + pd_new + pd_newest + pd_objectmaker DATA + pd_pointer + pd_popsym + pd_pushsym + pd_symbol + pd_typedmess + pd_unbind + pd_vmess + plus_perform + pointerinlet_new + post + postatom + postfloat + poststring + powtodb + q8_rsqrt + q8_sqrt + qrsqrt + qsqrt + resample_dsp + resample_free + resample_init + resamplefrom_dsp + resampleto_dsp + resizebytes + rmstodb + rtext_activate + rtext_displace + rtext_draw + rtext_erase + rtext_free + rtext_getseltext + rtext_gettag + rtext_gettext + rtext_height + rtext_key + rtext_mouse + rtext_new + rtext_retext + rtext_select + rtext_width + s_ DATA + s__N DATA + s__X DATA + s_anything DATA + s_bang DATA + s_float DATA + s_list DATA + s_pointer DATA + s_signal DATA + s_symbol DATA + s_x DATA + s_y DATA + scalar_class DATA + scalar_getbasexy + scalar_new + scalar_redraw + sched_geteventno + sched_set_using_audio + sched_tick + signalinlet_new + socketreceiver_new + socketreceiver_read + startpost + symbolinlet_new + sys_addhist + sys_addpollfn + sys_advance_samples DATA + sys_audioapi DATA + sys_audiodevnametonumber + sys_audiodevnumbertoname + sys_bail + sys_bashfilename + sys_clearhist + sys_close + sys_close_audio + sys_close_midi + sys_closesocket + sys_debuglevel DATA + sys_decodedialog + sys_defaultfont DATA + sys_defeatrt DATA + sys_do_open_midi + sys_externalschedlib DATA + sys_fclose + sys_flags DATA + sys_font DATA + sys_fontheight + sys_fontweight DATA + sys_fontwidth + sys_fopen + sys_get_audio_apis + sys_get_audio_devs + sys_get_audio_params + sys_get_inchannels + sys_get_midi_apis + sys_get_midi_devs + sys_get_midi_params + sys_get_outchannels + sys_getblksize + sys_getmeters + sys_getrealtime + sys_getsr + sys_getversion + sys_gui + sys_guicmd DATA + sys_havegui + sys_hipriority DATA + sys_hostfontsize + sys_idlehook DATA + sys_init_fdpoll + sys_initmidiqueue + sys_isabsolutepath + sys_libdir DATA + sys_listdevs + sys_listmididevs + sys_load_lib + sys_loadpreferences + sys_lock + sys_log_error + sys_logerror + sys_microsleep + sys_midiapi DATA + sys_midibytein + sys_mididevnametonumber + sys_mididevnumbertoname + sys_midiindevlist DATA + sys_midioutdevlist DATA + sys_nearestfontsize + sys_nmidiin DATA + sys_nmidiout DATA + sys_noloadbang DATA + sys_open + sys_open_absolute + sys_open_midi + sys_ouch + sys_poll_midi + sys_pollgui + sys_pollmidiqueue + sys_pretendguibytes + sys_printhook DATA + sys_printtostderr DATA + sys_putmidibyte + sys_putmidimess + sys_queuegui + sys_register_loader + sys_reopen_audio + sys_reopen_midi + sys_reportidle + sys_rmpollfn + sys_save_audio_params + sys_savepreferences + sys_schedadvance DATA + sys_send_dacs + sys_set_audio_api + sys_set_audio_settings + sys_set_audio_settings_reopen + sys_set_audio_state + sys_set_midi_api + sys_setchsr + sys_setextrapath + sys_setmiditimediff + sys_sleepgrain DATA + sys_sockerror + sys_trylock + sys_trytoopenone + sys_unbashfilename + sys_unixerror + sys_unlock + sys_unqueuegui + sys_usestdpath DATA + sys_verbose DATA + sys_vgui + sys_zoom_open DATA + sys_zoomfontheight + sys_zoomfontwidth + template_find_field + template_findbyname + template_findcanvas + template_free + template_getfloat + template_getsymbol + template_match + template_new + template_notify + template_setfloat + template_setsymbol + text_drawborder + text_eraseborder + text_setto + text_widgetbehavior DATA + text_xpix + text_ypix + value_get + value_getfloat + value_release + value_setfloat + verbose + vinlet_class DATA + voutlet_class DATA + word_free + word_init + word_restore + zero_perform + zgetfn diff --git a/plugins/puredata/external/pd/include/m_pd.h b/plugins/puredata/external/pd/include/m_pd.h new file mode 100644 index 0000000..1bcbfff --- /dev/null +++ b/plugins/puredata/external/pd/include/m_pd.h @@ -0,0 +1,911 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#ifndef __m_pd_h_ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +#define PD_MAJOR_VERSION 0 +#define PD_MINOR_VERSION 51 +#define PD_BUGFIX_VERSION 4 +#define PD_TEST_VERSION "" +extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */ + +/* old name for "MSW" flag -- we have to take it for the sake of many old +"nmakefiles" for externs, which will define NT and not MSW */ +#if defined(NT) && !defined(MSW) +#define MSW +#endif + +/* These pragmas are only used for MSVC, not MinGW or Cygwin */ +#ifdef _MSC_VER +/* #pragma warning( disable : 4091 ) */ +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */ +#pragma warning( disable : 4101 ) /* unused automatic variables */ +#endif /* _MSC_VER */ + + /* the external storage class is "extern" in UNIX; in MSW it's ugly. */ +#ifdef _WIN32 +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* _WIN32 */ + + /* On most c compilers, you can just say "struct foo;" to declare a + structure whose elements are defined elsewhere. On MSVC, when compiling + C (but not C++) code, you have to say "extern struct foo;". So we make + a stupid macro: */ +#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \ + && !defined(__cplusplus) +#define EXTERN_STRUCT extern struct +#else +#define EXTERN_STRUCT struct +#endif + +/* Define some attributes, specific to the compiler */ +#if defined(__GNUC__) +#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b))) +#else +#define ATTRIBUTE_FORMAT_PRINTF(a, b) +#endif + +#if !defined(_SIZE_T) && !defined(_SIZE_T_) +#include /* just for size_t -- how lame! */ +#endif + +/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers: + https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library + These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +# include +#endif + +/* for FILE, needed by sys_fopen() and sys_fclose() only */ +#include + +#define MAXPDSTRING 1000 /* use this for anything you want */ +#define MAXPDARG 5 /* max number of args we can typecheck today */ + +/* signed and unsigned integer types the size of a pointer: */ +#if !defined(PD_LONGINTTYPE) +#if defined(_WIN32) && defined(_WIN64) +#define PD_LONGINTTYPE long long +#else +#define PD_LONGINTTYPE long +#endif +#endif + +#if !defined(PD_FLOATSIZE) + /* normally, our floats (t_float, t_sample,...) are 32bit */ +# define PD_FLOATSIZE 32 +#endif + +#if PD_FLOATSIZE == 32 +# define PD_FLOATTYPE float +/* an unsigned int of the same size as FLOATTYPE: */ +# define PD_FLOATUINTTYPE uint32_t + +#elif PD_FLOATSIZE == 64 +# define PD_FLOATTYPE double +# define PD_FLOATUINTTYPE uint64_t +#else +# error invalid FLOATSIZE: must be 32 or 64 +#endif + +typedef PD_LONGINTTYPE t_int; /* pointer-size integer */ +typedef PD_FLOATTYPE t_float; /* a float type at most the same size */ +typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */ + +typedef struct _symbol +{ + const char *s_name; + struct _class **s_thing; + struct _symbol *s_next; +} t_symbol; + +EXTERN_STRUCT _array; +#define t_array struct _array /* g_canvas.h */ + +/* pointers to glist and array elements go through a "stub" which sticks +around after the glist or array is freed. The stub itself is deleted when +both the glist/array is gone and the refcount is zero, ensuring that no +gpointers are pointing here. */ + +#define GP_NONE 0 /* the stub points nowhere (has been cut off) */ +#define GP_GLIST 1 /* the stub points to a glist element */ +#define GP_ARRAY 2 /* ... or array */ + +typedef struct _gstub +{ + union + { + struct _glist *gs_glist; /* glist we're in */ + struct _array *gs_array; /* array we're in */ + } gs_un; + int gs_which; /* GP_GLIST/GP_ARRAY */ + int gs_refcount; /* number of gpointers pointing here */ +} t_gstub; + +typedef struct _gpointer /* pointer to a gobj in a glist */ +{ + union + { + struct _scalar *gp_scalar; /* scalar we're in (if glist) */ + union word *gp_w; /* raw data (if array) */ + } gp_un; + int gp_valid; /* number which must match gpointee */ + t_gstub *gp_stub; /* stub which points to glist/array */ +} t_gpointer; + +typedef union word +{ + t_float w_float; + t_symbol *w_symbol; + t_gpointer *w_gpointer; + t_array *w_array; + struct _binbuf *w_binbuf; + int w_index; +} t_word; + +typedef enum +{ + A_NULL, + A_FLOAT, + A_SYMBOL, + A_POINTER, + A_SEMI, + A_COMMA, + A_DEFFLOAT, + A_DEFSYM, + A_DOLLAR, + A_DOLLSYM, + A_GIMME, + A_CANT +} t_atomtype; + +#define A_DEFSYMBOL A_DEFSYM /* better name for this */ + +typedef struct _atom +{ + t_atomtype a_type; + union word a_w; +} t_atom; + +EXTERN_STRUCT _class; +#define t_class struct _class + +EXTERN_STRUCT _outlet; +#define t_outlet struct _outlet + +EXTERN_STRUCT _inlet; +#define t_inlet struct _inlet + +EXTERN_STRUCT _binbuf; +#define t_binbuf struct _binbuf + +EXTERN_STRUCT _clock; +#define t_clock struct _clock + +EXTERN_STRUCT _outconnect; +#define t_outconnect struct _outconnect + +EXTERN_STRUCT _glist; +#define t_glist struct _glist +#define t_canvas struct _glist /* LATER lose this */ + +EXTERN_STRUCT _template; + +typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ + +typedef struct _gobj /* a graphical object */ +{ + t_pd g_pd; /* pure datum header (class) */ + struct _gobj *g_next; /* next in list */ +} t_gobj; + +typedef struct _scalar /* a graphical object holding data */ +{ + t_gobj sc_gobj; /* header for graphical object */ + t_symbol *sc_template; /* template name (LATER replace with pointer) */ + t_word sc_vec[1]; /* indeterminate-length array of words */ +} t_scalar; + +typedef struct _text /* patchable object - graphical, with text */ +{ + t_gobj te_g; /* header for graphical object */ + t_binbuf *te_binbuf; /* holder for the text */ + t_outlet *te_outlet; /* linked list of outlets */ + t_inlet *te_inlet; /* linked list of inlets */ + short te_xpix; /* x&y location (within the toplevel) */ + short te_ypix; + short te_width; /* requested width in chars, 0 if auto */ + unsigned int te_type:2; /* from defs below */ +} t_text; + +#define T_TEXT 0 /* just a textual comment */ +#define T_OBJECT 1 /* a MAX style patchable object */ +#define T_MESSAGE 2 /* a MAX type message */ +#define T_ATOM 3 /* a cell to display a number or symbol */ + +#define te_pd te_g.g_pd + + /* t_object is synonym for t_text (LATER unify them) */ + +typedef struct _text t_object; + +#define ob_outlet te_outlet +#define ob_inlet te_inlet +#define ob_binbuf te_binbuf +#define ob_pd te_g.g_pd +#define ob_g te_g + +typedef void (*t_method)(void); +typedef void *(*t_newmethod)(void); + +/* in ARM 64 a varargs prototype generates a different function call sequence +from a fixed one, so in that special case we make a more restrictive +definition for t_gotfn. This will break some code in the "chaos" package +in Pd extended. (that code will run incorrectly anyhow so why not catch it +at compile time anyhow.) */ +#if defined(__APPLE__) && defined(__aarch64__) +typedef void (*t_gotfn)(void *x); +#else +typedef void (*t_gotfn)(void *x, ...); +#endif + +/* ---------------- pre-defined objects and symbols --------------*/ +EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */ +EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */ + +/* --------- prototypes from the central message system ----------- */ +EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv); +EXTERN t_symbol *gensym(const char *s); +EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s); +EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s); +EXTERN void nullfn(void); +EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...); + +/* the following macros are for sending non-type-checkable messages, i.e., +using function lookup but circumventing type checking on arguments. Only +use for internal messaging protected by A_CANT so that the message can't +be generated at patch level. */ +#define mess0(x, s) ((*getfn((x), (s)))((x))) +typedef void (*t_gotfn1)(void *x, void *arg1); +#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a))) +typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2); +#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b))) +typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3); +#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c))) +typedef void (*t_gotfn4)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4); +#define mess4(x, s, a,b,c,d) \ + ((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d))) +typedef void (*t_gotfn5)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4, void *arg5); +#define mess5(x, s, a,b,c,d,e) \ + ((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e))) + +EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_pd *pd_newest(void); + +/* --------------- memory management -------------------- */ +EXTERN void *getbytes(size_t nbytes); +EXTERN void *getzbytes(size_t nbytes); +EXTERN void *copybytes(const void *src, size_t nbytes); +EXTERN void freebytes(void *x, size_t nbytes); +EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); + +/* -------------------- atoms ----------------------------- */ + +#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0) +#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0) +#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \ + (atom)->a_w.w_gpointer = (gp)) +#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) +#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ + (atom)->a_w.w_symbol = (s)) +#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \ + (atom)->a_w.w_index = (n)) +#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \ + (atom)->a_w.w_symbol= (s)) + +EXTERN t_float atom_getfloat(const t_atom *a); +EXTERN t_int atom_getint(const t_atom *a); +EXTERN t_symbol *atom_getsymbol(const t_atom *a); +EXTERN t_symbol *atom_gensym(const t_atom *a); +EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv); +EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv); +EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv); + +EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize); + +/* ------------------ binbufs --------------- */ + +EXTERN t_binbuf *binbuf_new(void); +EXTERN void binbuf_free(t_binbuf *x); +EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y); + +EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size); +EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp); +EXTERN void binbuf_clear(t_binbuf *x); +EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...); +EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y); +EXTERN void binbuf_addsemi(t_binbuf *x); +EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_print(const t_binbuf *x); +EXTERN int binbuf_getnatom(const t_binbuf *x); +EXTERN t_atom *binbuf_getvec(const t_binbuf *x); +EXTERN int binbuf_resize(t_binbuf *x, int newsize); +EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv); +EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas, + int crflag); +EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir, + int crflag); +EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av, + int tonew); + +/* ------------------ clocks --------------- */ + +EXTERN t_clock *clock_new(void *owner, t_method fn); +EXTERN void clock_set(t_clock *x, double systime); +EXTERN void clock_delay(t_clock *x, double delaytime); +EXTERN void clock_unset(t_clock *x); +EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag); +EXTERN double clock_getlogicaltime(void); +EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ +EXTERN double clock_gettimesince(double prevsystime); +EXTERN double clock_gettimesincewithunits(double prevsystime, + double units, int sampflag); +EXTERN double clock_getsystimeafter(double delaytime); +EXTERN void clock_free(t_clock *x); + +/* ----------------- pure data ---------------- */ +EXTERN t_pd *pd_new(t_class *cls); +EXTERN void pd_free(t_pd *x); +EXTERN void pd_bind(t_pd *x, t_symbol *s); +EXTERN void pd_unbind(t_pd *x, t_symbol *s); +EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c); +EXTERN void pd_pushsym(t_pd *x); +EXTERN void pd_popsym(t_pd *x); +EXTERN void pd_bang(t_pd *x); +EXTERN void pd_pointer(t_pd *x, t_gpointer *gp); +EXTERN void pd_float(t_pd *x, t_float f); +EXTERN void pd_symbol(t_pd *x, t_symbol *s); +EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv); +#define pd_class(x) (*(x)) + +/* ----------------- pointers ---------------- */ +EXTERN void gpointer_init(t_gpointer *gp); +EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto); +EXTERN void gpointer_unset(t_gpointer *gp); +EXTERN int gpointer_check(const t_gpointer *gp, int headok); + +/* ----------------- patchable "objects" -------------- */ +EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, + t_symbol *s2); +EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp); +EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f); +EXTERN void inlet_free(t_inlet *x); + +EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s); +EXTERN void outlet_bang(t_outlet *x); +EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp); +EXTERN void outlet_float(t_outlet *x, t_float f); +EXTERN void outlet_symbol(t_outlet *x, t_symbol *s); +EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_symbol *outlet_getsymbol(t_outlet *x); +EXTERN void outlet_free(t_outlet *x); +EXTERN t_object *pd_checkobject(t_pd *x); + + +/* -------------------- canvases -------------- */ + +EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); + +EXTERN void canvas_setargs(int argc, const t_atom *argv); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); +EXTERN t_symbol *canvas_getcurrentdir(void); +EXTERN t_glist *canvas_getcurrent(void); +EXTERN void canvas_makefilename(const t_glist *c, const char *file, + char *result, int resultsize); +EXTERN t_symbol *canvas_getdir(const t_glist *x); +EXTERN char sys_font[]; /* default typeface set in s_main.c */ +EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */ +EXTERN int sys_hostfontsize(int fontsize, int zoom); +EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase); +EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase); +EXTERN int sys_fontwidth(int fontsize); +EXTERN int sys_fontheight(int fontsize); +EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); +EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); + +/* ---------------- widget behaviors ---------------------- */ + +EXTERN_STRUCT _widgetbehavior; +#define t_widgetbehavior struct _widgetbehavior + +EXTERN_STRUCT _parentwidgetbehavior; +#define t_parentwidgetbehavior struct _parentwidgetbehavior +EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x); + +/* -------------------- classes -------------- */ + +#define CLASS_DEFAULT 0 /* flags for new classes below */ +#define CLASS_PD 1 +#define CLASS_GOBJ 2 +#define CLASS_PATCHABLE 3 +#define CLASS_NOINLET 8 + +#define CLASS_TYPEMASK 3 + +EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN void class_free(t_class *c); + +#ifdef PDINSTANCE +EXTERN t_class *class_getfirst(void); +#endif + +EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +EXTERN void class_addbang(t_class *c, t_method fn); +EXTERN void class_addpointer(t_class *c, t_method fn); +EXTERN void class_doaddfloat(t_class *c, t_method fn); +EXTERN void class_addsymbol(t_class *c, t_method fn); +EXTERN void class_addlist(t_class *c, t_method fn); +EXTERN void class_addanything(t_class *c, t_method fn); +EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s); +EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w); +EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w); +EXTERN const char *class_getname(const t_class *c); +EXTERN const char *class_gethelpname(const t_class *c); +EXTERN const char *class_gethelpdir(const t_class *c); +EXTERN void class_setdrawcommand(t_class *c); +EXTERN int class_isdrawcommand(const t_class *c); +EXTERN void class_domainsignalin(t_class *c, int onset); +EXTERN void class_set_extern_dir(t_symbol *s); +#define CLASS_MAINSIGNALIN(c, type, field) \ + class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0) + + /* prototype for functions to save Pd's to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); +EXTERN void class_setsavefn(t_class *c, t_savefn f); +EXTERN t_savefn class_getsavefn(const t_class *c); +EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */ + + /* prototype for functions to open properties dialogs */ +typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); +EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f); +EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c); + +typedef void (*t_classfreefn)(t_class *); +EXTERN void class_setfreefn(t_class *c, t_classfreefn fn); + +#ifndef PD_CLASS_DEF +#define class_addbang(x, y) class_addbang((x), (t_method)(y)) +#define class_addpointer(x, y) class_addpointer((x), (t_method)(y)) +#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y)) +#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y)) +#define class_addlist(x, y) class_addlist((x), (t_method)(y)) +#define class_addanything(x, y) class_addanything((x), (t_method)(y)) +#endif + +#if PD_FLOATSIZE == 64 +# define class_new class_new64 +#endif + +/* ------------ printing --------------------------------- */ +EXTERN void post(const char *fmt, ...); +EXTERN void startpost(const char *fmt, ...); +EXTERN void poststring(const char *s); +EXTERN void postfloat(t_floatarg f); +EXTERN void postatom(int argc, const t_atom *argv); +EXTERN void endpost(void); +EXTERN void error(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void logpost(const void *object, const int level, const char *fmt, ...) + ATTRIBUTE_FORMAT_PRINTF(3, 4); + +/* ------------ system interface routines ------------------- */ +EXTERN int sys_isabsolutepath(const char *dir); +EXTERN void sys_bashfilename(const char *from, char *to); +EXTERN void sys_unbashfilename(const char *from, char *to); +EXTERN int open_via_path(const char *dir, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); +EXTERN int sched_geteventno(void); +EXTERN double sys_getrealtime(void); +EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */ + +/* Win32's open()/fopen() do not handle UTF-8 filenames so we need + * these internal versions that handle UTF-8 filenames the same across + * all platforms. They are recommended for use in external + * objectclasses as well so they work with Unicode filenames on Windows */ +EXTERN int sys_open(const char *path, int oflag, ...); +EXTERN int sys_close(int fd); +EXTERN FILE *sys_fopen(const char *filename, const char *mode); +EXTERN int sys_fclose(FILE *stream); + +/* ------------ threading ------------------- */ +EXTERN void sys_lock(void); +EXTERN void sys_unlock(void); +EXTERN int sys_trylock(void); + + +/* --------------- signals ----------------------------------- */ + +typedef PD_FLOATTYPE t_sample; +typedef union _sampleint_union { + t_sample f; + PD_FLOATUINTTYPE i; +} t_sampleint_union; +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) + +typedef struct _signal +{ + int s_n; /* number of points in the array */ + t_sample *s_vec; /* the array */ + t_float s_sr; /* sample rate */ + int s_refcount; /* number of times used */ + int s_isborrowed; /* whether we're going to borrow our array */ + struct _signal *s_borrowedfrom; /* signal to borrow it from */ + struct _signal *s_nextfree; /* next in freelist */ + struct _signal *s_nextused; /* next in used list */ + int s_vecsize; /* allocated size of array in points */ +} t_signal; + +typedef t_int *(*t_perfroutine)(t_int *args); + +EXTERN t_int *plus_perform(t_int *args); +EXTERN t_int *zero_perform(t_int *args); +EXTERN t_int *copy_perform(t_int *args); + +EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n); +EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n); +EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n); +EXTERN void dsp_add_zero(t_sample *out, int n); + +EXTERN int sys_getblksize(void); +EXTERN t_float sys_getsr(void); +EXTERN int sys_get_inchannels(void); +EXTERN int sys_get_outchannels(void); + +EXTERN void dsp_add(t_perfroutine f, int n, ...); +EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec); +EXTERN void pd_fft(t_float *buf, int npoints, int inverse); +EXTERN int ilog2(int n); + +EXTERN void mayer_fht(t_sample *fz, int n); +EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_realfft(int n, t_sample *real); +EXTERN void mayer_realifft(int n, t_sample *real); + +EXTERN float *cos_table; +#define LOGCOSTABSIZE 9 +#define COSTABSIZE (1<> 1) & 0x20000000)); +} + +#elif PD_FLOATSIZE == 64 + +typedef union +{ + t_float f; + unsigned int ui[2]; +}t_bigorsmall64; + +static inline int PD_BADFLOAT(t_float f) /* malformed double */ +{ + t_bigorsmall64 pun; + pun.f = f; + pun.ui[1] &= 0x7ff00000; + return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000)); +} + +static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */ +{ + t_bigorsmall64 pun; + pun.f = f; + return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000)); +} + +#endif /* PD_FLOATSIZE */ +#else /* not INTEL or ARM */ +#define PD_BADFLOAT(f) 0 +#define PD_BIGORSMALL(f) 0 +#endif + +#else /* _MSC_VER */ +#if PD_FLOATSIZE == 32 +#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \ + (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000)) +/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */ +#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \ + (((*(unsigned int*)&(f))&0x60000000)==0x60000000)) +#else /* 64 bits... don't know what to do here */ +#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0))) +#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \ + || (f) > -1e-150 && (f) < 1e-150 ) +#endif +#endif /* _MSC_VER */ + /* get version number at run time */ +EXTERN void sys_getversion(int *major, int *minor, int *bugfix); + +EXTERN_STRUCT _instancemidi; +#define t_instancemidi struct _instancemidi + +EXTERN_STRUCT _instanceinter; +#define t_instanceinter struct _instanceinter + +EXTERN_STRUCT _instancecanvas; +#define t_instancecanvas struct _instancecanvas + +EXTERN_STRUCT _instanceugen; +#define t_instanceugen struct _instanceugen + +EXTERN_STRUCT _instancestuff; +#define t_instancestuff struct _instancestuff + +#ifndef PDTHREADS +#define PDTHREADS 1 +#endif + +struct _pdinstance +{ + double pd_systime; /* global time in Pd ticks */ + t_clock *pd_clock_setlist; /* list of set clocks */ + t_canvas *pd_canvaslist; /* list of all root canvases */ + struct _template *pd_templatelist; /* list of all templates */ + int pd_instanceno; /* ordinal number of this instance */ + t_symbol **pd_symhash; /* symbol table hash table */ + t_instancemidi *pd_midi; /* private stuff for x_midi.c */ + t_instanceinter *pd_inter; /* private stuff for s_inter.c */ + t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */ + t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */ + t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */ + t_pd *pd_newest; /* most recently created object */ +#ifdef PDINSTANCE + t_symbol pd_s_pointer; + t_symbol pd_s_float; + t_symbol pd_s_symbol; + t_symbol pd_s_bang; + t_symbol pd_s_list; + t_symbol pd_s_anything; + t_symbol pd_s_signal; + t_symbol pd_s__N; + t_symbol pd_s__X; + t_symbol pd_s_x; + t_symbol pd_s_y; + t_symbol pd_s_; +#endif +#if PDTHREADS + int pd_islocked; +#endif +}; +#define t_pdinstance struct _pdinstance +EXTERN t_pdinstance pd_maininstance; + +/* m_pd.c */ +#ifdef PDINSTANCE +EXTERN t_pdinstance *pdinstance_new(void); +EXTERN void pd_setinstance(t_pdinstance *x); +EXTERN void pdinstance_free(t_pdinstance *x); +#endif /* PDINSTANCE */ + +#if defined(PDTHREADS) && defined(PDINSTANCE) +#ifdef _MSC_VER +#define PERTHREAD __declspec(thread) +#else +#define PERTHREAD __thread +#endif /* _MSC_VER */ +#else +#define PERTHREAD +#endif + +#ifdef PDINSTANCE +extern PERTHREAD t_pdinstance *pd_this; +EXTERN t_pdinstance **pd_instances; +EXTERN int pd_ninstances; +#else +#define pd_this (&pd_maininstance) +#endif /* PDINSTANCE */ + +#ifdef PDINSTANCE +#define s_pointer (pd_this->pd_s_pointer) +#define s_float (pd_this->pd_s_float) +#define s_symbol (pd_this->pd_s_symbol) +#define s_bang (pd_this->pd_s_bang) +#define s_list (pd_this->pd_s_list) +#define s_anything (pd_this->pd_s_anything) +#define s_signal (pd_this->pd_s_signal) +#define s__N (pd_this->pd_s__N) +#define s__X (pd_this->pd_s__X) +#define s_x (pd_this->pd_s_x) +#define s_y (pd_this->pd_s_y) +#define s_ (pd_this->pd_s_) +#else +EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything, + s_signal, s__N, s__X, s_x, s_y, s_; +#endif + +EXTERN t_canvas *pd_getcanvaslist(void); +EXTERN int pd_getdspstate(void); + +/* x_text.c */ +EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */ +EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif + +#define __m_pd_h_ +#endif /* __m_pd_h_ */ diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c new file mode 100644 index 0000000..b0d5ad5 --- /dev/null +++ b/plugins/puredata/sfizz_puredata.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "GitBuildId.h" +#include +#include +#include +#include +#include +#include +#include + +static t_class* cls_sfizz_tilde; + +typedef struct _sfizz_tilde { + t_object obj; + t_outlet* outputs[2]; + t_inlet* input_cc; + t_inlet* input_bend; + t_inlet* input_touch; + t_inlet* input_polytouch; + sfizz_synth_t* synth; + int midi[3]; + int midinum; + t_symbol* dir; + char* filepath; +} t_sfizz_tilde; + +static t_float clamp01(t_float x) +{ + x = (x > 0) ? x : 0; + x = (x < 1) ? x : 1; + return x; +} + +static t_float clampB1(t_float x) +{ + x = (x > -1) ? x : -1; + x = (x < 1) ? x : 1; + return x; +} + +static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file) +{ + const char* dir = self->dir->s_name; + char* filepath; + if (file[0] != '\0') { + filepath = malloc(strlen(dir) + 1 + strlen(file) + 1); + sprintf(filepath, "%s/%s", dir, file); + } + else { + filepath = malloc(1); + *filepath = '\0'; + } + free(self->filepath); + self->filepath = filepath; +} + +static bool sfizz_tilde_do_load(t_sfizz_tilde* self) +{ + bool loaded; + if (self->filepath[0] != '\0') + loaded = sfizz_load_or_import_file(self->synth, self->filepath, NULL); + else + loaded = sfizz_load_string(self->synth, "default.sfz", "sample=*sine"); + return loaded; +} + +static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[]) +{ + (void)sym; + t_sfizz_tilde* self = (t_sfizz_tilde*)pd_new(cls_sfizz_tilde); + + const char* file; + if (argc == 0) + file = ""; + else if (argc == 1 && argv[0].a_type == A_SYMBOL) + file = argv[0].a_w.w_symbol->s_name; + else { + pd_free((t_pd*)self); + return NULL; + } + + self->dir = canvas_getcurrentdir(); + + self->outputs[0] = outlet_new(&self->obj, &s_signal); + self->outputs[1] = outlet_new(&self->obj, &s_signal); + + self->input_cc = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("cc")); + self->input_bend = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("bend")); + self->input_touch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("touch")); + self->input_polytouch = inlet_new(&self->obj, &self->obj.ob_pd, &s_float, gensym("polytouch")); + + sfizz_synth_t* synth = sfizz_create_synth(); + self->synth = synth; + + sfizz_set_sample_rate(synth, sys_getsr()); + sfizz_set_samples_per_block(synth, sys_getblksize()); + + sfizz_tilde_set_file(self, file); + if (!sfizz_tilde_do_load(self)) { + pd_free((t_pd*)self); + return NULL; + } + + return self; +} + +static void sfizz_tilde_free(t_sfizz_tilde* self) +{ + if (self->filepath) + free(self->filepath); + if (self->synth) + sfizz_free(self->synth); + if (self->outputs[0]) + outlet_free(self->outputs[0]); + if (self->outputs[1]) + outlet_free(self->outputs[1]); + if (self->input_cc) + inlet_free(self->input_cc); + if (self->input_bend) + inlet_free(self->input_bend); + if (self->input_touch) + inlet_free(self->input_touch); + if (self->input_polytouch) + inlet_free(self->input_polytouch); +} + +static t_int* sfizz_tilde_perform(t_int* w) +{ + t_sfizz_tilde* self; + t_sample* outputs[2]; + t_int nframes; + + w++; + self = (t_sfizz_tilde*)*w++; + outputs[0] = (t_sample*)*w++; + outputs[1] = (t_sample*)*w++; + nframes = (t_int)*w++; + + sfizz_render_block(self->synth, outputs, 2, nframes); + + return w; +} + +static void sfizz_tilde_dsp(t_sfizz_tilde* self, t_signal** sp) +{ + dsp_add( + &sfizz_tilde_perform, 4, (t_int)self, + (t_int)sp[0]->s_vec, (t_int)sp[1]->s_vec, (t_int)sp[0]->s_n); +} + +static void sfizz_tilde_list(t_sfizz_tilde* self, t_symbol* sym, int argc, t_atom* argv) +{ + (void)sym; + + if (argc == 2 && argv[0].a_type == A_FLOAT && argv[1].a_type == A_FLOAT) { + int key = (int)argv[0].a_w.w_float; + if (key < 0 || key > 127) + return; + t_float vel = clamp01(argv[1].a_w.w_float / 127); + if (vel > 0) + sfizz_send_hd_note_on(self->synth, 0, key, vel); + else + sfizz_send_hd_note_off(self->synth, 0, key, 0); + } +} + +static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) +{ + int byte = (int)f; + bool isstatus = (byte & 0x80) != 0; + + int* midi = self->midi; + int midinum = self->midinum; + + // + if (isstatus) { + midi[0] = byte; + midinum = 1; + } + else if (midinum != -1 && midinum < 3) + midi[midinum++] = byte; + else + midinum = -1; + + // + switch (midinum) { + case 2: + switch (midi[0] & 0xf0) { + case 0xd0: // channel aftertouch + sfizz_send_channel_aftertouch(self->synth, 0, midi[1]); + break; + } + break; + case 3: + switch (midi[0] & 0xf0) { + case 0x90: // note on + if (midi[2] == 0) + goto noteoff; + sfizz_send_note_on(self->synth, 0, midi[1], midi[2]); + break; + case 0x80: // note off + noteoff: + sfizz_send_note_off(self->synth, 0, midi[1], midi[2]); + break; + case 0xb0: // controller + sfizz_send_cc(self->synth, 0, midi[1], midi[2]); + break; + case 0xa0: // key aftertouch + sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]); + break; + case 0xe0: // pitch bend + sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); + break; + } + break; + } + + self->midinum = midinum; +} + +static void sfizz_tilde_load(t_sfizz_tilde* self, t_symbol* sym) +{ + sfizz_tilde_set_file(self, sym->s_name); + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) +{ + (void)value; + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float f1, t_float f2) +{ + int cc = (int)f1; + if (cc < 0 || cc >= MIDI_CC_MAX) + return; + sfizz_automate_hdcc(self->synth, 0, (int)cc, clamp01(f2)); +} + +static void sfizz_tilde_cc(t_sfizz_tilde* self, t_float f1, t_float f2) +{ + sfizz_tilde_hdcc(self, f1, f2 / 127); +} + +static void sfizz_tilde_hdbend(t_sfizz_tilde* self, t_float f1) +{ + sfizz_send_hd_pitch_wheel(self->synth, 0, clampB1(f1)); +} + +static void sfizz_tilde_bend(t_sfizz_tilde* self, t_float f1) +{ + return sfizz_tilde_hdbend(self, f1 / 8191); +} + +static void sfizz_tilde_hdtouch(t_sfizz_tilde* self, t_float f1) +{ + sfizz_send_hd_channel_aftertouch(self->synth, 0, clamp01(f1)); +} + +static void sfizz_tilde_touch(t_sfizz_tilde* self, t_float f1) +{ + sfizz_tilde_hdtouch(self, f1 / 127); +} + +static void sfizz_tilde_hdpolytouch(t_sfizz_tilde* self, t_float key, t_float f2) +{ + if (key < 0 || key > 127) + return; + sfizz_send_hd_poly_aftertouch(self->synth, 0, (int)key, clamp01(f2)); +} + +static void sfizz_tilde_polytouch(t_sfizz_tilde* self, t_float f1, t_float f2) +{ + sfizz_tilde_hdpolytouch(self, f1, f2 / 127); +} + +static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float f1) +{ + int numvoices = (int)f1; + numvoices = (numvoices < 1) ? 1 : numvoices; + sfizz_set_num_voices(self->synth, numvoices); +} + +#if defined(_WIN32) +__declspec(dllexport) +#else +__attribute__((visibility("default"))) +#endif +void sfizz_setup() +{ + if (GitBuildId[0]) + post("sfizz external for Puredata, version '%s.%s'", SFIZZ_VERSION, GitBuildId); + else + post("sfizz external for Puredata, version '%s'", SFIZZ_VERSION); + + cls_sfizz_tilde = class_new( + gensym("sfizz~"), + (t_newmethod)&sfizz_tilde_new, + (t_method)&sfizz_tilde_free, + sizeof(t_sfizz_tilde), + CLASS_DEFAULT, A_GIMME, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_dsp, gensym("dsp"), A_CANT, A_NULL); + class_addlist( + cls_sfizz_tilde, (t_method)&sfizz_tilde_list); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_midiin, &s_float, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_load, gensym("load"), A_DEFSYM, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_reload, gensym("reload"), A_DEFFLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_cc, gensym("cc"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdcc, gensym("hdcc"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_bend, gensym("bend"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdbend, gensym("hdbend"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_touch, gensym("touch"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdtouch, gensym("hdtouch"), A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_polytouch, gensym("polytouch"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdpolytouch, gensym("hdpolytouch"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_voices, gensym("voices"), A_FLOAT, A_NULL); +} diff --git a/plugins/puredata/sfizz~-help.pd b/plugins/puredata/sfizz~-help.pd new file mode 100644 index 0000000..c3024d3 --- /dev/null +++ b/plugins/puredata/sfizz~-help.pd @@ -0,0 +1,60 @@ +#N canvas 614 464 709 426 12; +#X declare -lib sfizz; +#X obj 4 3 cnv 15 700 80 empty empty sfizz 20 30 0 40 -261682 -66577 +0; +#X text 45 54 A synthesizer for instruments in SFZ format; +#X text 33 167 create a SFZ instrument; +#X obj 26 323 midiin; +#X text 23 300 connect MIDI-in; +#X obj 162 223 dac~; +#X msg 177 345 voices \$1; +#X text 175 301 modify the polyphony; +#X obj 376 326 hsl 127 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 6300 1; +#X floatatom 466 346 5 0 0 0 - - -; +#X text 372 302 modulate a parameter; +#X msg 373 346 hdcc 300 \$1; +#X obj 27 115 declare -lib sfizz; +#X obj 163 192 sfizz~ example.sfz; +#X text 18 93 load the sfizz library; +#X msg 221 115 \; pd dsp 1; +#X text 218 94 click to turn on DSP; +#X text 153 247 output stereo audio; +#X msg 398 223 reload; +#X msg 498 223 load example.sfz; +#X text 396 200 reload the instrument or load another; +#X obj 241 165 r \$0-input; +#X obj 26 348 s \$0-input; +#X obj 177 370 s \$0-input; +#X obj 373 371 s \$0-input; +#X obj 398 248 s \$0-input; +#X obj 498 248 s \$0-input; +#X obj 177 326 nbx 5 14 1 256 0 1 empty empty empty 0 -8 0 10 -262144 +-1 -1 64 256; +#X text 567 302 send a note; +#X obj 568 348 makenote 100 500; +#X obj 568 373 list; +#X obj 568 398 s \$0-input; +#X msg 568 323 60; +#X obj 386 92 cnv 15 300 100 empty empty Inlets 20 12 0 14 -233017 +-66577 0; +#X text 395 140 3: pitch bend; +#X text 395 154 4: channel aftertouch; +#X text 395 168 5: polyphonic aftertouch and key; +#X text 395 126 2: controller and value; +#X text 395 112 1: note and velocity; +#X connect 3 0 22 0; +#X connect 6 0 23 0; +#X connect 8 0 11 0; +#X connect 8 0 9 0; +#X connect 11 0 24 0; +#X connect 13 0 5 0; +#X connect 13 1 5 1; +#X connect 18 0 25 0; +#X connect 19 0 26 0; +#X connect 21 0 13 0; +#X connect 27 0 6 0; +#X connect 29 0 30 0; +#X connect 29 1 30 1; +#X connect 30 0 31 0; +#X connect 32 0 29 0; diff --git a/plugins/vst/CMakeLists.txt b/plugins/vst/CMakeLists.txt new file mode 100644 index 0000000..c7025cd --- /dev/null +++ b/plugins/vst/CMakeLists.txt @@ -0,0 +1,376 @@ +if(SFIZZ_USE_SYSTEM_VST3SDK) + find_package(PkgConfig REQUIRED) + pkg_check_modules(VST3SDK "vst3sdk" REQUIRED) + set(VST3SDK_BASEDIR "${VST3SDK_INCLUDE_DIRS}") +else() + set(VST3SDK_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/VST_SDK/VST3_SDK") +endif() + +#set(AUWRAPPER_BASEDIR "${VST3SDK_BASEDIR}/public.sdk/source/vst/auwrapper") +set(AUWRAPPER_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/sfzt_auwrapper") + +# VST plugin specific settings +include(VSTConfig) + +configure_file(VstPluginDefs.h.in "${CMAKE_CURRENT_BINARY_DIR}/VstPluginDefs.h") + +# Build VST3 SDK +include("cmake/Vst3.cmake") + +# Add the ring buffer +set(RINGBUFFER_HEADERS + "external/ring_buffer/ring_buffer/ring_buffer.h" + "external/ring_buffer/ring_buffer/ring_buffer.tcc") +add_library(sfizz-ring-buffer STATIC EXCLUDE_FROM_ALL + "external/ring_buffer/ring_buffer/ring_buffer.cpp" + ${RINGBUFFER_HEADERS}) +source_group("Header Files" FILES ${RINGBUFFER_HEADERS}) +target_include_directories(sfizz-ring-buffer PUBLIC "external/ring_buffer") +add_library(sfizz::ring-buffer ALIAS sfizz-ring-buffer) + +# Build the VST shared code +add_library(sfizz-vst3-core STATIC EXCLUDE_FROM_ALL + SfizzVstProcessor.cpp + SfizzVstProcessor.h + SfizzVstController.cpp + SfizzVstController.h + SfizzVstEditor.cpp + SfizzVstEditor.h + SfizzVstState.cpp + SfizzVstState.h + SfizzVstParameters.h + SfizzVstUpdates.h + SfizzVstUpdates.hpp + SfizzVstUpdates.cpp + SfizzVstIDs.h + OrderedEventProcessor.h + OrderedEventProcessor.hpp + IdleUpdateHandler.h + X11RunLoop.h + X11RunLoop.cpp) +target_include_directories(sfizz-vst3-core + PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") +target_link_libraries(sfizz-vst3-core + PRIVATE sfizz::sfizz + PRIVATE sfizz::import + PRIVATE sfizz::editor + PRIVATE sfizz::plugins-common + PRIVATE sfizz::pugixml sfizz::filesystem + PRIVATE sfizz::ring-buffer + PRIVATE absl::strings absl::optional absl::container_common + vst3sdk + vst3sdk_vstgui) +gw_target_warn(sfizz-vst3-core PRIVATE + "-Wno-ignored-qualifiers" + "-Wno-unused-parameter") +add_library(sfizz::vst3-core ALIAS sfizz-vst3-core) + +# --- VST3 plugin --- # + +if(SFIZZ_VST) + set(VSTPLUGIN_PRJ_NAME "${PROJECT_NAME}_vst3") + set(VSTPLUGIN_BUNDLE_NAME "${PROJECT_NAME}.vst3") + + add_library(${VSTPLUGIN_PRJ_NAME} MODULE "VstPluginFactory.cpp") + if(WIN32) + target_sources(${VSTPLUGIN_PRJ_NAME} PRIVATE vst3.def) + endif() + target_include_directories(${VSTPLUGIN_PRJ_NAME} + PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_link_libraries(${VSTPLUGIN_PRJ_NAME} + PRIVATE sfizz-vst3-core) + set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES + OUTPUT_NAME "${PROJECT_NAME}" + PREFIX "") + + plugin_add_vst3sdk(${VSTPLUGIN_PRJ_NAME}) + plugin_add_vstgui(${VSTPLUGIN_PRJ_NAME}) + + if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(${VSTPLUGIN_PRJ_NAME} PRIVATE + "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/vst3.version") + endif() + sfizz_enable_lto_if_needed(${VSTPLUGIN_PRJ_NAME}) + if(MINGW) + set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES LINK_FLAGS "-static") + endif() + + # Create the bundle(see "VST 3 Locations / Format") + execute_process( + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources") + copy_editor_resources( + ${VSTPLUGIN_PRJ_NAME} + "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources" + "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources") + if(WIN32) + set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES + SUFFIX ".vst3" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/${VST3_PACKAGE_ARCHITECTURE}-win/$<0:>") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/win/Plugin.ico" + "${CMAKE_CURRENT_SOURCE_DIR}/win/desktop.ini" + DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}") + elseif(APPLE) + set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES + SUFFIX "" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/MacOS/$<0:>") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/mac/PkgInfo" + DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents") + set(SFIZZ_VST3_BUNDLE_EXECUTABLE "${PROJECT_NAME}") + set(SFIZZ_VST3_BUNDLE_VERSION "${PROJECT_VERSION}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.vst3.plist" + "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Info.plist" @ONLY) + else() + set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/${VST3_PACKAGE_ARCHITECTURE}-linux/$<0:>") + endif() + + # Copy the license + if(APPLE) + # on macOS, files are not permitted at the bundle root, during code signing + file(COPY "gpl-3.0.txt" + DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/SharedSupport/License") + else() + file(COPY "gpl-3.0.txt" + DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}") + endif() + + # To help debugging the link only + if(FALSE) + target_link_options(${VSTPLUGIN_PRJ_NAME} PRIVATE "-Wl,-no-undefined") + endif() + + # Installation + if(NOT MSVC) + install(DIRECTORY "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}" + DESTINATION "${VSTPLUGIN_INSTALL_DIR}" + COMPONENT "vst" + USE_SOURCE_PERMISSIONS) + bundle_dylibs(vst + "${VSTPLUGIN_INSTALL_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz" + COMPONENT "vst") + endif() +endif() + +# --- Audio Unit wrapper --- # + +if(SFIZZ_AU AND NOT APPLE) + message(WARNING "Audio Unit is available only for macOS builds") +elseif(SFIZZ_AU) + set(AUPLUGIN_PRJ_NAME "${PROJECT_NAME}_au") + set(AUPLUGIN_BUNDLE_NAME "${PROJECT_NAME}.component") + + add_library(${AUPLUGIN_PRJ_NAME} MODULE + "VstPluginFactory.cpp" + "${AUWRAPPER_BASEDIR}/aucarbonview.mm" + "${AUWRAPPER_BASEDIR}/aucocoaview.mm" + "${AUWRAPPER_BASEDIR}/ausdk.mm" + "${AUWRAPPER_BASEDIR}/auwrapper.mm" + "${AUWRAPPER_BASEDIR}/NSDataIBStream.mm") + target_include_directories(${AUPLUGIN_PRJ_NAME} PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" + "${VST3SDK_BASEDIR}") + target_link_libraries(${AUPLUGIN_PRJ_NAME} PRIVATE + "${APPLE_FOUNDATION_LIBRARY}" + "${APPLE_COCOA_LIBRARY}" + "${APPLE_CARBON_LIBRARY}" + "${APPLE_AUDIOTOOLBOX_LIBRARY}" + "${APPLE_AUDIOUNIT_LIBRARY}" + "${APPLE_COREAUDIO_LIBRARY}" + "${APPLE_COREMIDI_LIBRARY}") + + target_link_libraries(${AUPLUGIN_PRJ_NAME} + PRIVATE sfizz-vst3-core) + set_target_properties(${AUPLUGIN_PRJ_NAME} PROPERTIES + OUTPUT_NAME "${PROJECT_NAME}" + PREFIX "") + + plugin_add_vst3sdk(${AUPLUGIN_PRJ_NAME}) + plugin_add_vstgui(${AUPLUGIN_PRJ_NAME}) + + # Get Core Audio utility classes if missing + set(CA_UTILITY_BASEDIR + "${CMAKE_CURRENT_SOURCE_DIR}/external/CoreAudioUtilityClasses") + if(EXISTS "${CA_UTILITY_BASEDIR}") + message(STATUS "The CoreAudioUtilityClasses are available locally") + else() + message(STATUS "The CoreAudioUtilityClasses are not available locally") + + set(CA_UTILITY_VERSION "1.1") + set(CA_UTILITY_ARCHIVE "CoreAudioUtilityClasses-${CA_UTILITY_VERSION}.tar.gz") + set(CA_UTILITY_DOWNLOAD_URL "https://github.com/sfztools/CoreAudioUtilityClasses/releases/download/v${CA_UTILITY_VERSION}/${CA_UTILITY_ARCHIVE}") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/download/${CA_UTILITY_ARCHIVE}") + message(STATUS "The CoreAudioUtilityClasses archive is available") + else() + message(STATUS "The CoreAudioUtilityClasses archive is missing") + message(STATUS "Downloading: ${CA_UTILITY_DOWNLOAD_URL}") + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/download") + file(DOWNLOAD "${CA_UTILITY_DOWNLOAD_URL}" + "${CMAKE_CURRENT_SOURCE_DIR}/download/${CA_UTILITY_ARCHIVE}") + endif() + message(STATUS "Extracting: ${CA_UTILITY_ARCHIVE}") + execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "tar" "xvf" + "${CMAKE_CURRENT_SOURCE_DIR}/download/${CA_UTILITY_ARCHIVE}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external") + endif() + + # Add Core Audio utility classes + target_include_directories(${AUPLUGIN_PRJ_NAME} PRIVATE + "${CA_UTILITY_BASEDIR}/CoreAudio" + "${CA_UTILITY_BASEDIR}/CoreAudio/AudioUnits" + "${CA_UTILITY_BASEDIR}/CoreAudio/AudioUnits/AUPublic/AUBase" + "${CA_UTILITY_BASEDIR}/CoreAudio/AudioUnits/AUPublic/Utility" + "${CA_UTILITY_BASEDIR}/CoreAudio/PublicUtility") + + # Add VST hosting classes + target_link_libraries(${AUPLUGIN_PRJ_NAME} PRIVATE vst3sdk_hosting) + + # Add generated source + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") + target_include_directories(${AUPLUGIN_PRJ_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") + string(TIMESTAMP SFIZZ_AU_CLASS_PREFIX_NUMBER "%s" UTC) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/include/aucocoaclassprefix.h" + "#define SMTG_AU_NAMESPACE SMTGAUCocoa${SFIZZ_AU_CLASS_PREFIX_NUMBER}_") + + sfizz_enable_lto_if_needed(${AUPLUGIN_PRJ_NAME}) + + # Create the bundle + execute_process( + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/Resources") + copy_editor_resources( + ${AUPLUGIN_PRJ_NAME} + "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources" + "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/Resources") + set_target_properties(${AUPLUGIN_PRJ_NAME} PROPERTIES + SUFFIX "" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/MacOS/$<0:>") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/mac/PkgInfo" + DESTINATION "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents") + set(SFIZZ_AU_BUNDLE_EXECUTABLE "${PROJECT_NAME}") + set(SFIZZ_AU_BUNDLE_VERSION "${PROJECT_VERSION}") + set(SFIZZ_AU_BUNDLE_IDENTIFIER "tools.sfz.sfizz.au") + set(SFIZZ_AU_BUNDLE_TYPE "aumu") + set(SFIZZ_AU_BUNDLE_SUBTYPE "samp") + set(SFIZZ_AU_BUNDLE_MANUFACTURER "Sfzt") + set(SFIZZ_AU_BUNDLE_AUTHOR "SFZTools") + math(EXPR SFIZZ_AU_DECIMAL_VERSION + "${PROJECT_VERSION_MAJOR}*256*256 + ${PROJECT_VERSION_MINOR}*256 + ${PROJECT_VERSION_PATCH}") + execute_process( + COMMAND "sh" "-c" "echo 'obase=16;${SFIZZ_AU_DECIMAL_VERSION}' | bc" + OUTPUT_VARIABLE SFIZZ_AU_HEXADECIMAL_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.au.plist" + "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/Info.plist" @ONLY) + + file(COPY "gpl-3.0.txt" + DESTINATION "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/SharedSupport/License") + + # Add the resource fork + if(FALSE) + execute_process(COMMAND "xcrun" "--find" "Rez" + OUTPUT_VARIABLE OSX_REZ_COMMAND OUTPUT_STRIP_TRAILING_WHITESPACE) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mac/audiounitconfig.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/audiounitconfig.h" @ONLY) + add_custom_command(TARGET ${AUPLUGIN_PRJ_NAME} POST_BUILD COMMAND + "${OSX_REZ_COMMAND}" + "-d" "SystemSevenOrLater=1" + "-script" "Roman" + "-d" "i386_YES" + "-d" "x86_64_YES" + "-is" "${CMAKE_OSX_SYSROOT}" + "-I" "${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers" + "-I" "/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers" + "-I" "/System/Library/Frameworks/AudioUnit.framework/Versions/A/Headers/" + "-I" "${CA_UTILITY_BASEDIR}/CoreAudio/AudioUnits/AUPublic/AUBase" + "-I" "${CMAKE_CURRENT_BINARY_DIR}/include" # generated audiounitconfig.h + "-o" "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/Resources/${PROJECT_NAME}.rsrc" + "-useDF" + "${AUWRAPPER_BASEDIR}/auresource.r") + endif() + + # Installation + if(AUPLUGIN_INSTALL_DIR) + install(DIRECTORY "${PROJECT_BINARY_DIR}/${AUPLUGIN_BUNDLE_NAME}" + DESTINATION "${AUPLUGIN_INSTALL_DIR}" + COMPONENT "au" + USE_SOURCE_PERMISSIONS) + bundle_dylibs(au + "${AUPLUGIN_INSTALL_DIR}/${AUPLUGIN_BUNDLE_NAME}/Contents/MacOS/sfizz" + COMPONENT "au") + endif() +endif() + +# --- VST2 wrapper --- # + +if(SFIZZ_VST2) + set(VST2PLUGIN_PRJ_NAME "${PROJECT_NAME}_vst2") + set(VST2PLUGIN_BUNDLE_NAME "${PROJECT_NAME}.vst2") + + set(VST2SDK_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/vstsdk2.4") + set(VST2WRAPPER_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/VST_SDK/VST3_SDK/public.sdk/source/vst/vst2wrapper") + + if(NOT EXISTS "${VST2SDK_BASEDIR}/pluginterfaces/vst2.x/aeffect.h") + message(FATAL_ERROR "VST SDK 2.4 is missing. Make it available in the following folder: ${VST2SDK_BASEDIR}") + endif() + + add_library(${VST2PLUGIN_PRJ_NAME} MODULE + "VstPluginFactory.cpp" + "Vst2PluginFactory.cpp" + "Vst2PluginEntry.c" + "${VST2WRAPPER_BASEDIR}/vst2wrapper.cpp" + "${VST2WRAPPER_BASEDIR}/../basewrapper/basewrapper.cpp" + "${VST2SDK_BASEDIR}/public.sdk/source/vst2.x/audioeffect.cpp" + "${VST2SDK_BASEDIR}/public.sdk/source/vst2.x/audioeffectx.cpp") + target_include_directories(${VST2PLUGIN_PRJ_NAME} PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" + "${VST2SDK_BASEDIR}") + + if(NOT WIN32) + target_compile_definitions(${VST2PLUGIN_PRJ_NAME} PRIVATE "__cdecl=") + endif() + + target_link_libraries(${VST2PLUGIN_PRJ_NAME} + PRIVATE sfizz-vst3-core) + set_target_properties(${VST2PLUGIN_PRJ_NAME} PROPERTIES + OUTPUT_NAME "${PROJECT_NAME}" + PREFIX "") + + plugin_add_vst3sdk(${VST2PLUGIN_PRJ_NAME}) + plugin_add_vstgui(${VST2PLUGIN_PRJ_NAME}) + + # Add VST hosting classes + target_link_libraries(${VST2PLUGIN_PRJ_NAME} PRIVATE vst3sdk_hosting) + + sfizz_enable_lto_if_needed(${VST2PLUGIN_PRJ_NAME}) + + # Create the bundle + execute_process( + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Resources") + copy_editor_resources( + ${VST2PLUGIN_PRJ_NAME} + "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources" + "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Resources") + set_target_properties(${VST2PLUGIN_PRJ_NAME} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Binary/$<0:>") + + file(COPY "gpl-3.0.txt" + DESTINATION "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}") + + if(MINGW) + set_target_properties(${VST2PLUGIN_PRJ_NAME} PROPERTIES LINK_FLAGS "-static") + endif() + + # Installation + if(VST2PLUGIN_INSTALL_DIR) + install(DIRECTORY "${PROJECT_BINARY_DIR}/${VST2PLUGIN_BUNDLE_NAME}" + DESTINATION "${VST2PLUGIN_INSTALL_DIR}" + COMPONENT "vst2" + USE_SOURCE_PERMISSIONS) + if(APPLE) + bundle_dylibs(vst2 + "${VST2PLUGIN_INSTALL_DIR}/${VST2PLUGIN_BUNDLE_NAME}/Contents/Binary/sfizz.${CMAKE_SHARED_MODULE_SUFFIX}" + COMPONENT "vst2") + endif() + endif() +endif() diff --git a/plugins/vst/IdleUpdateHandler.h b/plugins/vst/IdleUpdateHandler.h new file mode 100644 index 0000000..37488c9 --- /dev/null +++ b/plugins/vst/IdleUpdateHandler.h @@ -0,0 +1,47 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once +#include "base/source/updatehandler.h" +#include + +/// @cond ignore +namespace Steinberg { + +class IdleUpdateHandler +{ +public: + static void start () + { + auto& instance = get (); + if (++instance.users == 1) + { + instance.timer = VSTGUI::makeOwned ( + [] (VSTGUI::CVSTGUITimer*) { return UpdateHandler::instance ()->triggerDeferedUpdates (); }, + 1000 / 30); + } + } + + static void stop () + { + auto& instance = get (); + if (--instance.users == 0) + { + instance.timer = nullptr; + } + } + +protected: + static IdleUpdateHandler& get () + { + static IdleUpdateHandler gInstance; + return gInstance; + } + + VSTGUI::SharedPointer timer; + std::atomic users {0}; +}; + +} // namespace Steinberg +/// @endcond ignore diff --git a/plugins/vst/OrderedEventProcessor.h b/plugins/vst/OrderedEventProcessor.h new file mode 100644 index 0000000..4bc01f1 --- /dev/null +++ b/plugins/vst/OrderedEventProcessor.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "pluginterfaces/vst/ivstevents.h" +#include "pluginterfaces/vst/ivstparameterchanges.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include + +using namespace Steinberg; + +template +class OrderedEventProcessor { +public: + virtual ~OrderedEventProcessor() {} + + void initializeEventProcessor(const Vst::ProcessSetup& setup, int32 paramCount, int32 subdivSize = 128); + void processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs); + +private: + int32 paramCount_ { 0 }; + int32 subdivSize_ { 0 }; + struct SubdivChange { + SubdivChange(int32 offset, Vst::ParamID id, Vst::ParamValue value) + : offset(offset), id(std::move(id)), value(std::move(value)) {} + int32 offset; + Vst::ParamID id {}; + Vst::ParamValue value {}; + }; + std::vector subdivChanges_; + std::vector queuePositions_; +}; + +#include "OrderedEventProcessor.hpp" diff --git a/plugins/vst/OrderedEventProcessor.hpp b/plugins/vst/OrderedEventProcessor.hpp new file mode 100644 index 0000000..c423d25 --- /dev/null +++ b/plugins/vst/OrderedEventProcessor.hpp @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "OrderedEventProcessor.h" +#include +#include +#include + +template +void OrderedEventProcessor::initializeEventProcessor(const Vst::ProcessSetup& setup, int32 paramCount, int32 subdivSize) +{ + paramCount_ = paramCount; + subdivSize_ = subdivSize; + subdivChanges_.reserve(subdivSize * paramCount); + queuePositions_.reserve(paramCount); +} + +template +void OrderedEventProcessor::processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs) +{ + if (!pcs || !evs) + return; + + R& receiver = *static_cast(this); + int32 sampleIndex = 0; + int32 subdivNumber = 0; + const int32 subdivSize = subdivSize_; + if (subdivSize == 0) + return; + + int32 eventIdx = 0; + int32 eventCount = evs->getEventCount(); + Vst::Event event; + bool hasEvent = false; + if (eventCount > 0) { + evs->getEvent(eventIdx++, event); + hasEvent = true; + } + + // Assume that there would be as many parameter changes as there are parameters, but some hosts are nice + // and send multiple queues per parameter. Clamp the number of considered parameter queues to the number of parameters. + queuePositions_.clear(); + const int32 parameterCount = pcs->getParameterCount(); + const int32 queueCapacity = static_cast(queuePositions_.capacity()); + assert(queueCapacity >= parameterCount); + const int32 consideredQueueCount = std::min(queueCapacity, parameterCount); + queuePositions_.resize(consideredQueueCount, 0); + + while (sampleIndex < numSamples || subdivNumber == 0) { + const int32 subdivCurrentSize = std::min(numSamples - sampleIndex, subdivSize); + const int32 lastOffset = sampleIndex + subdivSize; + + // Queue all changes for the subdiv + subdivChanges_.clear(); + for (int32 qIdx = 0; qIdx < consideredQueueCount; ++qIdx) { + auto* vq = pcs->getParameterData(qIdx); + if (!vq) + continue; + + int32 offset; + Vst::ParamID id = vq->getParameterId(); + Vst::ParamValue value; + + int32& queuePosition = queuePositions_[qIdx]; + while (queuePosition < vq->getPointCount()) { + vq->getPoint(queuePosition, offset, value); + if (offset > lastOffset) + break; + + subdivChanges_.emplace_back(offset, id, value); + queuePosition++; + } + } + + // Sort the changes + std::sort(subdivChanges_.begin(), subdivChanges_.end(), [] (const SubdivChange& lhs, const SubdivChange& rhs) { + return lhs.offset < rhs.offset; + }); + + // Play the parameter changes, interleaving events as needed in between + for (const auto& change: subdivChanges_) { + while (hasEvent && event.sampleOffset < change.offset) { + receiver.playOrderedEvent(event); + hasEvent = (eventIdx < eventCount); + evs->getEvent(eventIdx++, event); + } + receiver.playOrderedParameter(change.offset, change.id, change.value); + } + + sampleIndex += subdivCurrentSize; + ++subdivNumber; + } + + // Play the remaining events + while (hasEvent) { + receiver.playOrderedEvent(event); + hasEvent = (eventIdx < eventCount); + evs->getEvent(eventIdx++, event); + } +} diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp new file mode 100644 index 0000000..2aeb4d5 --- /dev/null +++ b/plugins/vst/SfizzVstController.cpp @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstController.h" +#include "SfizzVstEditor.h" +#include "SfizzVstParameters.h" +#include "SfizzVstIDs.h" +#include "plugin/InstrumentDescription.h" +#include "base/source/fstreamer.h" +#include "base/source/updatehandler.h" +#include +#include +#include +#include + +enum { kProgramListID = 0 }; + +tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) +{ + tresult result = EditControllerEx1::initialize(context); + if (result != kResultTrue) + return result; + + // initialize the update handler + Steinberg::UpdateHandler::instance(); + + // initialize the thread checker + threadChecker_ = Vst::ThreadChecker::create(); + + // create update objects + queuedUpdates_ = Steinberg::owned(new QueuedUpdates); + sfzUpdate_ = Steinberg::owned(new SfzUpdate); + sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate); + scalaUpdate_ = Steinberg::owned(new ScalaUpdate); + playStateUpdate_ = Steinberg::owned(new PlayStateUpdate); + + // Unit + addUnit(new Vst::Unit(Steinberg::String("Root"), Vst::kRootUnitId, Vst::kNoParentUnitId, kProgramListID)); + + // Parameters + Vst::ParamID pid = 0; + + // Ordinary parameters + parameters.addParameter( + SfizzRange::getForParameter(kPidVolume).createParameter( + Steinberg::String("Volume"), pid++, Steinberg::String("dB"), + 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidNumVoices).createParameter( + Steinberg::String("Polyphony"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidOversampling).createParameter( + Steinberg::String("Oversampling"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidPreloadSize).createParameter( + Steinberg::String("Preload size"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidScalaRootKey).createParameter( + Steinberg::String("Scala root key"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidTuningFrequency).createParameter( + Steinberg::String("Tuning frequency"), pid++, Steinberg::String("Hz"), + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidStretchedTuning).createParameter( + Steinberg::String("Stretched tuning"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidSampleQuality).createParameter( + Steinberg::String("Sample quality"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidOscillatorQuality).createParameter( + Steinberg::String("Oscillator quality"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidFreewheelingSampleQuality).createParameter( + Steinberg::String("Freewheeling sample quality"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidFreewheelingOscillatorQuality).createParameter( + Steinberg::String("Freewheeling oscillator quality"), pid++, nullptr, + 0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidSustainCancelsRelease).createParameter( + Steinberg::String("Sustain cancels release"), pid++, nullptr, + 1, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId)); + + // MIDI special controllers + parameters.addParameter( + SfizzRange::getForParameter(kPidAftertouch).createParameter( + Steinberg::String("Aftertouch"), pid++, nullptr, + 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); + parameters.addParameter( + SfizzRange::getForParameter(kPidPitchBend).createParameter( + Steinberg::String("Pitch bend"), pid++, nullptr, + 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); + + // MIDI controllers + for (unsigned i = 0; i < sfz::config::numCCs; ++i) { + Steinberg::String title; + Steinberg::String shortTitle; + title.printf("Controller %u", i); + shortTitle.printf("CC%u", i); + + parameters.addParameter( + SfizzRange::getForParameter(kPidCC0 + i).createParameter( + title, pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, + Vst::kRootUnitId, shortTitle)); + } + + // Volume levels + for (unsigned i = 0; i < 16; ++i) { + Steinberg::String title; + title.printf("Level %u", i); + + parameters.addParameter( + SfizzRange::getForParameter(kPidLevel0 + i).createParameter( + title, pid++, nullptr, 0, + Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId)); + } + + // Editor status + parameters.addParameter( + SfizzRange::getForParameter(kPidEditorOpen).createParameter( + Steinberg::String("Editor open"), pid++, nullptr, + 0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId)); + + // Initial MIDI mapping + for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) { + Vst::ParamID id = Vst::kNoParamId; + switch (i) { + case Vst::kAfterTouch: + id = kPidAftertouch; + break; + case Vst::kPitchBend: + id = kPidPitchBend; + break; + default: + if (i < 128) + id = kPidCC0 + i; + break; + } + midiMapping_[i] = id; + } + + // Program list + IPtr list = Steinberg::owned( + new Vst::ProgramListWithPitchNames(Steinberg::String("Programs"), kProgramListID, Vst::kRootUnitId)); + list->addProgram(Steinberg::String("Default")); + addProgramList(list); + list->addRef(); + + // Use linear knobs + setKnobMode(kLinearMode); + + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstControllerNoUi::terminate() +{ + return EditControllerEx1::terminate(); +} + +tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) +{ + if (midiControllerNumber < 0 || midiControllerNumber >= Vst::kCountCtrlNumber) { + id = Vst::kNoParamId; + return kResultFalse; + } + + id = midiMapping_[midiControllerNumber]; + if (id == Vst::kNoParamId) + return kResultFalse; + + return kResultTrue; +} + +int32 PLUGIN_API SfizzVstControllerNoUi::getKeyswitchCount(int32 busIndex, int16 channel) +{ + (void)channel; + + if (busIndex != 0) + return 0; + + return keyswitches_.size(); +} + +tresult PLUGIN_API SfizzVstControllerNoUi::getKeyswitchInfo(int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) +{ + (void)channel; + + if (busIndex != 0) + return kResultFalse; + + if (keySwitchIndex < 0 || keySwitchIndex >= keyswitches_.size()) + return kResultFalse; + + info = keyswitches_[keySwitchIndex]; + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstControllerNoUi::beginEditFromHost(Vst::ParamID paramID) +{ + // Note(jpc) implementing this interface is a workaround to make + // non-automatable parameters editable in Ardour (as of 6.6) + (void)paramID; + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstControllerNoUi::endEditFromHost(Vst::ParamID paramID) +{ + (void)paramID; + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) +{ + switch (tag) { + case kPidOversampling: + { + const SfizzRange range = SfizzRange::getForParameter(tag); + const int factorLog2 = static_cast(range.denormalize(valueNormalized)); + Steinberg::String buf; + buf.printf("%dX", 1 << factorLog2); + buf.copyTo(string); + return kResultTrue; + } + } + + return EditControllerEx1::getParamStringByValue(tag, valueNormalized, string); +} + +tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) +{ + switch (tag) { + case kPidOversampling: + { + int32 factor; + if (!Steinberg::String::scanInt32(string, factor, false)) + factor = 1; + + const SfizzRange range = SfizzRange::getForParameter(tag); + valueNormalized = range.normalize(integerLog2(factor)); + return kResultTrue; + } + } + + return EditControllerEx1::getParamValueByString(tag, string, valueNormalized); +} + +tresult SfizzVstControllerNoUi::setParam(Vst::ParamID tag, float value) +{ + const SfizzRange range = SfizzRange::getForParameter(tag); + return setParamNormalized(tag, range.normalize(value)); +} + +tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) +{ + SfizzVstState s; + + tresult r = s.load(stream); + if (r != kResultTrue) + return r; + + setParam(kPidVolume, s.volume); + setParam(kPidNumVoices, s.numVoices); + setParam(kPidOversampling, s.oversamplingLog2); + setParam(kPidPreloadSize, s.preloadSize); + setParam(kPidScalaRootKey, s.scalaRootKey); + setParam(kPidTuningFrequency, s.tuningFrequency); + setParam(kPidStretchedTuning, s.stretchedTuning); + setParam(kPidSampleQuality, s.sampleQuality); + setParam(kPidOscillatorQuality, s.oscillatorQuality); + setParam(kPidFreewheelingSampleQuality, s.freewheelingSampleQuality); + setParam(kPidFreewheelingOscillatorQuality, s.freewheelingOscillatorQuality); + setParam(kPidSustainCancelsRelease, s.sustainCancelsRelease); + + uint32 ccLimit = uint32(std::min(s.controllers.size(), size_t(sfz::config::numCCs))); + for (uint32 cc = 0; cc < ccLimit; ++cc) { + if (absl::optional value = s.controllers[cc]) + setParam(kPidCC0 + cc, *value); + } + + sfzUpdate_->setPath(s.sfzFile); + sfzUpdate_->deferUpdate(); + scalaUpdate_->setPath(s.scalaFile); + scalaUpdate_->deferUpdate(); + + return kResultTrue; +} + +tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) +{ + // Note: is expected to be called from the controller thread only + + tresult result = EditControllerEx1::notify(message); + if (result != kResultFalse) + return result; + + if (!threadChecker_->test()) { + static std::atomic_bool warn_once_flag { false }; + if (!warn_once_flag.exchange(true)) + fprintf(stderr, "[sfizz] controller notification arrives from the wrong thread\n"); + } + + const char* id = message->getMessageID(); + + /// + if (!strcmp(id, SfzUpdate::getFClassID())) { + if (!sfzUpdate_->convertFromMessage(*message)) { + // assert(false); + return kResultFalse; + } + + // update the program name and notify + std::string name = fs::u8path(sfzUpdate_->getPath()).filename().u8string(); + if (absl::EndsWithIgnoreCase(name, ".sfz")) + name.resize(name.size() - 4); + setProgramName(kProgramListID, 0, Steinberg::String(name.c_str())); + + FUnknownPtr unitHandler(getComponentHandler()); + if (unitHandler) + unitHandler->notifyProgramListChange(kProgramListID, 0); + + // + sfzUpdate_->deferUpdate(); + } + else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) { + if (!sfzDescriptionUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } + + // parse the description blob + const InstrumentDescription desc = parseDescriptionBlob( + sfzDescriptionUpdate_->getDescription()); + + // update pitch names and notify + Vst::ProgramListWithPitchNames* list = + static_cast(getProgramList(kProgramListID)); + for (int16 pitch = 0; pitch < 128; ++pitch) { + Steinberg::String pitchName; + if (desc.keyUsed.test(pitch) && !desc.keyLabel[pitch].empty()) + pitchName = Steinberg::String(desc.keyLabel[pitch].c_str()); + else if (desc.keyswitchUsed.test(pitch) && !desc.keyswitchLabel[pitch].empty()) + pitchName = Steinberg::String(desc.keyswitchLabel[pitch].c_str()); + + list->setPitchName(0, pitch, pitchName); + } + + FUnknownPtr unitHandler(getComponentHandler()); + if (unitHandler) + unitHandler->notifyProgramListChange(kProgramListID, 0); + + // update the key switches and notify + size_t idKeyswitch = 0; + for (int16 pitch = 0; pitch < 128; ++pitch) + idKeyswitch += desc.keyswitchUsed.test(pitch); + keyswitches_.resize(idKeyswitch); + + idKeyswitch = 0; + for (int16 pitch = 0; pitch < 128; ++pitch) { + if (!desc.keyswitchUsed.test(pitch)) + continue; + Vst::KeyswitchInfo info {}; + info.typeId = Vst::kNoteOnKeyswitchTypeID; + Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.title); + Steinberg::String(desc.keyswitchLabel[pitch].c_str()).copyTo(info.shortTitle); + info.keyswitchMin = pitch; // TODO reexamine this when supporting keyswitch groups + info.keyswitchMax = pitch; // TODO reexamine this when supporting keyswitch groups + info.keyRemapped = pitch; + info.unitId = Vst::kRootUnitId; + info.flags = 0; + keyswitches_[idKeyswitch++] = info; + } + + if (Vst::IComponentHandler* componentHandler = getComponentHandler()) + componentHandler->restartComponent(Vst::kKeyswitchChanged); + + // update the parameter titles and notify + for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { + Vst::ParamID pid = kPidCC0 + cc; + Vst::Parameter* param = getParameterObject(pid); + Vst::ParameterInfo& info = param->getInfo(); + Steinberg::String title; + Steinberg::String shortTitle; + if (!desc.ccLabel[cc].empty()) { + title = desc.ccLabel[cc].c_str(); + shortTitle = title; + } + else { + title.printf("Controller %u", cc); + shortTitle.printf("CC%u", cc); + } + title.copyTo(info.title); + shortTitle.copyTo(info.shortTitle); + } + + if (Vst::IComponentHandler* componentHandler = getComponentHandler()) + componentHandler->restartComponent(Vst::kParamTitlesChanged); + + // + sfzDescriptionUpdate_->deferUpdate(); + } + else if (!strcmp(id, ScalaUpdate::getFClassID())) { + if (!scalaUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } + scalaUpdate_->deferUpdate(); + } + else if (!strcmp(id, PlayStateUpdate::getFClassID())) { + if (!playStateUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } + playStateUpdate_->deferUpdate(); + } + else if (!strcmp(id, OSCUpdate::getFClassID())) { + IPtr update = OSCUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); + } + else if (!strcmp(id, NoteUpdate::getFClassID())) { + IPtr update = NoteUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); + } + else if (!strcmp(id, AutomationUpdate::getFClassID())) { + IPtr update = AutomationUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } + for (AutomationUpdate::Item item : update->getItems()) + setParam(item.first, item.second); + } + + return result; +} + +// --- Controller with UI --- // + +IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) +{ + ConstString name(_name); + + fprintf(stderr, "[sfizz] about to create view: %s\n", _name); + + if (name != Vst::ViewType::kEditor) + return nullptr; + + std::vector updates; + updates.push_back(queuedUpdates_); + updates.push_back(sfzUpdate_); + updates.push_back(sfzDescriptionUpdate_); + updates.push_back(scalaUpdate_); + updates.push_back(playStateUpdate_); + for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) + updates.push_back(parameters.getParameterByIndex(i)); + + IPtr editor = Steinberg::owned( + new SfizzVstEditor(this, absl::MakeSpan(updates))); + + editor->remember(); + return editor; +} + +FUnknown* SfizzVstController::createInstance(void*) +{ + return static_cast(new SfizzVstController); +} + +template <> +FUnknown* createInstance(void* context) +{ + return SfizzVstController::createInstance(context); +} + +FUID SfizzVstController::cid = SfizzVstController_cid; diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h new file mode 100644 index 0000000..c26e4b1 --- /dev/null +++ b/plugins/vst/SfizzVstController.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstState.h" +#include "SfizzVstUpdates.h" +#include "public.sdk/source/vst/vsteditcontroller.h" +#include "public.sdk/source/vst/vstparameters.h" +#include "public.sdk/source/common/threadchecker.h" +#include "pluginterfaces/vst/ivstmidicontrollers.h" +#include "pluginterfaces/vst/ivstnoteexpression.h" +#include "vstgui/plugin-bindings/vst3editor.h" +#include +#include +class SfizzVstState; +class SfizzVstEditor; + +using namespace Steinberg; +using namespace VSTGUI; + +class SfizzVstControllerNoUi : public Vst::EditControllerEx1, + public Vst::IMidiMapping, + public Vst::IKeyswitchController, + public Vst::IEditControllerHostEditing { +public: + virtual ~SfizzVstControllerNoUi() {} + + tresult PLUGIN_API initialize(FUnknown* context) override; + tresult PLUGIN_API terminate() override; + + tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) override; + + int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel) override; + tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, Vst::KeyswitchInfo& info) override; + + tresult PLUGIN_API beginEditFromHost(Vst::ParamID paramID) override; + tresult PLUGIN_API endEditFromHost(Vst::ParamID paramID) override; + + tresult PLUGIN_API getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) override; + tresult PLUGIN_API getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) override; + + tresult setParam(Vst::ParamID tag, float value); + tresult PLUGIN_API setComponentState(IBStream* stream) override; + tresult PLUGIN_API notify(Vst::IMessage* message) override; + + // interfaces + OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditControllerEx1) + DEFINE_INTERFACES + DEF_INTERFACE(Vst::IMidiMapping) + DEF_INTERFACE(Vst::IKeyswitchController) + DEF_INTERFACE(Vst::IEditControllerHostEditing) + END_DEFINE_INTERFACES(Vst::EditControllerEx1) + REFCOUNT_METHODS(Vst::EditControllerEx1) + +protected: + std::unique_ptr threadChecker_; + Steinberg::IPtr queuedUpdates_; + Steinberg::IPtr sfzUpdate_; + Steinberg::IPtr sfzDescriptionUpdate_; + Steinberg::IPtr scalaUpdate_; + Steinberg::IPtr playStateUpdate_; + Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {}; + std::vector keyswitches_; +}; + +class SfizzVstController : public SfizzVstControllerNoUi, public VSTGUI::VST3EditorDelegate { +public: + IPlugView* PLUGIN_API createView(FIDString name) override; + + static FUnknown* createInstance(void*); + + static FUID cid; +}; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp new file mode 100644 index 0000000..c401535 --- /dev/null +++ b/plugins/vst/SfizzVstEditor.cpp @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstEditor.h" +#include "SfizzVstState.h" +#include "SfizzVstParameters.h" +#include "SfizzVstUpdates.h" +#include "editor/Editor.h" +#include "editor/EditIds.h" +#include "plugin/SfizzFileScan.h" +#include "plugin/InstrumentDescription.h" +#include "pluginterfaces/vst/ivsthostapplication.h" +#include "IdleUpdateHandler.h" +#if !defined(__APPLE__) && !defined(_WIN32) +#include "X11RunLoop.h" +#endif +#include +#include + +using namespace VSTGUI; + +static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight }; + +enum { + kOscTempSize = 8192, + kOscQueueSize = 65536, + kNoteEventQueueSize = 8192, +}; + +SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span updates) + : VSTGUIEditor(controller, &sfizzUiViewRect), + oscTemp_(new uint8_t[kOscTempSize]), + updates_(updates.begin(), updates.end()) +{ +} + +SfizzVstEditor::~SfizzVstEditor() +{ +} + +bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType) +{ + fprintf(stderr, "[sfizz] about to open view with parent %p\n", parent); + + CRect wsize(0, 0, sfizzUiViewRect.getWidth(), sfizzUiViewRect.getHeight()); + CFrame *frame = new CFrame(wsize, this); + this->frame = frame; + + IPlatformFrameConfig* config = nullptr; + +#if !defined(__APPLE__) && !defined(_WIN32) + X11::FrameConfig x11config; + if (!_runLoop) + _runLoop = new RunLoop(plugFrame); + x11config.runLoop = _runLoop; + config = &x11config; +#endif + + Editor* editor = new Editor(*this); + editor_.reset(editor); + + if (!frame->open(parent, platformType, config)) { + fprintf(stderr, "[sfizz] error opening frame\n"); + return false; + } + + editor->open(*frame); + + for (FObject* update : updates_) + update->addDependent(this); + + threadChecker_ = Vst::ThreadChecker::create(); + + parametersToUpdate_.clear(); + + Steinberg::IdleUpdateHandler::start(); + + for (FObject* update : updates_) + update->deferUpdate(); + + // let the editor know about plugin format + absl::string_view pluginFormatName = "VST3"; + + if (FUnknownPtr app { controller->getHostContext() }) { + Vst::String128 name; + app->getName(name); + uiReceiveValue(EditId::PluginHost, std::string(Steinberg::String(name).text8())); + + void* interfacePtr; + if (app->queryInterface(Vst::IVst3ToAUWrapper_iid, &interfacePtr) == kResultTrue) + pluginFormatName = "Audio Unit"; + else if (app->queryInterface(Vst::IVst3ToVst2Wrapper_iid, &interfacePtr) == kResultTrue) + pluginFormatName = "VST2"; + else if (app->queryInterface(Vst::IVst3ToAAXWrapper_iid, &interfacePtr) == kResultTrue) + pluginFormatName = "AAX"; + } + + uiReceiveValue(EditId::PluginFormat, std::string(pluginFormatName)); + + absl::optional userFilesDir = SfizzPaths::getSfzConfigDefaultPath(); + uiReceiveValue(EditId::CanEditUserFilesDir, 1); + uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string()); + uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string()); + + updateEditorIsOpenParameter(); + + return true; +} + +void PLUGIN_API SfizzVstEditor::close() +{ + CFrame *frame = this->frame; + if (frame) { + Steinberg::IdleUpdateHandler::stop(); + + for (FObject* update : updates_) + update->removeDependent(this); + + if (editor_) { + editor_->close(); + editor_ = nullptr; + } + + if (frame->getNbReference() != 1) + frame->forget(); + else { + frame->close(); +#if !defined(__APPLE__) && !defined(_WIN32) + // if vstgui is done using the runloop, destroy it + if (!RunLoop::get()) + _runLoop = nullptr; +#endif + } + this->frame = nullptr; + } + + updateEditorIsOpenParameter(); +} + +void SfizzVstEditor::updateEditorIsOpenParameter() +{ + SfizzVstController* ctrl = getController(); + bool editorIsOpen = frame && frame->isVisible(); + ctrl->setParamNormalized(kPidEditorOpen, editorIsOpen); + ctrl->performEdit(kPidEditorOpen, editorIsOpen); +} + +/// +CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) +{ + CMessageResult result = VSTGUIEditor::notify(sender, message); + + if (result != kMessageNotified) + return result; + +#if !defined(__APPLE__) && !defined(_WIN32) + if (message == CVSTGUITimer::kMsgTimer) { + SharedPointer runLoop = RunLoop::get(); + if (runLoop) { + // note(jpc) I don't find a reliable way to check if the host + // notifier of X11 events is working. If there is, remove this and + // avoid polluting Linux hosts which implement the loop correctly. + runLoop->processSomeEvents(); + } + } +#endif + + if (message == CVSTGUITimer::kMsgTimer) { + processParameterUpdates(); + updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time + } + + return result; +} + +void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) +{ + if (processUpdate(changedUnknown, message)) + return; + + Vst::VSTGUIEditor::update(changedUnknown, message); +} + +bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message) +{ + if (QueuedUpdates* update = FCast(changedUnknown)) { + for (FObject* queuedUpdate : update->getUpdates(this)) + processUpdate(queuedUpdate, message); + return true; + } + + if (OSCUpdate* update = FCast(changedUnknown)) { + const uint8* oscData = update->data(); + uint32 oscSize = update->size(); + + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + + uint32_t msgSize; + while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + uiReceiveMessage(path, sig, args); + oscData += msgSize; + oscSize -= msgSize; + } + + return true; + } + + if (NoteUpdate* update = FCast(changedUnknown)) { + const NoteUpdate::Item* events = update->events(); + uint32 count = update->count(); + for (uint32 i = 0; i < count; ++i) + uiReceiveValue(editIdForKey(events[i].first), events[i].second); + return true; + } + + if (SfzUpdate* update = FCast(changedUnknown)) { + const std::string path = update->getPath(); + uiReceiveValue(EditId::SfzFile, path); + return true; + } + + if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { + const InstrumentDescription desc = parseDescriptionBlob(update->getDescription()); + + uiReceiveValue(EditId::UINumCurves, desc.numCurves); + uiReceiveValue(EditId::UINumMasters, desc.numMasters); + uiReceiveValue(EditId::UINumGroups, desc.numGroups); + uiReceiveValue(EditId::UINumRegions, desc.numRegions); + uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); + + const fs::path rootPath = fs::u8path(desc.rootPath); + const fs::path imagePath = rootPath / fs::u8path(desc.image); + uiReceiveValue(EditId::BackgroundImage, imagePath.u8string()); + + const fs::path ctrlImagePath = rootPath / fs::u8path(desc.image_controls); + uiReceiveValue(EditId::ControlsImage, ctrlImagePath.u8string()); + + for (unsigned key = 0; key < 128; ++key) { + bool keyUsed = desc.keyUsed.test(key); + bool keyswitchUsed = desc.keyswitchUsed.test(key); + uiReceiveValue(editIdForKeyUsed(int(key)), float(keyUsed)); + uiReceiveValue(editIdForKeyswitchUsed(int(key)), float(keyswitchUsed)); + if (keyUsed) + uiReceiveValue(editIdForKeyLabel(int(key)), desc.keyLabel[key]); + if (keyswitchUsed) + uiReceiveValue(editIdForKeyswitchLabel(int(key)), desc.keyswitchLabel[key]); + } + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); + uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed)); + if (ccUsed) { + uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]); + uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]); + } + } + return true; + } + + if (ScalaUpdate* update = FCast(changedUnknown)) { + const std::string path = update->getPath(); + uiReceiveValue(EditId::ScalaFile, path); + return true; + } + + if (PlayStateUpdate* update = FCast(changedUnknown)) { + const SfizzPlayState playState = update->getState(); + uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); + return true; + } + + if (Vst::RangeParameter* param = Steinberg::FCast(changedUnknown)) { + // Note(jpc) some hosts send us the parameters in the wrong thread... + // store these parameters thread-safely and let the idle + // callback process them later + if (threadChecker_->test()) + updateParameter(param); + else { + static std::atomic_bool warn_once_flag { false }; + if (!warn_once_flag.exchange(true)) + fprintf(stderr, "[sfizz] using a thread-safety workaround for parameter updates\n"); + const Vst::ParamID id = param->getInfo().id; + std::lock_guard lock(parametersToUpdateMutex_); + parametersToUpdate_.insert(id); + } + return true; + } + + return false; +} + +void SfizzVstEditor::processParameterUpdates() +{ + auto extractNextParamID = [this]() -> Vst::ParamID { + Vst::ParamID id = Vst::kNoParamId; + std::lock_guard lock(parametersToUpdateMutex_); + auto it = parametersToUpdate_.begin(); + if (it != parametersToUpdate_.end()) { + id = *it; + parametersToUpdate_.erase(it); + } + return id; + }; + + for (Vst::ParamID id; (id = extractNextParamID()) != Vst::kNoParamId; ) + updateParameter(getController()->getParameterObject(id)); +} + +void SfizzVstEditor::updateParameter(Vst::Parameter* parameterToUpdate) +{ + if (Vst::RangeParameter* param = FCast(parameterToUpdate)) { + const Vst::ParamID id = param->getInfo().id; + const Vst::ParamValue value = param->getNormalized(); + const SfizzRange range = SfizzRange::getForParameter(id); + switch (id) { + case kPidVolume: + uiReceiveValue(EditId::Volume, range.denormalize(value)); + break; + case kPidNumVoices: + uiReceiveValue(EditId::Polyphony, range.denormalize(value)); + break; + case kPidOversampling: + uiReceiveValue(EditId::Oversampling, float(1u << (int32)range.denormalize(value))); + break; + case kPidPreloadSize: + uiReceiveValue(EditId::PreloadSize, range.denormalize(value)); + break; + case kPidScalaRootKey: + uiReceiveValue(EditId::ScalaRootKey, range.denormalize(value)); + break; + case kPidTuningFrequency: + uiReceiveValue(EditId::TuningFrequency, range.denormalize(value)); + break; + case kPidStretchedTuning: + uiReceiveValue(EditId::StretchTuning, range.denormalize(value)); + break; + case kPidSampleQuality: + uiReceiveValue(EditId::SampleQuality, range.denormalize(value)); + break; + case kPidOscillatorQuality: + uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value)); + break; + case kPidFreewheelingSampleQuality: + uiReceiveValue(EditId::FreewheelingSampleQuality, range.denormalize(value)); + break; + case kPidFreewheelingOscillatorQuality: + uiReceiveValue(EditId::FreewheelingOscillatorQuality, range.denormalize(value)); + break; + case kPidSustainCancelsRelease: + uiReceiveValue(EditId::SustainCancelsRelease, range.denormalize(value)); + break; + case kPidNumOutputs: + uiReceiveValue(EditId::PluginOutputs, (int32)range.denormalize(value)); + break; + default: + if (id >= kPidCC0 && id <= kPidCCLast) { + int cc = int(id - kPidCC0); + uiReceiveValue(editIdForCC(cc), range.denormalize(value)); + } else if (id >= kPidLevel0 && id <= kPidLevelLast) { + int levelId = int(id - kPidLevel0); + uiReceiveValue(editIdForLevel(levelId), range.denormalize(value)); + } + break; + } + } +} + +/// +void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v) +{ + if (id == EditId::SfzFile) + loadSfzFile(v.to_string()); + else if (id == EditId::ScalaFile) + loadScalaFile(v.to_string()); + else { + SfizzVstController* ctrl = getController(); + + auto normalizeAndSet = [ctrl](Vst::ParamID pid, float value) { + float normValue = SfizzRange::getForParameter(pid).normalize(value); + ctrl->setParamNormalized(pid, normValue); + ctrl->performEdit(pid, normValue); + }; + + switch (id) { + case EditId::Volume: + normalizeAndSet(kPidVolume, v.to_float()); + break; + case EditId::Polyphony: + normalizeAndSet(kPidNumVoices, v.to_float()); + break; + case EditId::Oversampling: + { + const int32 factor = static_cast(v.to_float()); + normalizeAndSet(kPidOversampling, integerLog2(factor)); + } + break; + case EditId::PreloadSize: + normalizeAndSet(kPidPreloadSize, v.to_float()); + break; + case EditId::ScalaRootKey: + normalizeAndSet(kPidScalaRootKey, v.to_float()); + break; + case EditId::TuningFrequency: + normalizeAndSet(kPidTuningFrequency, v.to_float()); + break; + case EditId::StretchTuning: + normalizeAndSet(kPidStretchedTuning, v.to_float()); + break; + case EditId::SampleQuality: + normalizeAndSet(kPidSampleQuality, v.to_float()); + break; + case EditId::OscillatorQuality: + normalizeAndSet(kPidOscillatorQuality, v.to_float()); + break; + case EditId::FreewheelingSampleQuality: + normalizeAndSet(kPidFreewheelingSampleQuality, v.to_float()); + break; + case EditId::FreewheelingOscillatorQuality: + normalizeAndSet(kPidFreewheelingOscillatorQuality, v.to_float()); + break; + case EditId::SustainCancelsRelease: + normalizeAndSet(kPidSustainCancelsRelease, v.to_float()); + break; + case EditId::UserFilesDir: + SfizzPaths::setSfzConfigDefaultPath(fs::u8path(v.to_string())); + break; + + default: + if (editIdIsCC(id)) + normalizeAndSet(kPidCC0 + ccForEditId(id), v.to_float()); + break; + } + } +} + +void SfizzVstEditor::uiBeginSend(EditId id) +{ + Vst::ParamID pid = parameterOfEditId(id); + if (pid != Vst::kNoParamId) + getController()->beginEdit(pid); +} + +void SfizzVstEditor::uiEndSend(EditId id) +{ + Vst::ParamID pid = parameterOfEditId(id); + if (pid != Vst::kNoParamId) + getController()->endEdit(pid); +} + +void SfizzVstEditor::uiSendMIDI(const uint8_t* data, uint32_t len) +{ + SfizzVstController* ctl = getController(); + + Steinberg::OPtr msg { ctl->allocateMessage() }; + if (!msg) { + fprintf(stderr, "[Sfizz] UI could not allocate message\n"); + return; + } + + msg->setMessageID("MidiMessage"); + Vst::IAttributeList* attr = msg->getAttributes(); + attr->setBinary("Data", data, len); + ctl->sendMessage(msg); +} + +void SfizzVstEditor::uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) +{ + SfizzVstController* ctl = getController(); + + Steinberg::OPtr msg { ctl->allocateMessage() }; + if (!msg) { + fprintf(stderr, "[Sfizz] UI could not allocate message\n"); + return; + } + + uint8_t* oscTemp = oscTemp_.get(); + uint32_t oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args); + if (oscSize <= kOscTempSize) { + msg->setMessageID("OscMessage"); + Vst::IAttributeList* attr = msg->getAttributes(); + attr->setBinary("Data", oscTemp, oscSize); + ctl->sendMessage(msg); + } +} + +/// +void SfizzVstEditor::loadSfzFile(const std::string& filePath) +{ + SfizzVstController* ctl = getController(); + + Steinberg::OPtr msg { ctl->allocateMessage() }; + if (!msg) { + fprintf(stderr, "[Sfizz] UI could not allocate message\n"); + return; + } + + msg->setMessageID("LoadSfz"); + Vst::IAttributeList* attr = msg->getAttributes(); + attr->setBinary("File", filePath.data(), filePath.size()); + ctl->sendMessage(msg); +} + +void SfizzVstEditor::loadScalaFile(const std::string& filePath) +{ + SfizzVstController* ctl = getController(); + + Steinberg::OPtr msg { ctl->allocateMessage() }; + if (!msg) { + fprintf(stderr, "[Sfizz] UI could not allocate message\n"); + return; + } + + msg->setMessageID("LoadScala"); + Vst::IAttributeList* attr = msg->getAttributes(); + attr->setBinary("File", filePath.data(), filePath.size()); + ctl->sendMessage(msg); +} + +Vst::ParamID SfizzVstEditor::parameterOfEditId(EditId id) +{ + switch (id) { + case EditId::Volume: return kPidVolume; + case EditId::Polyphony: return kPidNumVoices; + case EditId::Oversampling: return kPidOversampling; + case EditId::PreloadSize: return kPidPreloadSize; + case EditId::ScalaRootKey: return kPidScalaRootKey; + case EditId::TuningFrequency: return kPidTuningFrequency; + case EditId::StretchTuning: return kPidStretchedTuning; + case EditId::SampleQuality: return kPidSampleQuality; + case EditId::OscillatorQuality: return kPidOscillatorQuality; + default: + if (editIdIsCC(id)) + return kPidCC0 + ccForEditId(id); + else if (editIdIsLevel(id)) + return kPidLevel0 + levelForEditId(id); + return Vst::kNoParamId; + } +} diff --git a/plugins/vst/SfizzVstEditor.h b/plugins/vst/SfizzVstEditor.h new file mode 100644 index 0000000..9ced765 --- /dev/null +++ b/plugins/vst/SfizzVstEditor.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstController.h" +#include "editor/EditorController.h" +#include "public.sdk/source/vst/vstguieditor.h" +#include "public.sdk/source/common/threadchecker.h" +#include +#include +#include +class Editor; +#if !defined(__APPLE__) && !defined(_WIN32) +namespace VSTGUI { class RunLoop; } +#endif + +using namespace Steinberg; +using namespace VSTGUI; + +class SfizzVstEditor : public Vst::VSTGUIEditor, + public EditorController { +public: + using Self = SfizzVstEditor; + + SfizzVstEditor(SfizzVstController* controller, absl::Span updates); + ~SfizzVstEditor(); + + bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override; + void PLUGIN_API close() override; + + SfizzVstController* getController() const + { + return static_cast(Vst::VSTGUIEditor::getController()); + } + + void updateEditorIsOpenParameter(); + + // VSTGUIEditor + CMessageResult notify(CBaseObject* sender, const char* message) override; + // FObject + void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; + + // +private: + bool processUpdate(FUnknown* changedUnknown, int32 message); + void processParameterUpdates(); + void updateParameter(Vst::Parameter* parameterToUpdate); + +protected: + // EditorController + void uiSendValue(EditId id, const EditValue& v) override; + void uiBeginSend(EditId id) override; + void uiEndSend(EditId id) override; + void uiSendMIDI(const uint8_t* data, uint32_t len) override; + void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) override; + +private: + void loadSfzFile(const std::string& filePath); + void loadScalaFile(const std::string& filePath); + + Vst::ParamID parameterOfEditId(EditId id); + + std::unique_ptr editor_; + +#if !defined(__APPLE__) && !defined(_WIN32) + SharedPointer _runLoop; +#endif + + // messaging + std::unique_ptr oscTemp_; + + // subscribed updates + std::vector> updates_; + + // thread safety + std::unique_ptr threadChecker_; + + // parameters to process, whose values have received changes + // Note(jpc) it's because hosts send us parameter updates in the wrong thread.. + std::set parametersToUpdate_; + std::mutex parametersToUpdateMutex_; +}; diff --git a/plugins/vst/SfizzVstIDs.h b/plugins/vst/SfizzVstIDs.h new file mode 100644 index 0000000..eb4e738 --- /dev/null +++ b/plugins/vst/SfizzVstIDs.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +/* + Note(jpc) Generated at random with uuidgen. + Can't find docs on it... maybe it's to register somewhere? + */ +#define SfizzVstProcessor_cid \ + Steinberg::FUID(0xe8fab718, 0x15ed46e3, 0x8b598310, 0x1e12993f) +#define SfizzVstProcessorMulti_cid \ + Steinberg::FUID(0xc9da9274, 0x43794873, 0xa900ed81, 0xd1946115) +#define SfizzVstController_cid \ + Steinberg::FUID(0x7129736c, 0xbc784134, 0xbb899d56, 0x2ebafe4f) + +template +Steinberg::FUnknown* createInstance(void*); diff --git a/plugins/vst/SfizzVstParameters.h b/plugins/vst/SfizzVstParameters.h new file mode 100644 index 0000000..07d3bb4 --- /dev/null +++ b/plugins/vst/SfizzVstParameters.h @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "sfizz/Config.h" +#include "public.sdk/source/vst/vstparameters.h" +#include + +using namespace Steinberg; + +// parameters +enum { + kPidVolume, + kPidNumVoices, + kPidOversampling, + kPidPreloadSize, + kPidScalaRootKey, + kPidTuningFrequency, + kPidStretchedTuning, + kPidSampleQuality, + kPidOscillatorQuality, + kPidFreewheelingSampleQuality, + kPidFreewheelingOscillatorQuality, + kPidSustainCancelsRelease, + kPidAftertouch, + kPidPitchBend, + kPidCC0, + kPidCCLast = kPidCC0 + sfz::config::numCCs - 1, + kPidNumOutputs, + kPidLevel0, + kPidLevelLast = kPidLevel0 + 16, + kPidEditorOpen, + /* Reserved */ + kNumParameters, +}; + +struct SfizzRange { + float def = 0.0; + float min = 0.0; + float max = 1.0; + + constexpr SfizzRange() {} + constexpr SfizzRange(float def, float min, float max) : def(def), min(min), max(max) {} + + constexpr float normalize(float x) const noexcept + { + return (x - min) / (max - min); + } + + constexpr float denormalize(float x) const noexcept + { + return min + x * (max - min); + } + + Vst::RangeParameter* createParameter(const Vst::TChar *title, Vst::ParamID tag, const Vst::TChar *units = nullptr, int32 stepCount = 0, int32 flags = Vst::ParameterInfo::kCanAutomate, Vst::UnitID unitID = Vst::kRootUnitId, const Vst::TChar *shortTitle = nullptr) const + { + return new Vst::RangeParameter(title, tag, units, min, max, def, stepCount, flags, unitID, shortTitle); + } + + static SfizzRange getForParameter(Vst::ParamID id) + { + switch (id) { + case kPidVolume: + return {0.0, -60.0, +6.0}; + case kPidNumVoices: + return {64.0, 1.0, 256.0}; + case kPidOversampling: + return {0.0, 0.0, 3.0}; + case kPidPreloadSize: + return {8192.0, 1024.0, 65536.0}; + case kPidScalaRootKey: + return {60.0, 0.0, 127.0}; + case kPidTuningFrequency: + return {440.0, 300.0, 500.0}; + case kPidStretchedTuning: + return {0.0, 0.0, 1.0}; + case kPidSampleQuality: + return {2.0, 0.0, 10.0}; + case kPidOscillatorQuality: + return {1.0, 0.0, 3.0}; + case kPidFreewheelingSampleQuality: + return {10.0, 0.0, 10.0}; + case kPidFreewheelingOscillatorQuality: + return {3.0, 0.0, 3.0}; + case kPidSustainCancelsRelease: + return {0.0, 0.0, 1.0}; + case kPidAftertouch: + return {0.0, 0.0, 1.0}; + case kPidPitchBend: + return {0.0, -1.0, 1.0}; + case kPidEditorOpen: + return {0.0, 0.0, 1.0}; + case kPidNumOutputs: + return {2.0, 2.0, 16.0}; + default: + if (id >= kPidCC0 && id <= kPidCCLast) + return {0.0, 0.0, 1.0}; + else if (id >= kPidLevel0 && id <= kPidLevelLast) + return {0.0, 0.0, 1.0}; + throw std::runtime_error("Bad parameter ID"); + } + } +}; + +inline int32 integerLog2(int32 x) +{ + int32 l = 0; + for (; x > 1; x /= 2) ++l; + return l; +} diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp new file mode 100644 index 0000000..dc7ab60 --- /dev/null +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -0,0 +1,981 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstProcessor.h" +#include "SfizzVstController.h" +#include "SfizzVstState.h" +#include "SfizzVstParameters.h" +#include "SfizzVstIDs.h" +#include "sfizz/import/sfizz_import.h" +#include "plugin/SfizzFileScan.h" +#include "plugin/InstrumentDescription.h" +#include "base/source/fstreamer.h" +#include "base/source/updatehandler.h" +#include "pluginterfaces/vst/ivstevents.h" +#include "pluginterfaces/vst/ivstparameterchanges.h" +#include +#include +#include + +static const char defaultSfzText[] = + "sample=*sine" "\n" + "ampeg_attack=0.02 ampeg_release=0.1" "\n"; + +enum { + kMidiEventMaximumSize = 4, + kOscTempSize = 8192, +}; + +static const char* kRingIdMidi = "Mid"; +static const char* kRingIdOsc = "Osc"; + +static const char* kMsgIdSetNumVoices = "SetNumVoices"; +static const char* kMsgIdSetOversampling = "SetOversampling"; +static const char* kMsgIdSetPreloadSize = "SetPreloadSize"; +static const char* kMsgIdReceiveOSC = "ReceiveOSC"; +static const char* kMsgIdNoteEvents = "NoteEvents"; + +static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 }; + +SfizzVstProcessor::SfizzVstProcessor() + : _oscTemp(new uint8_t[kOscTempSize]), + _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024) +{ + setControllerClass(SfizzVstController::cid); + + // ensure the SFZ path exists: + // the one specified in the configuration, otherwise the fallback + absl::optional configDefaultPath = SfizzPaths::getSfzConfigDefaultPath(); + if (configDefaultPath) { + std::error_code ec; + fs::create_directory(*configDefaultPath, ec); + } + else { + fs::path fallbackDefaultPath = SfizzPaths::getSfzFallbackDefaultPath(); + std::error_code ec; + fs::create_directory(fallbackDefaultPath, ec); + } +} + +SfizzVstProcessor::~SfizzVstProcessor() +{ + try { + stopBackgroundWork(); + } catch (const std::exception& e) { + fprintf(stderr, "Caught exception: %s\n", e.what()); + } +} + +tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) +{ + tresult result = AudioEffect::initialize(context); + if (result != kResultTrue) + return result; + + // initialize the update handler + Steinberg::UpdateHandler::instance(); + + _queuedMessages = Steinberg::owned(new QueuedUpdates); + _playStateUpdate = Steinberg::owned(new PlayStateUpdate); + _sfzUpdate = Steinberg::owned(new SfzUpdate); + _sfzDescriptionUpdate = Steinberg::owned(new SfzDescriptionUpdate); + _scalaUpdate = Steinberg::owned(new ScalaUpdate); + _automationUpdate = Steinberg::owned(new AutomationUpdate); + + _queuedMessages->addDependent(this); + _playStateUpdate->addDependent(this); + _sfzUpdate->addDependent(this); + _sfzDescriptionUpdate->addDependent(this); + _scalaUpdate->addDependent(this); + _automationUpdate->addDependent(this); + + addAudioOutput(STR16("Audio Output 1"), Vst::SpeakerArr::kStereo); + addEventInput(STR16("Event Input"), 1); + + _state = SfizzVstState(); + + // allocate needed space to track CC values + _state.controllers.resize(sfz::config::numCCs); + + fprintf(stderr, "[sfizz] new synth\n"); + _synth.reset(new sfz::Sfizz); + + auto onMessage = +[](void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args) + { + auto *self = reinterpret_cast(data); + self->receiveOSC(delay, path, sig, args); + }; + _client = _synth->createClient(this); + _synth->setReceiveCallback(*_client, onMessage); + _synth->setBroadcastCallback(onMessage, this); + + _currentStretchedTuning = 0.0; + loadSfzFileOrDefault({}, false); + + _synth->bpmTempo(0, 120); + _timeSigNumerator = 4; + _timeSigDenominator = 4; + _synth->timeSignature(0, _timeSigNumerator, _timeSigDenominator); + _synth->timePosition(0, 0, 0); + _synth->playbackState(0, 0); + + _noteEventsCurrentCycle.fill(-1.0f); + + _editorIsOpen = false; + + return result; +} + +tresult PLUGIN_API SfizzVstProcessor::terminate() +{ + _queuedMessages->removeDependent(this); + _playStateUpdate->removeDependent(this); + _sfzUpdate->removeDependent(this); + _sfzDescriptionUpdate->removeDependent(this); + _scalaUpdate->removeDependent(this); + _automationUpdate->removeDependent(this); + + return AudioEffect::terminate(); +} + +tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) +{ + bool allStereo { true }; + for (unsigned o = 0; o < numOuts; ++o) + allStereo &= (outputs[o] == Vst::SpeakerArr::kStereo); + + if (!allStereo) + return kResultFalse; + + return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); +} + +tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other) +{ + tresult result = AudioEffect::connect(other); + if (result != kResultTrue) + return result; + + // when controller connects, send these messages that we couldn't earlier + _queuedMessages->deferUpdate(); + + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream) +{ + SfizzVstState s; + + tresult r = s.load(stream); + if (r != kResultTrue) + return r; + + // check the files to really exist, otherwise search them + for (std::string* statePath : { &s.sfzFile, &s.scalaFile }) { + if (statePath->empty()) + continue; + + fs::path pathOrig = fs::u8path(*statePath); + std::error_code ec; + if (fs::is_regular_file(pathOrig, ec)) + continue; + + fprintf(stderr, "[Sfizz] searching for missing file: %s\n", pathOrig.filename().u8string().c_str()); + + SfzFileScan& fileScan = SfzFileScan::getInstance(); + fs::path pathFound; + if (!fileScan.locateRealFile(pathOrig, pathFound)) + fprintf(stderr, "[Sfizz] file not found: %s\n", pathOrig.filename().u8string().c_str()); + else { + fprintf(stderr, "[Sfizz] file found: %s\n", pathFound.u8string().c_str()); + *statePath = pathFound.u8string(); + } + } + + // + std::lock_guard lock(_processMutex); + _state = s; + + // allocate needed space to track CC values + _state.controllers.resize(sfz::config::numCCs); + + syncStateToSynth(); + + return r; +} + +tresult PLUGIN_API SfizzVstProcessor::getState(IBStream* stream) +{ + std::lock_guard lock(_processMutex); + return _state.store(stream); +} + +void SfizzVstProcessor::syncStateToSynth() +{ + sfz::Sfizz* synth = _synth.get(); + + if (!synth) + return; + + loadSfzFileOrDefault(_state.sfzFile, true); + synth->setVolume(_state.volume); + synth->setNumVoices(_state.numVoices); + synth->setOversamplingFactor(1 << _state.oversamplingLog2); + synth->setPreloadSize(_state.preloadSize); + synth->loadScalaFile(_state.scalaFile); + synth->setScalaRootKey(_state.scalaRootKey); + synth->setTuningFrequency(_state.tuningFrequency); + synth->loadStretchTuningByRatio(_state.stretchedTuning); + if (_state.lastKeyswitch >= 0 && _state.lastKeyswitch <= 127) { + synth->hdNoteOn(0, _state.lastKeyswitch, 1.0f); + synth->hdNoteOff(1, _state.lastKeyswitch, 0.0f); + } +} + +tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize) +{ + if (symbolicSampleSize != Vst::kSample32) + return kResultFalse; + + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state) +{ + sfz::Sfizz* synth = _synth.get(); + + if (bool(state) == _isActive) + return kResultTrue; + + if (!synth) + return kResultFalse; + + if (state) { + synth->setSampleRate(processSetup.sampleRate); + synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock); + _rmsFollower.init(processSetup.sampleRate); + initializeEventProcessor(processSetup, kNumParameters); + startBackgroundWork(); + } else { + stopBackgroundWork(); + synth->allSoundOff(); + } + + _isActive = bool(state); + return kResultTrue; +} + +tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) +{ + sfz::Sfizz& synth = *_synth; + + std::unique_lock lock(_processMutex, std::defer_lock); + if (data.processMode == Vst::kOffline) + lock.lock(); + else + (void)lock.try_lock(); + + if (data.processContext) + updateTimeInfo(*data.processContext); + + const uint32 numFrames = data.numSamples; + _canPerformEventsAndParameters = lock.owns_lock(); + bool editorWasOpen = _editorIsOpen; + processUnorderedEvents(numFrames, data.inputParameterChanges, data.inputEvents); + + if (data.numOutputs < 1) // flush mode + return kResultTrue; + + constexpr uint32 numChannels = 2; + constexpr uint32 maxChannels = 16; + float* outputs[maxChannels]; + const auto numMonoChannels = data.numOutputs * numChannels; + + for (unsigned o = 0; o < data.numOutputs; ++o) { + assert(data.outputs[o].numChannels == numChannels); + for (unsigned c = 0; c < numChannels; ++c) + outputs[numChannels * o + c] = data.outputs[o].channelBuffers32[c]; + } + + if (!editorWasOpen && _editorIsOpen) { + if (Vst::IParameterChanges* pcs = data.outputParameterChanges) { + int32 index; + if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidNumOutputs, index)) + vq->addPoint(0, SfizzRange::getForParameter(kPidNumOutputs).normalize(numMonoChannels), index); + } + } + + if (!lock.owns_lock()) { + for (unsigned c = 0; c < numChannels; ++c) + std::memset(outputs[c], 0, numFrames * sizeof(float)); + + for (unsigned o = 0; o < data.numOutputs; ++o) + data.outputs[o].silenceFlags = 3; + + return kResultTrue; + } + + if (data.processMode == Vst::kOffline) + synth.enableFreeWheeling(); + else + synth.disableFreeWheeling(); + + processMessagesFromUi(); + + synth.setVolume(_state.volume); + synth.setScalaRootKey(_state.scalaRootKey); + synth.setTuningFrequency(_state.tuningFrequency); + if (_currentStretchedTuning != _state.stretchedTuning) { + synth.loadStretchTuningByRatio(_state.stretchedTuning); + _currentStretchedTuning = _state.stretchedTuning; + } + synth.setSampleQuality(sfz::Sfizz::ProcessLive, _state.sampleQuality); + synth.setOscillatorQuality(sfz::Sfizz::ProcessLive, _state.oscillatorQuality); + synth.setSampleQuality(sfz::Sfizz::ProcessFreewheeling, _state.freewheelingSampleQuality); + synth.setOscillatorQuality(sfz::Sfizz::ProcessFreewheeling, _state.freewheelingOscillatorQuality); + synth.setSustainCancelsRelease(_state.sustainCancelsRelease); + + synth.renderBlock(outputs, numFrames, data.numOutputs); + + // Update levels, if editor is open, otherwise skip + RMSFollower& rmsFollower = _rmsFollower; + if (_editorIsOpen) { + rmsFollower.process((const float**)outputs, numFrames, numMonoChannels); + float levels[maxChannels]; + rmsFollower.getRMS(levels, numMonoChannels); + if (Vst::IParameterChanges* pcs = data.outputParameterChanges) { + int32 index; + for (int c = 0; c < numMonoChannels; ++c) { + if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidLevel0 + c, index)) + vq->addPoint(0, levels[c], index); + } + } + } + else + rmsFollower.clear(); + + // Request OSC updates + sfz::Client& client = *_client; + synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); + + // + NoteUpdate::Item noteEvents[128]; + size_t numNoteEvents = 0; + for (uint32 key = 0; key < 128; ++key) { + float value = _noteEventsCurrentCycle[key]; + if (value < 0.0f) + continue; + noteEvents[numNoteEvents++] = std::make_pair(key, value); + _noteEventsCurrentCycle[key] = -1.0f; + } + if (numNoteEvents > 0) { + if (writeWorkerMessage(kMsgIdNoteEvents, noteEvents, numNoteEvents * sizeof(noteEvents[0]))) + _semaToWorker.post(); + } + + return kResultTrue; +} + +void SfizzVstProcessor::updateTimeInfo(const Vst::ProcessContext& context) +{ + sfz::Sfizz& synth = *_synth; + + if (context.state & context.kTempoValid) + synth.bpmTempo(0, static_cast(context.tempo)); + + if (context.state & context.kTimeSigValid) { + _timeSigNumerator = context.timeSigNumerator; + _timeSigDenominator = context.timeSigDenominator; + synth.timeSignature(0, _timeSigNumerator, _timeSigDenominator); + } + + if (context.state & context.kProjectTimeMusicValid) { + double beats = context.projectTimeMusic * 0.25 * _timeSigDenominator; + double bars = beats / _timeSigNumerator; + beats -= int(bars) * _timeSigNumerator; + synth.timePosition(0, int(bars), beats); + } + + synth.playbackState(0, (context.state & context.kPlaying) != 0); +} + +void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id, Vst::ParamValue value) +{ + if (!_canPerformEventsAndParameters) + return; + + sfz::Sfizz& synth = *_synth; + const SfizzRange range = SfizzRange::getForParameter(id); + + switch (id) { + case kPidVolume: + _state.volume = range.denormalize(value); + break; + case kPidNumVoices: + { + int32 data = static_cast(range.denormalize(value)); + _state.numVoices = data; + if (writeWorkerMessage(kMsgIdSetNumVoices, &data, sizeof(data))) + _semaToWorker.post(); + } + break; + case kPidOversampling: + { + int32 data = static_cast(range.denormalize(value)); + _state.oversamplingLog2 = data; + if (writeWorkerMessage(kMsgIdSetOversampling, &data, sizeof(data))) + _semaToWorker.post(); + } + break; + case kPidPreloadSize: + { + int32 data = static_cast(range.denormalize(value)); + _state.preloadSize = data; + if (writeWorkerMessage(kMsgIdSetPreloadSize, &data, sizeof(data))) + _semaToWorker.post(); + } + break; + case kPidScalaRootKey: + _state.scalaRootKey = static_cast(range.denormalize(value)); + break; + case kPidTuningFrequency: + _state.tuningFrequency = range.denormalize(value); + break; + case kPidStretchedTuning: + _state.stretchedTuning = range.denormalize(value); + break; + case kPidSampleQuality: + _state.sampleQuality = static_cast(range.denormalize(value)); + break; + case kPidOscillatorQuality: + _state.oscillatorQuality = static_cast(range.denormalize(value)); + break; + case kPidFreewheelingSampleQuality: + _state.freewheelingSampleQuality = static_cast(range.denormalize(value)); + break; + case kPidFreewheelingOscillatorQuality: + _state.freewheelingOscillatorQuality = static_cast(range.denormalize(value)); + break; + case kPidSustainCancelsRelease: + _state.sustainCancelsRelease = (range.denormalize(value) > 0.0f); + break; + case kPidAftertouch: + synth.hdChannelAftertouch(sampleOffset, value); + break; + case kPidPitchBend: + synth.hdPitchWheel(sampleOffset, range.denormalize(value)); + break; + case kPidEditorOpen: + _editorIsOpen = value != 0; + break; + default: + if (id >= kPidCC0 && id <= kPidCCLast) { + int32 ccNumber = static_cast(id - kPidCC0); + synth.automateHdcc(sampleOffset, ccNumber, value); + _state.controllers[ccNumber] = value; + } + break; + } +} + +void SfizzVstProcessor::playOrderedEvent(const Vst::Event& event) +{ + if (!_canPerformEventsAndParameters) + return; + + sfz::Sfizz& synth = *_synth; + const int32 sampleOffset = event.sampleOffset; + + switch (event.type) { + case Vst::Event::kNoteOnEvent: { + int pitch = event.noteOn.pitch; + if (pitch < 0 || pitch >= 128) + break; + if (event.noteOn.velocity <= 0.0f) { + synth.noteOff(sampleOffset, pitch, 0); + _noteEventsCurrentCycle[pitch] = 0.0f; + } + else { + synth.hdNoteOn(sampleOffset, pitch, event.noteOn.velocity); + _noteEventsCurrentCycle[pitch] = event.noteOn.velocity; + } + break; + } + case Vst::Event::kNoteOffEvent: { + int pitch = event.noteOn.pitch; + if (pitch < 0 || pitch >= 128) + break; + synth.hdNoteOff(sampleOffset, pitch, event.noteOff.velocity); + _noteEventsCurrentCycle[pitch] = 0.0f; + break; + } + case Vst::Event::kPolyPressureEvent: { + int pitch = event.polyPressure.pitch; + if (pitch < 0 || pitch >= 128) + break; + synth.hdPolyAftertouch(sampleOffset, pitch, event.polyPressure.pressure); + break; + } + } +} + +void SfizzVstProcessor::processMessagesFromUi() +{ + sfz::Sfizz& synth = *_synth; + sfz::Client& client = *_client; + Ring_Buffer& fifo = _fifoMessageFromUi; + RTMessage header; + + while (fifo.peek(header) && fifo.size_used() >= sizeof(header) + header.size) { + fifo.discard(sizeof(header)); + + if (header.type == kRingIdMidi) { + if (header.size > kMidiEventMaximumSize) { + fifo.discard(header.size); + continue; + } + + uint8_t data[kMidiEventMaximumSize] = {}; + fifo.get(data, header.size); + + // interpret the MIDI message + switch (data[0] & 0xf0) { + case 0x80: + synth.noteOff(0, data[1] & 0x7f, data[2] & 0x7f); + break; + case 0x90: + synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f); + break; + case 0xb0: + synth.automateHdcc(0, data[1] & 0x7f, static_cast(data[2] & 0x7f) / 127.0f); + break; + case 0xe0: + synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192); + break; + } + } + else if (header.type == kRingIdOsc) { + uint8_t* oscTemp = _oscTemp.get(); + + if (header.size > kOscTempSize) { + fifo.discard(header.size); + continue; + } + + fifo.get(oscTemp, header.size); + + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + if (sfizz_extract_message(oscTemp, header.size, buffer, sizeof(buffer), &path, &sig, &args) > 0) + synth.sendMessage(client, 0, path, sig, args); + } + else { + assert(false); + return; + } + } +} + +tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) +{ + // Note(jpc) this notification is not handled by the RT thread + + tresult result = AudioEffect::notify(message); + if (result != kResultFalse) + return result; + + const char* id = message->getMessageID(); + Vst::IAttributeList* attr = message->getAttributes(); + + if (!std::strcmp(id, "LoadSfz")) { + const void* data = nullptr; + uint32 size = 0; + result = attr->getBinary("File", data, size); + + if (result != kResultTrue) + return result; + + std::unique_lock lock(_processMutex); + _state.sfzFile.assign(static_cast(data), size); + loadSfzFileOrDefault(_state.sfzFile, false); + lock.unlock(); + } + else if (!std::strcmp(id, "LoadScala")) { + const void* data = nullptr; + uint32 size = 0; + result = attr->getBinary("File", data, size); + + if (result != kResultTrue) + return result; + + std::unique_lock lock(_processMutex); + _state.scalaFile.assign(static_cast(data), size); + _synth->loadScalaFile(_state.scalaFile); + lock.unlock(); + + _scalaUpdate->setPath(_state.scalaFile); + _scalaUpdate->deferUpdate(); + } + else if (!std::strcmp(id, "MidiMessage")) { + const void* data = nullptr; + uint32 size = 0; + result = attr->getBinary("Data", data, size); + if (size < kMidiEventMaximumSize) + writeMessage(_fifoMessageFromUi, kRingIdMidi, data, size); + } + else if (!std::strcmp(id, "OscMessage")) { + const void* data = nullptr; + uint32 size = 0; + result = attr->getBinary("Data", data, size); + writeMessage(_fifoMessageFromUi, kRingIdOsc, data, size); + } + + return result; +} + +void PLUGIN_API SfizzVstProcessor::update(FUnknown* changedUnknown, int32 message) +{ + if (processUpdate(changedUnknown, message)) + return; + + AudioEffect::update(changedUnknown, message); +} + +bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) +{ + if (QueuedUpdates* update = FCast(changedUnknown)) { + for (FObject* queuedUpdate : update->getUpdates(this)) + processUpdate(queuedUpdate, message); + return true; + } + + if (OSCUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (PlayStateUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (NoteUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (SfzUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (ScalaUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + if (AutomationUpdate* update = FCast(changedUnknown)) { + if (IPtr msg = update->convertToMessage(this)) + sendMessage(msg); + return true; + } + + return false; +} + +void SfizzVstProcessor::receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args) +{ + if (!strcmp(path, "/sw/last/current") && sig) + { + if (sig[0] == 'i') + _state.lastKeyswitch = args[0].i; + else if (sig[0] == 'N') + _state.lastKeyswitch = -1; + } + + uint8_t* oscTemp = _oscTemp.get(); + uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args); + if (oscSize <= kOscTempSize) { + if (writeWorkerMessage(kMsgIdReceiveOSC, oscTemp, oscSize)) + _semaToWorker.post(); + } +} + +void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool initParametersFromState) +{ + sfz::Sfizz& synth = *_synth; + + if (!filePath.empty()) { + sfizz_load_or_import_file(synth.handle(), filePath.c_str(), nullptr); + } + else { + synth.loadSfzString("default.sfz", defaultSfzText); + } + + const std::string descBlob = getDescriptionBlob(synth.handle()); + { + std::vector> newControllers(sfz::config::numCCs); + const std::vector> oldControllers = std::move(_state.controllers); + // collect initial CC from instrument + const InstrumentDescription desc = parseDescriptionBlob(descBlob); + for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { + if (desc.ccUsed.test(cc)) + newControllers[cc] = desc.ccValue[cc]; + } + // set CC from existing state + if (initParametersFromState) { + for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { + if (absl::optional value = oldControllers[cc]) { + newControllers[cc] = *value; + synth.automateHdcc(0, int(cc), *value); + } + } + } + _state.controllers = std::move(newControllers); + } + + // create a message which requests the controller to automate initial parameters + std::vector automationItems; + automationItems.reserve(sfz::config::numCCs); + for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { + Vst::ParamID pid = kPidCC0 + cc; + float value = _state.controllers[cc].value_or(0.0f); + automationItems.emplace_back(pid, value); + } + + // send message + _sfzUpdate->setPath(filePath); + _sfzUpdate->deferUpdate(); + _sfzDescriptionUpdate->setDescription(descBlob); + _sfzDescriptionUpdate->deferUpdate(); + _automationUpdate->setItems(std::move(automationItems)); + _automationUpdate->deferUpdate(); +} + +void SfizzVstProcessor::doBackgroundWork() +{ + using Clock = std::chrono::steady_clock; + + bool haveDoneIdleWork = false; + Clock::time_point lastIdleWorkTime; + size_t idleCounter = 0; + + for (;;) { + bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count()); + + if (!_workRunning) { + // if the quit signal is sent, the semaphore is also signaled + // make sure the count is kept consistent + if (!isNotified) + _semaToWorker.wait(); + break; + } + + const char* id = nullptr; + RTMessagePtr msg; + + if (isNotified) { + msg = readWorkerMessage(); + if (!msg) { + fprintf(stderr, "[Sfizz] message synchronization error in worker\n"); + std::abort(); + } + id = msg->type; + } + + if (id == kMsgIdSetNumVoices) { + int32 value = *msg->payload(); + std::lock_guard lock(_processMutex); + _synth->setNumVoices(value); + } + else if (id == kMsgIdSetOversampling) { + int32 value = *msg->payload(); + std::lock_guard lock(_processMutex); + _synth->setOversamplingFactor(1 << value); + } + else if (id == kMsgIdSetPreloadSize) { + int32 value = *msg->payload(); + std::lock_guard lock(_processMutex); + _synth->setPreloadSize(value); + } + else if (id == kMsgIdReceiveOSC) { + IPtr update = Steinberg::owned( + new OSCUpdate(msg->payload(), msg->size)); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); + } + else if (id == kMsgIdNoteEvents) { + IPtr update = Steinberg::owned( + new NoteUpdate(msg->payload(), msg->size / sizeof(NoteUpdate::Item))); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); + } + + Clock::time_point currentTime = Clock::now(); + if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) { + doBackgroundIdle(idleCounter++); + haveDoneIdleWork = true; + lastIdleWorkTime = currentTime; + } + } +} + +void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter) +{ + { + SfizzPlayState ps; + ps.activeVoices = _synth->getNumActiveVoices(); + _playStateUpdate->setState(ps); + _playStateUpdate->deferUpdate(); + } + + if (idleCounter % 25 == 0) { + if (_synth->shouldReloadFile()) { + fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + loadSfzFileOrDefault(_state.sfzFile, false); + } + if (_synth->shouldReloadScala()) { + fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + _synth->loadScalaFile(_state.scalaFile); + } + } +} + +void SfizzVstProcessor::startBackgroundWork() +{ + if (_workRunning) + return; + + _workRunning = true; + _worker = std::thread([this]() { doBackgroundWork(); }); +} + +void SfizzVstProcessor::stopBackgroundWork() +{ + if (!_workRunning) + return; + + _workRunning = false; + _semaToWorker.post(); + _worker.join(); + + while (_semaToWorker.try_wait()) { + if (!discardWorkerMessage()) { + fprintf(stderr, "[Sfizz] message synchronization error in processor\n"); + std::abort(); + } + } +} + +bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size) +{ + return writeMessage(_fifoToWorker, type, data, size); +} + +SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage() +{ + RTMessage header; + + if (!_fifoToWorker.peek(header)) + return nullptr; + if (_fifoToWorker.size_used() < sizeof(header) + header.size) + return nullptr; + + RTMessagePtr msg { reinterpret_cast(std::malloc(sizeof(header) + header.size)) }; + if (!msg) + throw std::bad_alloc(); + + msg->type = header.type; + msg->size = header.size; + _fifoToWorker.discard(sizeof(header)); + _fifoToWorker.get(const_cast(msg->payload()), header.size); + + return msg; +} + +bool SfizzVstProcessor::discardWorkerMessage() +{ + RTMessage header; + + if (!_fifoToWorker.peek(header)) + return false; + if (_fifoToWorker.size_used() < sizeof(header) + header.size) + return false; + + _fifoToWorker.discard(sizeof(header) + header.size); + return true; +} + +bool SfizzVstProcessor::writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size) +{ + RTMessage header; + header.type = type; + header.size = size; + + if (fifo.size_free() < sizeof(header) + size) + return false; + + fifo.put(header); + fifo.put(static_cast(data), size); + return true; +} + +FUnknown* SfizzVstProcessor::createInstance(void*) +{ + return static_cast(new SfizzVstProcessor); +} + +FUnknown* SfizzVstProcessorMulti::createInstance(void*) +{ + return static_cast(new SfizzVstProcessorMulti); +} + +template <> +FUnknown* createInstance(void* context) +{ + return SfizzVstProcessor::createInstance(context); +} + +tresult PLUGIN_API SfizzVstProcessorMulti::initialize(FUnknown* context) +{ + tresult res = SfizzVstProcessor::initialize(context); + if (res != kResultFalse) { + addAudioOutput(STR16("Audio Output 2"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 3"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 4"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 5"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 6"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 7"), Vst::SpeakerArr::kStereo); + addAudioOutput(STR16("Audio Output 8"), Vst::SpeakerArr::kStereo); + } + _multi = true; + _rmsFollower.setNumOutputs(16); + return res; +} + +template <> +FUnknown* createInstance(void* context) +{ + return SfizzVstProcessorMulti::createInstance(context); +} + +FUID SfizzVstProcessor::cid = SfizzVstProcessor_cid; +FUID SfizzVstProcessorMulti::cid = SfizzVstProcessor_cid; diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h new file mode 100644 index 0000000..0f98303 --- /dev/null +++ b/plugins/vst/SfizzVstProcessor.h @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstState.h" +#include "SfizzVstUpdates.h" +#include "OrderedEventProcessor.h" +#include "plugin/RMSFollower.h" +#include "sfizz/RTSemaphore.h" +#include "ring_buffer/ring_buffer.h" +#include "public.sdk/source/vst/vstaudioeffect.h" +#include +#include +#include +#include +#include +#include + +using namespace Steinberg; + +class SfizzVstProcessor : public Vst::AudioEffect, + public OrderedEventProcessor { +public: + SfizzVstProcessor(); + ~SfizzVstProcessor(); + + tresult PLUGIN_API initialize(FUnknown* context) override; + tresult PLUGIN_API terminate() override; + tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override; + + tresult PLUGIN_API connect(IConnectionPoint* other) override; + + tresult PLUGIN_API setState(IBStream* stream) override; + tresult PLUGIN_API getState(IBStream* stream) override; + void syncStateToSynth(); + + tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override; + tresult PLUGIN_API setActive(TBool state) override; + tresult PLUGIN_API process(Vst::ProcessData& data) override; + + // OrderedEventProcessor + void playOrderedParameter(int32 sampleOffset, Vst::ParamID id, Vst::ParamValue value); + void playOrderedEvent(const Vst::Event& event); + + void processMessagesFromUi(); + + tresult PLUGIN_API notify(Vst::IMessage* message) override; + void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; + + static FUnknown* createInstance(void*); + + static FUID cid; + + // --- Sfizz stuff here below --- +protected: + RMSFollower _rmsFollower {}; + bool _multi { false }; +private: + // synth state. acquire processMutex before accessing + std::unique_ptr _synth; + bool _isActive = false; + SfizzVstState _state; + float _currentStretchedTuning = 0; + + // whether allowed to perform events (owns the processing lock) + bool _canPerformEventsAndParameters {}; + + // level meters + bool _editorIsOpen = false; + + // updates + IPtr _queuedMessages; + IPtr _playStateUpdate; + IPtr _sfzUpdate; + IPtr _sfzDescriptionUpdate; + IPtr _scalaUpdate; + IPtr _automationUpdate; + bool processUpdate(FUnknown* changedUnknown, int32 message); + + // client + sfz::ClientPtr _client; + std::unique_ptr _oscTemp; + void receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args); + + // misc + void loadSfzFileOrDefault(const std::string& filePath, bool initParametersFromState); + + // note event tracking + std::array _noteEventsCurrentCycle; // 0: off, >0: on, <0: no change + + // worker and thread sync + std::thread _worker; + volatile bool _workRunning = false; + Ring_Buffer _fifoToWorker; + RTSemaphore _semaToWorker; + Ring_Buffer _fifoMessageFromUi; + SpinMutex _processMutex; + + // time info + int _timeSigNumerator = 0; + int _timeSigDenominator = 0; + void updateTimeInfo(const Vst::ProcessContext& context); + + // messaging + struct RTMessage { + const char* type; + uintptr_t size; + // 32-bit aligned data after header + template const T* payload() const; + }; + struct RTMessageDelete { + void operator()(RTMessage* x) const noexcept { std::free(x); } + }; + typedef std::unique_ptr RTMessagePtr; + + // worker + void doBackgroundWork(); + void doBackgroundIdle(size_t idleCounter); + void startBackgroundWork(); + void stopBackgroundWork(); + // writer + bool writeWorkerMessage(const char* type, const void* data, uintptr_t size); + // reader + RTMessagePtr readWorkerMessage(); + bool discardWorkerMessage(); + + // generic + static bool writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size); +}; + +class SfizzVstProcessorMulti : public SfizzVstProcessor { +public: + tresult PLUGIN_API initialize(FUnknown* context) override; + static FUnknown* createInstance(void*); + static FUID cid; +}; +//------------------------------------------------------------------------------ + +template const T* SfizzVstProcessor::RTMessage::payload() const +{ + return reinterpret_cast( + reinterpret_cast(this) + sizeof(*this)); +} diff --git a/plugins/vst/SfizzVstState.cpp b/plugins/vst/SfizzVstState.cpp new file mode 100644 index 0000000..0919959 --- /dev/null +++ b/plugins/vst/SfizzVstState.cpp @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstState.h" +#include +#include +#include + +tresult SfizzVstState::load(IBStream* state) +{ + IBStreamer s(state, kLittleEndian); + + uint64 version = 0; + if (!s.readInt64u(version)) + return kResultFalse; + + if (version > currentStateVersion) + return kResultFalse; + + if (const char* str = s.readStr8()) + sfzFile = str; + else + return kResultFalse; + + if (!s.readFloat(volume)) + return kResultFalse; + + if (!s.readInt32(numVoices)) + return kResultFalse; + + if (!s.readInt32(oversamplingLog2)) + return kResultFalse; + + if (!s.readInt32(preloadSize)) + return kResultFalse; + + const SfizzVstState defaults; + + if (version >= 1) { + if (const char* str = s.readStr8()) + scalaFile = str; + else + return kResultFalse; + + if (!s.readInt32(scalaRootKey)) + return kResultFalse; + + if (!s.readFloat(tuningFrequency)) + return kResultFalse; + + if (!s.readFloat(stretchedTuning)) + return kResultFalse; + } + else { + scalaFile = defaults.scalaFile; + scalaRootKey = defaults.scalaRootKey; + tuningFrequency = defaults.tuningFrequency; + stretchedTuning = defaults.stretchedTuning; + } + + if (version >= 3) { + if (!s.readInt32(sampleQuality)) + return kResultFalse; + + if (!s.readInt32(oscillatorQuality)) + return kResultFalse; + } + else { + sampleQuality = defaults.sampleQuality; + oscillatorQuality = defaults.oscillatorQuality; + } + + if (version >= 5) { + if (!s.readInt32(freewheelingSampleQuality)) + return kResultFalse; + + if (!s.readInt32(freewheelingOscillatorQuality)) + return kResultFalse; + + if (!s.readBool(sustainCancelsRelease)) + return kResultFalse; + } + + if (version >= 4) { + if (!s.readInt32(lastKeyswitch)) + return kResultFalse; + } + else { + lastKeyswitch = -1; + } + + controllers.clear(); + if (version >= 2) { + uint32 count; + if (!s.readInt32u(count)) + return kResultFalse; + controllers.resize(0x10000); + uint32 size = 0; + for (uint32 i = 0; i < count; ++i) { + uint16 cc; + float value; + if (!s.readInt16u(cc) || !s.readFloat(value)) + return kResultFalse; + controllers[cc] = value; + size = std::max(size, uint32(cc) + 1); + } + controllers.resize(size); + controllers.shrink_to_fit(); + } + + return kResultTrue; +} + +tresult SfizzVstState::store(IBStream* state) const +{ + IBStreamer s(state, kLittleEndian); + + if (!s.writeInt64u(currentStateVersion)) + return kResultFalse; + + if (!s.writeStr8(sfzFile.c_str())) + return kResultFalse; + + if (!s.writeFloat(volume)) + return kResultFalse; + + if (!s.writeInt32(numVoices)) + return kResultFalse; + + if (!s.writeInt32(oversamplingLog2)) + return kResultFalse; + + if (!s.writeInt32(preloadSize)) + return kResultFalse; + + if (!s.writeStr8(scalaFile.c_str())) + return kResultFalse; + + if (!s.writeInt32(scalaRootKey)) + return kResultFalse; + + if (!s.writeFloat(tuningFrequency)) + return kResultFalse; + + if (!s.writeFloat(stretchedTuning)) + return kResultFalse; + + if (!s.writeInt32(sampleQuality)) + return kResultFalse; + + if (!s.writeInt32(oscillatorQuality)) + return kResultFalse; + + if (!s.writeInt32(freewheelingSampleQuality)) + return kResultFalse; + + if (!s.writeInt32(freewheelingOscillatorQuality)) + return kResultFalse; + + if (!s.writeBool(sustainCancelsRelease)) + return kResultFalse; + + if (!s.writeInt32(lastKeyswitch)) + return kResultFalse; + + { + uint32 ccCount = 0; + uint32 ccLimit = uint32(std::min(controllers.size(), size_t(0x10000))); + for (uint32_t cc = 0; cc < ccLimit; ++cc) + ccCount += controllers[cc] != absl::nullopt; + if (!s.writeInt32u(ccCount)) + return kResultFalse; + for (uint32_t cc = 0; cc < ccLimit; ++cc) { + if (absl::optional ccValue = controllers[cc]) { + if (!s.writeInt16u(uint16(cc)) || !s.writeFloat(*ccValue)) + return kResultFalse; + } + } + } + + return kResultTrue; +} + +constexpr uint64 SfizzVstState::currentStateVersion; diff --git a/plugins/vst/SfizzVstState.h b/plugins/vst/SfizzVstState.h new file mode 100644 index 0000000..3b43ccf --- /dev/null +++ b/plugins/vst/SfizzVstState.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "base/source/fstreamer.h" +#include +#include +#include + +using namespace Steinberg; + +class SfizzVstState { +public: + SfizzVstState() { sfzFile.reserve(8192); scalaFile.reserve(8192); } + + std::string sfzFile; + float volume = 0; + int32 numVoices = 64; + int32 oversamplingLog2 = 0; + int32 preloadSize = 8192; + std::string scalaFile; + int32 scalaRootKey = 60; + float tuningFrequency = 440.0; + float stretchedTuning = 0.0; + int32 sampleQuality = 2; + int32 oscillatorQuality = 1; + int32 freewheelingSampleQuality = 10; + int32 freewheelingOscillatorQuality = 3; + bool sustainCancelsRelease = false; + int32 lastKeyswitch = -1; + std::vector> controllers; + + static constexpr uint64 currentStateVersion = 5; + + tresult load(IBStream* state); + tresult store(IBStream* state) const; +}; + +struct SfizzPlayState { + uint32 activeVoices; +}; diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp new file mode 100644 index 0000000..5dfcfd3 --- /dev/null +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstUpdates.h" +#include +#include + +void QueuedUpdates::enqueue(IPtr update) +{ + std::lock_guard lock(mutex_); + for (std::pair& item : updates_) + item.second.push_back(update); +} + +auto QueuedUpdates::getUpdates(IDependent* dep) -> List +{ + std::lock_guard lock(mutex_); + List list; + auto it = updates_.find(dep); + if (it != updates_.end()) + std::swap(list, it->second); + return list; +} + +void QueuedUpdates::addDependent(IDependent* dep) +{ + std::lock_guard lock(mutex_); + FObject::addDependent(dep); + updates_.emplace(dep, List()); +} + +void QueuedUpdates::removeDependent(IDependent* dep) +{ + std::lock_guard lock(mutex_); + FObject::removeDependent(dep); + updates_.erase(dep); +} + +/// +bool OSCUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return attrs->setBinary("Data", data(), size()) == kResultTrue; +} + +bool OSCUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* data; + uint32 size; + if (attrs->getBinary("Data", data, size) != kResultTrue) + return false; + const uint8* data8 = reinterpret_cast(data); + data_.assign(data8, data8 + size); + return true; +} + +/// +bool NoteUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return attrs->setBinary("Events", events_.data(), events_.size() * sizeof(Item)) == kResultTrue; +} + +bool NoteUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Events", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + events_.assign(events, events + numEvents); + return true; +} + +/// +bool FilePathUpdate::saveFilePathAttributes_(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Path", path_.data(), path_.size()) == kResultTrue; +} + +bool FilePathUpdate::loadFilePathAttributes_(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Path", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + path_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool SfzUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool SfzUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool ScalaUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool ScalaUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool SfzDescriptionUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Blob", description_.data(), description_.size()) == kResultTrue; +} + +bool SfzDescriptionUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Blob", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + description_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool PlayStateUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setInt("ActiveVoices", state_.activeVoices) == kResultTrue; +} + +bool PlayStateUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + int64 activeVoices; + if (attrs->getInt("ActiveVoices", activeVoices) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + state_.activeVoices = static_cast(activeVoices); + return true; +} + +/// +bool AutomationUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Items", items_.data(), items_.size() * sizeof(Item)) == kResultTrue; +} + +bool AutomationUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Items", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + std::lock_guard lock(mutex_); + items_.assign(events, events + numEvents); + return true; +} diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h new file mode 100644 index 0000000..76357d5 --- /dev/null +++ b/plugins/vst/SfizzVstUpdates.h @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstState.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Update which is convertible with Vst::IMessage back and forth. + */ +template class IConvertibleToMessage { +public: + virtual ~IConvertibleToMessage() {} + + IPtr convertToMessage(Vst::ComponentBase* sender) const; + bool convertFromMessage(Vst::IMessage& message); + static IPtr createFromMessage(Vst::IMessage& message); + +protected: + virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0; + virtual bool loadFromAttributes(Vst::IAttributeList* attrs) = 0; +}; + +/** + * @brief Update which notifies a FIFO queue of one-time updates + */ +class QueuedUpdates : public Steinberg::FObject { +public: + using List = std::vector>; + + void enqueue(IPtr update); + List getUpdates(IDependent* dep); + + void addDependent(IDependent* dep) override; + void removeDependent(IDependent* dep) override; + + OBJ_METHODS(QueuedUpdates, FObject) + +private: + std::mutex mutex_; + std::map updates_; +}; + +/** + * @brief Update which notifies a single OSC message + */ +class OSCUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + OSCUpdate() = default; + OSCUpdate(const uint8* data, uint32 size) : data_(data, data + size) {} + const uint8* data() const noexcept { return data_.data(); } + uint32_t size() const noexcept { return static_cast(data_.size()); } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(OSCUpdate, FObject) + +private: + std::vector data_; +}; + +/** + * @brief Update which notifies one or more note on/off events + */ +class NoteUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + using Item = std::pair; + + NoteUpdate() = default; + NoteUpdate(const Item* items, uint32 count) : events_(items, items + count) {} + const Item* events() const noexcept { return events_.data(); } + const uint32 count() const noexcept { return static_cast(events_.size()); } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(NoteUpdate, FObject) + +private: + std::vector events_; +}; + +/** + * @brief Abstract update which notifies change of a certain file path. + */ +class FilePathUpdate : public Steinberg::FObject { +protected: + FilePathUpdate() = default; + +public: + void setPath(std::string newPath) + { + std::lock_guard lock(mutex_); + path_ = std::move(newPath); + } + + std::string getPath() const + { + std::lock_guard lock(mutex_); + return path_; + } + + OBJ_METHODS(FilePathUpdate, FObject) + +protected: + bool saveFilePathAttributes_(Vst::IAttributeList* attrs) const; + bool loadFilePathAttributes_(Vst::IAttributeList* attrs); + +private: + std::string path_; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which notifies a change of SFZ file. + */ +class SfzUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(SfzUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + +/** + * @brief Update which notifies a change of scala file. + */ +class ScalaUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(ScalaUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + +/** + * @brief Update which notifies a change of SFZ description. + */ +class SfzDescriptionUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + void setDescription(std::string newDescription) + { + std::lock_guard lock(mutex_); + description_ = std::move(newDescription); + } + + std::string getDescription() const + { + std::lock_guard lock(mutex_); + return description_; + } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(SfzDescriptionUpdate, FObject) + +private: + std::string description_; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which indicates the playing SFZ status. + */ +class PlayStateUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + void setState(SfizzPlayState newState) + { + std::lock_guard lock(mutex_); + state_ = std::move(newState); + } + + SfizzPlayState getState() const + { + std::lock_guard lock(mutex_); + return state_; + } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + virtual bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(PlayStateUpdate, FObject) + +private: + SfizzPlayState state_ {}; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which automates a pack of parameters + */ +class AutomationUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + using Item = std::pair; + + AutomationUpdate() = default; + + void setItems(std::vector newItems) + { + std::lock_guard lock(mutex_); + items_ = std::move(newItems); + } + + std::vector getItems() const + { + std::lock_guard lock(mutex_); + return items_; + } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(AutomationUpdate, FObject) + +private: + std::vector items_; + mutable std::mutex mutex_; +}; + +#include "SfizzVstUpdates.hpp" diff --git a/plugins/vst/SfizzVstUpdates.hpp b/plugins/vst/SfizzVstUpdates.hpp new file mode 100644 index 0000000..5b1ce1f --- /dev/null +++ b/plugins/vst/SfizzVstUpdates.hpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstUpdates.h" + +template +IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBase* sender) const +{ + IPtr message = Steinberg::owned(sender->allocateMessage()); + if (!message) + return nullptr; + message->setMessageID(static_cast(this)->isA()); + if (!saveToAttributes(message->getAttributes())) + return nullptr; + return message; +} + +template +IPtr IConvertibleToMessage::createFromMessage(Vst::IMessage& message) +{ + IPtr object; + if (!strcmp(T::getFClassID(), message.getMessageID())) { + object = Steinberg::owned(new T); + if (!object->loadFromAttributes(message.getAttributes())) + object = nullptr; + } + return object; +} + +template +bool IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +{ + bool success = false; + if (!strcmp(T::getFClassID(), message.getMessageID())) + success = loadFromAttributes(message.getAttributes()); + return success; +} diff --git a/plugins/vst/Vst2PluginEntry.c b/plugins/vst/Vst2PluginEntry.c new file mode 100644 index 0000000..030ea2c --- /dev/null +++ b/plugins/vst/Vst2PluginEntry.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/* + Note: this file provides the traditional VST2 entry point `main` on Windows. + some hosts only detect this entry point. + + It's required to compile this file in C mode. + Under MinGW, having a signature for `main` which is different from the + standard one is a warning in C, and an error in C++. +*/ + +#include "pluginterfaces/base/fplatform.h" +#include + +typedef struct AEffect AEffect; +typedef int32_t VstInt32; +typedef intptr_t VstIntPtr; +typedef intptr_t (__cdecl *audioMasterCallback) (AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt); + +#if defined(_WIN32) +SMTG_EXPORT_SYMBOL AEffect* MAIN (audioMasterCallback audioMaster); + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmain" +#endif + +SMTG_EXPORT_SYMBOL AEffect* main (audioMasterCallback audioMaster) +{ + return MAIN(audioMaster); +} + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif diff --git a/plugins/vst/Vst2PluginFactory.cpp b/plugins/vst/Vst2PluginFactory.cpp new file mode 100644 index 0000000..98b6981 --- /dev/null +++ b/plugins/vst/Vst2PluginFactory.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstIDs.h" +#include "public.sdk/source/vst/vst2wrapper/vst2wrapper.h" + +//------------------------------------------------------------------------ +::AudioEffect* createEffectInstance(audioMasterCallback audioMaster) +{ + return Steinberg::Vst::Vst2Wrapper::create( + GetPluginFactory(), + SfizzVstProcessor_cid, + 'Sfzz', + audioMaster); +} diff --git a/plugins/vst/VstPluginDefs.h.in b/plugins/vst/VstPluginDefs.h.in new file mode 100644 index 0000000..c67283d --- /dev/null +++ b/plugins/vst/VstPluginDefs.h.in @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#define VSTPLUGIN_NAME "@VSTPLUGIN_NAME@" +#define VSTPLUGIN_VENDOR "@VSTPLUGIN_VENDOR@" +#define VSTPLUGIN_URL "@VSTPLUGIN_URL@" +#define VSTPLUGIN_EMAIL "@VSTPLUGIN_EMAIL@" +#define VSTPLUGIN_VERSION "@PROJECT_VERSION@" diff --git a/plugins/vst/VstPluginFactory.cpp b/plugins/vst/VstPluginFactory.cpp new file mode 100644 index 0000000..b0bad95 --- /dev/null +++ b/plugins/vst/VstPluginFactory.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstIDs.h" +#include "VstPluginDefs.h" +#include "public.sdk/source/main/pluginfactory.h" +#include "pluginterfaces/vst/ivstcomponent.h" +#include "pluginterfaces/vst/ivstaudioprocessor.h" +#include "pluginterfaces/vst/ivsteditcontroller.h" + +class SfizzVstProcessor; +class SfizzVstProcessorMulti; +class SfizzVstController; + +BEGIN_FACTORY_DEF(VSTPLUGIN_VENDOR, + VSTPLUGIN_URL, + "mailto:" VSTPLUGIN_EMAIL) + +DEF_CLASS2 (INLINE_UID_FROM_FUID(SfizzVstProcessor_cid), + PClassInfo::kManyInstances, + kVstAudioEffectClass, + VSTPLUGIN_NAME, + Vst::kDistributable, + Vst::PlugType::kInstrumentSynth, + VSTPLUGIN_VERSION, + kVstVersionString, + createInstance) + +DEF_CLASS2 (INLINE_UID_FROM_FUID(SfizzVstProcessorMulti_cid), + PClassInfo::kManyInstances, + kVstAudioEffectClass, + VSTPLUGIN_NAME "-multi", + Vst::kDistributable, + Vst::PlugType::kInstrumentSynth, + VSTPLUGIN_VERSION, + kVstVersionString, + createInstance) + +DEF_CLASS2 (INLINE_UID_FROM_FUID(SfizzVstController_cid), + PClassInfo::kManyInstances, + kVstComponentControllerClass, + VSTPLUGIN_NAME, + 0, // not used here + "", // not used here + VSTPLUGIN_VERSION, + kVstVersionString, + createInstance) + +END_FACTORY diff --git a/plugins/vst/X11RunLoop.cpp b/plugins/vst/X11RunLoop.cpp new file mode 100644 index 0000000..2d8ded2 --- /dev/null +++ b/plugins/vst/X11RunLoop.cpp @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#if !defined(__APPLE__) && !defined(_WIN32) +#include "X11RunLoop.h" +#include "vstgui/lib/platform/linux/x11platform.h" +#include "base/source/fobject.h" +#include +#include +#include +#include + +namespace VSTGUI { + +struct RunLoop::Impl { + struct EventHandler; + struct TimerHandler; + + using EventHandlers = std::vector>; + using TimerHandlers = std::vector>; + + EventHandlers eventHandlers; + TimerHandlers timerHandlers; + Steinberg::FUnknownPtr runLoop; +}; + +//------------------------------------------------------------------------------ +struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject { + X11::IEventHandler* handler { nullptr }; + bool alive { false }; + + void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override; + + DELEGATE_REFCOUNT(Steinberg::FObject) + DEFINE_INTERFACES + DEF_INTERFACE(Steinberg::Linux::IEventHandler) + END_DEFINE_INTERFACES(Steinberg::FObject) +}; + +struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject { + X11::ITimerHandler* handler { nullptr }; + bool alive { false }; + + void PLUGIN_API onTimer() override; + + DELEGATE_REFCOUNT(Steinberg::FObject) + DEFINE_INTERFACES + DEF_INTERFACE(Steinberg::Linux::ITimerHandler) + END_DEFINE_INTERFACES(Steinberg::FObject) +}; + +//------------------------------------------------------------------------------ +void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor) +{ + SharedPointer runLoop = RunLoop::get(); + if (!runLoop) { + fprintf(stderr, "[x11] event has fired without active runloop\n"); + return; + } + + if (alive && handler) + handler->onEvent(); +} + +void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer() +{ + SharedPointer runLoop = RunLoop::get(); + if (!runLoop) { + fprintf(stderr, "[x11] timer has fired without active runloop\n"); + return; + } + + if (alive && handler) + handler->onTimer(); +} + +//------------------------------------------------------------------------------ +RunLoop::RunLoop(Steinberg::FUnknown* runLoop) + : impl(new Impl) +{ + impl->runLoop = runLoop; +} + +RunLoop::~RunLoop() +{ + //dumpCurrentState(); + + if (0) { + // remove any leftover handlers + for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { + const auto& eh = impl->eventHandlers[i]; + if (eh->alive && eh->handler) { + impl->runLoop->unregisterEventHandler(eh.get()); + } + } + for (size_t i = 0; i < impl->timerHandlers.size(); ++i) { + const auto& th = impl->timerHandlers[i]; + if (th->alive && th->handler) { + impl->runLoop->unregisterTimer(th.get()); + } + } + } +} + +SharedPointer RunLoop::get() +{ + return X11::RunLoop::get().cast(); +} + +void RunLoop::processSomeEvents() +{ + for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { + const auto& eh = impl->eventHandlers[i]; + if (eh->alive && eh->handler) { + eh->handler->onEvent(); + } + } +} + +void RunLoop::dumpCurrentState() +{ + fprintf(stderr, "=== X11 runloop ===\n"); + + fprintf(stderr, "\t" "Event slots:\n"); + for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) { + Impl::EventHandler *eh = impl->eventHandlers[i].get(); + fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : ""); + } + + fprintf(stderr, "\t" "Timer slots:\n"); + for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) { + Impl::TimerHandler *th = impl->timerHandlers[i].get(); + fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : ""); + } + + fprintf(stderr, "===/X11 runloop ===\n"); +} + +template +static void insertHandler(std::vector>& list, Steinberg::IPtr handler) +{ + size_t i = 0; + size_t n = list.size(); + while (i < n && list[i]->alive) + ++i; + if (i < n) + list[i] = handler; + else + list.emplace_back(handler); +} + +template +static size_t findHandler(const std::vector>& list, U* handler) +{ + for (size_t i = 0, n = list.size(); i < n; ++i) { + if (list[i]->alive && list[i]->handler == handler) + return i; + } + return ~size_t(0); +} + +bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler) +{ + if (!impl->runLoop) + return false; + + auto smtgHandler = Steinberg::owned(new Impl::EventHandler); + smtgHandler->handler = handler; + smtgHandler->alive = true; + if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) { + insertHandler(impl->eventHandlers, smtgHandler); + return true; + } + return false; +} + +bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler) +{ + if (!impl->runLoop) + return false; + + size_t index = findHandler(impl->eventHandlers, handler); + if (index == ~size_t(0)) + return false; + + Impl::EventHandler *eh = impl->eventHandlers[index].get(); + if (impl->runLoop->unregisterEventHandler(eh) != Steinberg::kResultTrue) + return false; + + eh->alive = false; + return true; +} + +bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler) +{ + if (!impl->runLoop) + return false; + + auto smtgHandler = Steinberg::owned(new Impl::TimerHandler); + smtgHandler->handler = handler; + smtgHandler->alive = true; + if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) { + insertHandler(impl->timerHandlers, smtgHandler); + return true; + } + return false; +} + +bool RunLoop::unregisterTimer(X11::ITimerHandler* handler) +{ + if (!impl->runLoop) + return false; + + size_t index = findHandler(impl->timerHandlers, handler); + if (index == ~size_t(0)) + return false; + + Impl::TimerHandler *th = impl->timerHandlers[index].get(); + if (impl->runLoop->unregisterTimer(th) != Steinberg::kResultTrue) + return false; + + th->alive = false; + return true; +} + +} // namespace VSTGUI + +#endif diff --git a/plugins/vst/X11RunLoop.h b/plugins/vst/X11RunLoop.h new file mode 100644 index 0000000..5755a06 --- /dev/null +++ b/plugins/vst/X11RunLoop.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/* + This runloop connects to X11 VSTGUI, it connects VST3 and VSTGUI together. + The Windows and macOS runloops do not need this, the OS-provided + functionality is used instead. + + Previously, this was based on VSTGUI code provided by Steinberg. + This is replaced with a rewrite, because the original code has too many + issues. For example, it has no robustness in case handlers get added or + removed within the execution of the handler. + + This version allows to call event processing externally, in case the host + has a defective X11 event loop notifier. (some versions of Bitwig do) +*/ + +#pragma once +#if !defined(__APPLE__) && !defined(_WIN32) +#include "vstgui/lib/platform/linux/x11frame.h" +#include "pluginterfaces/gui/iplugview.h" +#include + +namespace VSTGUI { + +class RunLoop final : public X11::IRunLoop, public AtomicReferenceCounted { +public: + explicit RunLoop(Steinberg::FUnknown* runLoop); + ~RunLoop(); + + static SharedPointer get(); + + void processSomeEvents(); + void dumpCurrentState(); + + // X11::IRunLoop + bool registerEventHandler(int fd, X11::IEventHandler* handler) override; + bool unregisterEventHandler(X11::IEventHandler* handler) override; + bool registerTimer(uint64_t interval, X11::ITimerHandler* handler) override; + bool unregisterTimer(X11::ITimerHandler* handler) override; + + struct Impl; + std::unique_ptr impl; +}; + +} // namespace VSTGUI + +#endif diff --git a/plugins/vst/cmake/Vst3.cmake b/plugins/vst/cmake/Vst3.cmake new file mode 100644 index 0000000..f461453 --- /dev/null +++ b/plugins/vst/cmake/Vst3.cmake @@ -0,0 +1,129 @@ +find_package(Threads REQUIRED) + +# --- VST3SDK --- +add_library(vst3sdk STATIC EXCLUDE_FROM_ALL + "${VST3SDK_BASEDIR}/base/source/baseiids.cpp" + "${VST3SDK_BASEDIR}/base/source/fbuffer.cpp" + "${VST3SDK_BASEDIR}/base/source/fdebug.cpp" + "${VST3SDK_BASEDIR}/base/source/fdynlib.cpp" + "${VST3SDK_BASEDIR}/base/source/fobject.cpp" + "${VST3SDK_BASEDIR}/base/source/fstreamer.cpp" + "${VST3SDK_BASEDIR}/base/source/fstring.cpp" + "${VST3SDK_BASEDIR}/base/source/timer.cpp" + "${VST3SDK_BASEDIR}/base/source/updatehandler.cpp" + "${VST3SDK_BASEDIR}/base/thread/source/fcondition.cpp" + "${VST3SDK_BASEDIR}/base/thread/source/flock.cpp" + "${VST3SDK_BASEDIR}/pluginterfaces/base/conststringtable.cpp" + "${VST3SDK_BASEDIR}/pluginterfaces/base/coreiids.cpp" + "${VST3SDK_BASEDIR}/pluginterfaces/base/funknown.cpp" + "${VST3SDK_BASEDIR}/pluginterfaces/base/ustring.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/common/commoniids.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/common/memorystream.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/common/pluginview.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/main/pluginfactory.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstaudioeffect.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstbus.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstcomponent.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstcomponentbase.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vsteditcontroller.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstinitiids.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstnoteexpressiontypes.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstparameters.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstpresetfile.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/utility/stringconvert.cpp") +if(WIN32) + target_sources(vst3sdk PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_win32.cpp") +elseif(APPLE) + target_sources(vst3sdk PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_mac.mm") +else() + target_sources(vst3sdk PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_linux.cpp") +endif() +target_include_directories(vst3sdk PUBLIC "${VST3SDK_BASEDIR}") +target_link_libraries(vst3sdk PUBLIC Threads::Threads) +if(APPLE) + target_link_libraries(vst3sdk PUBLIC ${APPLE_FOUNDATION_LIBRARY}) +endif() +if(MINGW) + target_compile_definitions(vst3sdk PUBLIC + "_NATIVE_WCHAR_T_DEFINED=1" "__wchar_t=wchar_t") +endif() +set(_vst_release_build_types MinSizeRel Release RelWithDebInfo) +if(CMAKE_BUILD_TYPE IN_LIST _vst_release_build_types) + target_compile_definitions(vst3sdk PUBLIC "RELEASE") +else() + target_compile_definitions(vst3sdk PUBLIC "DEVELOPMENT") +endif() + +function(plugin_add_vst3sdk NAME) + target_link_libraries("${NAME}" PRIVATE vst3sdk) + target_sources("${NAME}" PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/main/moduleinit.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/main/pluginfactory.cpp") + if(WIN32) + target_sources("${NAME}" PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/main/dllmain.cpp") + elseif(APPLE) + target_sources("${NAME}" PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/main/macmain.cpp") + else() + target_sources("${NAME}" PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/main/linuxmain.cpp") + endif() +endfunction() + +# --- VST3SDK hosting --- +add_library(vst3sdk_hosting STATIC EXCLUDE_FROM_ALL + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/connectionproxy.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/eventlist.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/hostclasses.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/parameterchanges.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/pluginterfacesupport.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/plugprovider.cpp" + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/processdata.cpp") +if(FALSE) + if(WIN32) + target_sources(vst3sdk_hosting PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/module_win32.cpp") + elseif(APPLE) + target_sources(vst3sdk_hosting PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/module_mac.mm") + else() + target_sources(vst3sdk_hosting PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/vst/hosting/module_linux.cpp") + endif() +endif() +target_link_libraries(vst3sdk_hosting PUBLIC vst3sdk) + +# --- VSTGUI --- +add_library(vst3sdk_vstgui STATIC EXCLUDE_FROM_ALL + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstguieditor.cpp") +if(WIN32) + target_sources(vst3sdk_vstgui PRIVATE + "${VST3SDK_BASEDIR}/public.sdk/source/vst/vstgui_win32_bundle_support.cpp") + target_compile_definitions(vst3sdk_vstgui PRIVATE "SMTG_MODULE_IS_BUNDLE=1") +endif() +target_link_libraries(vst3sdk_vstgui PUBLIC vst3sdk sfizz::vstgui) + +function(plugin_add_vstgui NAME) + target_link_libraries("${NAME}" PRIVATE vst3sdk_vstgui) +endfunction() + +# --- Warning suppressions --- +foreach(_target vst3sdk_vstgui vst3sdk) + gw_target_warn("${_target}" PUBLIC + "-Wno-extra" + "-Wno-class-memaccess") + gw_target_warn("${_target}" PRIVATE + "-Wno-multichar" + "-Wno-reorder" + "-Wno-class-memaccess" + "-Wno-ignored-qualifiers" + "-Wno-unknown-pragmas" + "-Wno-unused-function" + "-Wno-unused-parameter" + "-Wno-unused-variable") +endforeach() diff --git a/plugins/vst/external/VST_SDK/VST3_SDK/base b/plugins/vst/external/VST_SDK/VST3_SDK/base new file mode 160000 index 0000000..ea2bac9 --- /dev/null +++ b/plugins/vst/external/VST_SDK/VST3_SDK/base @@ -0,0 +1 @@ +Subproject commit ea2bac9a109cce69ced21833fa6ff873dd6e368a diff --git a/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces b/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces new file mode 160000 index 0000000..bc5ff0f --- /dev/null +++ b/plugins/vst/external/VST_SDK/VST3_SDK/pluginterfaces @@ -0,0 +1 @@ +Subproject commit bc5ff0f87aaa3cd28c114810f4f03c384421ad2c diff --git a/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk b/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk new file mode 160000 index 0000000..bbb0538 --- /dev/null +++ b/plugins/vst/external/VST_SDK/VST3_SDK/public.sdk @@ -0,0 +1 @@ +Subproject commit bbb0538535b171e805c8a8b612c2cd8a5f95738b diff --git a/plugins/vst/external/ring_buffer/LICENSE b/plugins/vst/external/ring_buffer/LICENSE new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/plugins/vst/external/ring_buffer/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp new file mode 100644 index 0000000..bf0fcb9 --- /dev/null +++ b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp @@ -0,0 +1,105 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "ring_buffer.h" +#include +#include + +#if defined(__cpp_if_constexpr) +# define if_constexpr if constexpr +#else +# define if_constexpr if +#endif + +template +Ring_Buffer_Ex::Ring_Buffer_Ex(size_t capacity) + : cap_(capacity + 1), + rbdata_(new uint8_t[capacity + 1]) +{ +} + +template +Ring_Buffer_Ex::~Ring_Buffer_Ex() +{ +} + +template +size_t Ring_Buffer_Ex::size_used() const +{ + const size_t rp = rp_, wp = wp_, cap = cap_; + return wp + ((wp < rp) ? cap : 0) - rp; +} + +template +bool Ring_Buffer_Ex::discard(size_t len) +{ + return getbytes_ex_(nullptr, len, true); +} + +template +size_t Ring_Buffer_Ex::size_free() const +{ + const size_t rp = rp_, wp = wp_, cap = cap_; + return rp + ((rp <= wp) ? cap : 0) - wp - 1; +} + +template +bool Ring_Buffer_Ex::getbytes_(void *data, size_t len) +{ + return getbytes_ex_(data, len, true); +} + +template +bool Ring_Buffer_Ex::peekbytes_(void *data, size_t len) const +{ + auto *ncthis = const_cast *>(this); + return ncthis->getbytes_ex_(data, len, false); +} + +template +bool Ring_Buffer_Ex::getbytes_ex_(void *data, size_t len, bool advp) +{ + if (size_used() < len) + return false; + + const size_t rp = rp_, cap = cap_; + const uint8_t *src = rbdata_.get(); + uint8_t *dst = (uint8_t *)data; + + if (data) { + const size_t taillen = std::min(len, cap - rp); + if_constexpr (Atomic) + std::atomic_thread_fence(std::memory_order_acquire); + std::copy_n(&src[rp], taillen, dst); + std::copy_n(src, len - taillen, dst + taillen); + } + + if (advp) + rp_ = (rp + len < cap) ? (rp + len) : (rp + len - cap); + return true; +} + +template +bool Ring_Buffer_Ex::putbytes_(const void *data, size_t len) +{ + if (size_free() < len) + return false; + + const size_t wp = wp_, cap = cap_; + const uint8_t *src = (const uint8_t *)data; + uint8_t *dst = rbdata_.get(); + + const size_t taillen = std::min(len, cap - wp); + std::copy_n(src, taillen, &dst[wp]); + std::copy_n(src + taillen, len - taillen, dst); + if_constexpr (Atomic) + std::atomic_thread_fence(std::memory_order_release); + + wp_ = (wp + len < cap) ? (wp + len) : (wp + len - cap); + return true; +} + +template class Ring_Buffer_Ex; +template class Ring_Buffer_Ex; diff --git a/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.h b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.h new file mode 100644 index 0000000..395ba9e --- /dev/null +++ b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.h @@ -0,0 +1,69 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#pragma once +#include +#include +#include +#include +#include + +#if defined(__cpp_lib_atomic_is_always_lock_free) +static_assert( + std::atomic::is_always_lock_free, "atomic must be lock free"); +#endif + +//------------------------------------------------------------------------------ +template class Ring_Buffer_Ex; +typedef Ring_Buffer_Ex Ring_Buffer; + +//------------------------------------------------------------------------------ +template +class Basic_Ring_Buffer { +public: + // read operations + template bool get(T &x); + template bool get(T *x, size_t n); + template bool peek(T &x); + template bool peek(T *x, size_t n); + // write operations + template bool put(const T &x); + template bool put(const T *x, size_t n); +}; + +//------------------------------------------------------------------------------ +template +class Ring_Buffer_Ex final : + private Basic_Ring_Buffer> { +private: + typedef Basic_Ring_Buffer> Base; +public: + // initialization and cleanup + explicit Ring_Buffer_Ex(size_t capacity); + ~Ring_Buffer_Ex(); + // attributes + size_t capacity() const; + // read operations + size_t size_used() const; + bool discard(size_t len); + using Base::get; + using Base::peek; + // write operations + size_t size_free() const; + using Base::put; + +private: + size_t cap_{0}; + typename std::conditional, size_t>::type rp_{0}, wp_{0}; + std::unique_ptr rbdata_ {}; + friend Base; + bool getbytes_(void *data, size_t len); + bool peekbytes_(void *data, size_t len) const; + bool getbytes_ex_(void *data, size_t len, bool advp); + bool putbytes_(const void *data, size_t len); +}; + +//------------------------------------------------------------------------------ +#include "ring_buffer.tcc" diff --git a/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc new file mode 100644 index 0000000..b53b17f --- /dev/null +++ b/plugins/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc @@ -0,0 +1,61 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "ring_buffer.h" + +template +inline size_t Ring_Buffer_Ex::capacity() const +{ + return cap_ - 1; +} + +//------------------------------------------------------------------------------ +template +template +inline bool Basic_Ring_Buffer::get(T &x) +{ + return get(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::get(T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->getbytes_(x, n * sizeof(T)); +} + +template +template +inline bool Basic_Ring_Buffer::peek(T &x) +{ + return peek(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::peek(T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->peekbytes_(x, n * sizeof(T)); +} + +template +template +inline bool Basic_Ring_Buffer::put(const T &x) +{ + return put(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::put(const T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->putbytes_(x, n * sizeof(T)); +} diff --git a/plugins/vst/external/sfzt_auwrapper b/plugins/vst/external/sfzt_auwrapper new file mode 160000 index 0000000..014311a --- /dev/null +++ b/plugins/vst/external/sfzt_auwrapper @@ -0,0 +1 @@ +Subproject commit 014311ae45b86571e1ae3aaa03ebbd7db8b3a32e diff --git a/plugins/vst/gpl-3.0.txt b/plugins/vst/gpl-3.0.txt new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/plugins/vst/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/plugins/vst/mac/Info.au.plist b/plugins/vst/mac/Info.au.plist new file mode 100644 index 0000000..f0a80fd --- /dev/null +++ b/plugins/vst/mac/Info.au.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + @SFIZZ_AU_BUNDLE_EXECUTABLE@ + CFBundleIdentifier + @SFIZZ_AU_BUNDLE_IDENTIFIER@ + CFBundleName + sfizz + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + @SFIZZ_AU_BUNDLE_VERSION@ + CSResourcesFileMapped + + AudioComponents + + + description + @PROJECT_NAME@ + factoryFunction + AUWrapperFactory + manufacturer + @SFIZZ_AU_BUNDLE_MANUFACTURER@ + name + @SFIZZ_AU_BUNDLE_AUTHOR@: @PROJECT_NAME@ + sandboxSafe + + type + @SFIZZ_AU_BUNDLE_TYPE@ + subtype + @SFIZZ_AU_BUNDLE_SUBTYPE@ + tags + + Sampler + Synthesizer + + version + @SFIZZ_AU_DECIMAL_VERSION@ + + + + diff --git a/plugins/vst/mac/Info.vst3.plist b/plugins/vst/mac/Info.vst3.plist new file mode 100644 index 0000000..7e3b3a7 --- /dev/null +++ b/plugins/vst/mac/Info.vst3.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + @SFIZZ_VST3_BUNDLE_EXECUTABLE@ + CFBundleIdentifier + tools.sfz.sfizz.vst + CFBundleName + sfizz + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + @SFIZZ_VST3_BUNDLE_VERSION@ + CSResourcesFileMapped + + + diff --git a/plugins/vst/mac/PkgInfo b/plugins/vst/mac/PkgInfo new file mode 100644 index 0000000..19a9cf6 --- /dev/null +++ b/plugins/vst/mac/PkgInfo @@ -0,0 +1 @@ +BNDL???? \ No newline at end of file diff --git a/plugins/vst/mac/audiounitconfig.h.in b/plugins/vst/mac/audiounitconfig.h.in new file mode 100644 index 0000000..0207e58 --- /dev/null +++ b/plugins/vst/mac/audiounitconfig.h.in @@ -0,0 +1,15 @@ +#define kAudioUnitBundleIdentifier @SFIZZ_AU_BUNDLE_IDENTIFIER@ +#define kAudioUnitVersion 0x@SFIZZ_AU_HEXADECIMAL_VERSION@ +#define kAUPluginName @SFIZZ_AU_BUNDLE_AUTHOR@: @PROJECT_NAME@ +#define kAUPluginDescription @PROJECT_NAME@ +#define kAUPluginType @SFIZZ_AU_BUNDLE_TYPE@ +#define kAUPluginSubType @SFIZZ_AU_BUNDLE_SUBTYPE@ +#define kAUPluginManufacturer @SFIZZ_AU_BUNDLE_MANUFACTURER@ + +#define kAudioUnitName "@SFIZZ_AU_BUNDLE_AUTHOR@: @PROJECT_NAME@" +#define kAudioUnitDescription "@PROJECT_NAME@" +#define kAudioUnitType '@SFIZZ_AU_BUNDLE_TYPE@' +#define kAudioUnitComponentSubType '@SFIZZ_AU_BUNDLE_SUBTYPE@' +#define kAudioUnitComponentManuf '@SFIZZ_AU_BUNDLE_MANUFACTURER@' + +#define kAudioUnitCarbonView 1 diff --git a/plugins/vst/vst3.def b/plugins/vst/vst3.def new file mode 100644 index 0000000..279a6a5 --- /dev/null +++ b/plugins/vst/vst3.def @@ -0,0 +1,4 @@ +EXPORTS + GetPluginFactory + InitDll + ExitDll diff --git a/plugins/vst/vst3.version b/plugins/vst/vst3.version new file mode 100644 index 0000000..f86d95c --- /dev/null +++ b/plugins/vst/vst3.version @@ -0,0 +1,7 @@ +VST3ABI_1.0 { + global: + *GetPluginFactory*; + *ModuleEntry*; + *ModuleExit*; + local: *; +}; diff --git a/plugins/vst/win/Plugin.ico b/plugins/vst/win/Plugin.ico new file mode 100644 index 0000000000000000000000000000000000000000..516565763840869a9599eb1fecf90b068b7f048c GIT binary patch literal 351974 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 literal 0 HcmV?d00001 diff --git a/plugins/vst/win/desktop.ini b/plugins/vst/win/desktop.ini new file mode 100644 index 0000000..be3001a --- /dev/null +++ b/plugins/vst/win/desktop.ini @@ -0,0 +1,2 @@ +[.ShellClassInfo] +IconResource=Plugin.ico,0 diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..464cb9ca8ca2450dc324142d6d9b1357b3456261 GIT binary patch literal 44587 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 M +#include +#endif + +#ifndef COM_NO_WINDOWS_H +#include +#include +#endif + +#ifndef __dwrite_3_h__ +#define __dwrite_3_h__ + +/* Forward declarations */ + +#ifndef __IDWriteFontDownloadListener_FWD_DEFINED__ +#define __IDWriteFontDownloadListener_FWD_DEFINED__ +typedef interface IDWriteFontDownloadListener IDWriteFontDownloadListener; +#ifdef __cplusplus +interface IDWriteFontDownloadListener; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontDownloadQueue_FWD_DEFINED__ +#define __IDWriteFontDownloadQueue_FWD_DEFINED__ +typedef interface IDWriteFontDownloadQueue IDWriteFontDownloadQueue; +#ifdef __cplusplus +interface IDWriteFontDownloadQueue; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteRenderingParams3_FWD_DEFINED__ +#define __IDWriteRenderingParams3_FWD_DEFINED__ +typedef interface IDWriteRenderingParams3 IDWriteRenderingParams3; +#ifdef __cplusplus +interface IDWriteRenderingParams3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteStringList_FWD_DEFINED__ +#define __IDWriteStringList_FWD_DEFINED__ +typedef interface IDWriteStringList IDWriteStringList; +#ifdef __cplusplus +interface IDWriteStringList; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSet_FWD_DEFINED__ +#define __IDWriteFontSet_FWD_DEFINED__ +typedef interface IDWriteFontSet IDWriteFontSet; +#ifdef __cplusplus +interface IDWriteFontSet; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontResource_FWD_DEFINED__ +#define __IDWriteFontResource_FWD_DEFINED__ +typedef interface IDWriteFontResource IDWriteFontResource; +#ifdef __cplusplus +interface IDWriteFontResource; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSet1_FWD_DEFINED__ +#define __IDWriteFontSet1_FWD_DEFINED__ +typedef interface IDWriteFontSet1 IDWriteFontSet1; +#ifdef __cplusplus +interface IDWriteFontSet1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFont3_FWD_DEFINED__ +#define __IDWriteFont3_FWD_DEFINED__ +typedef interface IDWriteFont3 IDWriteFont3; +#ifdef __cplusplus +interface IDWriteFont3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFamily1_FWD_DEFINED__ +#define __IDWriteFontFamily1_FWD_DEFINED__ +typedef interface IDWriteFontFamily1 IDWriteFontFamily1; +#ifdef __cplusplus +interface IDWriteFontFamily1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFamily2_FWD_DEFINED__ +#define __IDWriteFontFamily2_FWD_DEFINED__ +typedef interface IDWriteFontFamily2 IDWriteFontFamily2; +#ifdef __cplusplus +interface IDWriteFontFamily2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontCollection1_FWD_DEFINED__ +#define __IDWriteFontCollection1_FWD_DEFINED__ +typedef interface IDWriteFontCollection1 IDWriteFontCollection1; +#ifdef __cplusplus +interface IDWriteFontCollection1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontCollection2_FWD_DEFINED__ +#define __IDWriteFontCollection2_FWD_DEFINED__ +typedef interface IDWriteFontCollection2 IDWriteFontCollection2; +#ifdef __cplusplus +interface IDWriteFontCollection2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontCollection3_FWD_DEFINED__ +#define __IDWriteFontCollection3_FWD_DEFINED__ +typedef interface IDWriteFontCollection3 IDWriteFontCollection3; +#ifdef __cplusplus +interface IDWriteFontCollection3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFaceReference_FWD_DEFINED__ +#define __IDWriteFontFaceReference_FWD_DEFINED__ +typedef interface IDWriteFontFaceReference IDWriteFontFaceReference; +#ifdef __cplusplus +interface IDWriteFontFaceReference; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFaceReference1_FWD_DEFINED__ +#define __IDWriteFontFaceReference1_FWD_DEFINED__ +typedef interface IDWriteFontFaceReference1 IDWriteFontFaceReference1; +#ifdef __cplusplus +interface IDWriteFontFaceReference1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontList1_FWD_DEFINED__ +#define __IDWriteFontList1_FWD_DEFINED__ +typedef interface IDWriteFontList1 IDWriteFontList1; +#ifdef __cplusplus +interface IDWriteFontList1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontList2_FWD_DEFINED__ +#define __IDWriteFontList2_FWD_DEFINED__ +typedef interface IDWriteFontList2 IDWriteFontList2; +#ifdef __cplusplus +interface IDWriteFontList2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSet2_FWD_DEFINED__ +#define __IDWriteFontSet2_FWD_DEFINED__ +typedef interface IDWriteFontSet2 IDWriteFontSet2; +#ifdef __cplusplus +interface IDWriteFontSet2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSet3_FWD_DEFINED__ +#define __IDWriteFontSet3_FWD_DEFINED__ +typedef interface IDWriteFontSet3 IDWriteFontSet3; +#ifdef __cplusplus +interface IDWriteFontSet3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFace3_FWD_DEFINED__ +#define __IDWriteFontFace3_FWD_DEFINED__ +typedef interface IDWriteFontFace3 IDWriteFontFace3; +#ifdef __cplusplus +interface IDWriteFontFace3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteTextFormat2_FWD_DEFINED__ +#define __IDWriteTextFormat2_FWD_DEFINED__ +typedef interface IDWriteTextFormat2 IDWriteTextFormat2; +#ifdef __cplusplus +interface IDWriteTextFormat2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteTextFormat3_FWD_DEFINED__ +#define __IDWriteTextFormat3_FWD_DEFINED__ +typedef interface IDWriteTextFormat3 IDWriteTextFormat3; +#ifdef __cplusplus +interface IDWriteTextFormat3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteTextLayout3_FWD_DEFINED__ +#define __IDWriteTextLayout3_FWD_DEFINED__ +typedef interface IDWriteTextLayout3 IDWriteTextLayout3; +#ifdef __cplusplus +interface IDWriteTextLayout3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteTextLayout4_FWD_DEFINED__ +#define __IDWriteTextLayout4_FWD_DEFINED__ +typedef interface IDWriteTextLayout4 IDWriteTextLayout4; +#ifdef __cplusplus +interface IDWriteTextLayout4; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFallback1_FWD_DEFINED__ +#define __IDWriteFontFallback1_FWD_DEFINED__ +typedef interface IDWriteFontFallback1 IDWriteFontFallback1; +#ifdef __cplusplus +interface IDWriteFontFallback1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteGdiInterop1_FWD_DEFINED__ +#define __IDWriteGdiInterop1_FWD_DEFINED__ +typedef interface IDWriteGdiInterop1 IDWriteGdiInterop1; +#ifdef __cplusplus +interface IDWriteGdiInterop1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSetBuilder_FWD_DEFINED__ +#define __IDWriteFontSetBuilder_FWD_DEFINED__ +typedef interface IDWriteFontSetBuilder IDWriteFontSetBuilder; +#ifdef __cplusplus +interface IDWriteFontSetBuilder; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSetBuilder1_FWD_DEFINED__ +#define __IDWriteFontSetBuilder1_FWD_DEFINED__ +typedef interface IDWriteFontSetBuilder1 IDWriteFontSetBuilder1; +#ifdef __cplusplus +interface IDWriteFontSetBuilder1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSetBuilder2_FWD_DEFINED__ +#define __IDWriteFontSetBuilder2_FWD_DEFINED__ +typedef interface IDWriteFontSetBuilder2 IDWriteFontSetBuilder2; +#ifdef __cplusplus +interface IDWriteFontSetBuilder2; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFactory3_FWD_DEFINED__ +#define __IDWriteFactory3_FWD_DEFINED__ +typedef interface IDWriteFactory3 IDWriteFactory3; +#ifdef __cplusplus +interface IDWriteFactory3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFace4_FWD_DEFINED__ +#define __IDWriteFontFace4_FWD_DEFINED__ +typedef interface IDWriteFontFace4 IDWriteFontFace4; +#ifdef __cplusplus +interface IDWriteFontFace4; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFace5_FWD_DEFINED__ +#define __IDWriteFontFace5_FWD_DEFINED__ +typedef interface IDWriteFontFace5 IDWriteFontFace5; +#ifdef __cplusplus +interface IDWriteFontFace5; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteColorGlyphRunEnumerator1_FWD_DEFINED__ +#define __IDWriteColorGlyphRunEnumerator1_FWD_DEFINED__ +typedef interface IDWriteColorGlyphRunEnumerator1 IDWriteColorGlyphRunEnumerator1; +#ifdef __cplusplus +interface IDWriteColorGlyphRunEnumerator1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFactory4_FWD_DEFINED__ +#define __IDWriteFactory4_FWD_DEFINED__ +typedef interface IDWriteFactory4 IDWriteFactory4; +#ifdef __cplusplus +interface IDWriteFactory4; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteAsyncResult_FWD_DEFINED__ +#define __IDWriteAsyncResult_FWD_DEFINED__ +typedef interface IDWriteAsyncResult IDWriteAsyncResult; +#ifdef __cplusplus +interface IDWriteAsyncResult; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteRemoteFontFileStream_FWD_DEFINED__ +#define __IDWriteRemoteFontFileStream_FWD_DEFINED__ +typedef interface IDWriteRemoteFontFileStream IDWriteRemoteFontFileStream; +#ifdef __cplusplus +interface IDWriteRemoteFontFileStream; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteRemoteFontFileLoader_FWD_DEFINED__ +#define __IDWriteRemoteFontFileLoader_FWD_DEFINED__ +typedef interface IDWriteRemoteFontFileLoader IDWriteRemoteFontFileLoader; +#ifdef __cplusplus +interface IDWriteRemoteFontFileLoader; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteInMemoryFontFileLoader_FWD_DEFINED__ +#define __IDWriteInMemoryFontFileLoader_FWD_DEFINED__ +typedef interface IDWriteInMemoryFontFileLoader IDWriteInMemoryFontFileLoader; +#ifdef __cplusplus +interface IDWriteInMemoryFontFileLoader; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFactory5_FWD_DEFINED__ +#define __IDWriteFactory5_FWD_DEFINED__ +typedef interface IDWriteFactory5 IDWriteFactory5; +#ifdef __cplusplus +interface IDWriteFactory5; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFactory6_FWD_DEFINED__ +#define __IDWriteFactory6_FWD_DEFINED__ +typedef interface IDWriteFactory6 IDWriteFactory6; +#ifdef __cplusplus +interface IDWriteFactory6; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFactory7_FWD_DEFINED__ +#define __IDWriteFactory7_FWD_DEFINED__ +typedef interface IDWriteFactory7 IDWriteFactory7; +#ifdef __cplusplus +interface IDWriteFactory7; +#endif /* __cplusplus */ +#endif + +/* Headers for imported files */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __IDWriteFontFaceReference_FWD_DEFINED__ +#define __IDWriteFontFaceReference_FWD_DEFINED__ +typedef interface IDWriteFontFaceReference IDWriteFontFaceReference; +#ifdef __cplusplus +interface IDWriteFontFaceReference; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFaceReference1_FWD_DEFINED__ +#define __IDWriteFontFaceReference1_FWD_DEFINED__ +typedef interface IDWriteFontFaceReference1 IDWriteFontFaceReference1; +#ifdef __cplusplus +interface IDWriteFontFaceReference1; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFace3_FWD_DEFINED__ +#define __IDWriteFontFace3_FWD_DEFINED__ +typedef interface IDWriteFontFace3 IDWriteFontFace3; +#ifdef __cplusplus +interface IDWriteFontFace3; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontSet_FWD_DEFINED__ +#define __IDWriteFontSet_FWD_DEFINED__ +typedef interface IDWriteFontSet IDWriteFontSet; +#ifdef __cplusplus +interface IDWriteFontSet; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontDownloadQueue_FWD_DEFINED__ +#define __IDWriteFontDownloadQueue_FWD_DEFINED__ +typedef interface IDWriteFontDownloadQueue IDWriteFontDownloadQueue; +#ifdef __cplusplus +interface IDWriteFontDownloadQueue; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontFace5_FWD_DEFINED__ +#define __IDWriteFontFace5_FWD_DEFINED__ +typedef interface IDWriteFontFace5 IDWriteFontFace5; +#ifdef __cplusplus +interface IDWriteFontFace5; +#endif /* __cplusplus */ +#endif + +#ifndef __IDWriteFontList2_FWD_DEFINED__ +#define __IDWriteFontList2_FWD_DEFINED__ +typedef interface IDWriteFontList2 IDWriteFontList2; +#ifdef __cplusplus +interface IDWriteFontList2; +#endif /* __cplusplus */ +#endif + +#ifndef _WINGDI_ +typedef struct FONTSIGNATURE FONTSIGNATURE; +#endif /* _WINGDI_ */ +typedef enum DWRITE_LOCALITY { + DWRITE_LOCALITY_REMOTE = 0, + DWRITE_LOCALITY_PARTIAL = 1, + DWRITE_LOCALITY_LOCAL = 2 +} DWRITE_LOCALITY; +typedef enum DWRITE_RENDERING_MODE1 { + DWRITE_RENDERING_MODE1_DEFAULT = 0, + DWRITE_RENDERING_MODE1_ALIASED = 1, + DWRITE_RENDERING_MODE1_GDI_CLASSIC = 2, + DWRITE_RENDERING_MODE1_GDI_NATURAL = 3, + DWRITE_RENDERING_MODE1_NATURAL = 4, + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC = 5, + DWRITE_RENDERING_MODE1_OUTLINE = 6, + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC_DOWNSAMPLED = 7 +} DWRITE_RENDERING_MODE1; +typedef enum DWRITE_FONT_PROPERTY_ID { + DWRITE_FONT_PROPERTY_ID_NONE = 0, + DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME = 1, + DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FAMILY_NAME = 2, + DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FACE_NAME = 3, + DWRITE_FONT_PROPERTY_ID_FULL_NAME = 4, + DWRITE_FONT_PROPERTY_ID_WIN32_FAMILY_NAME = 5, + DWRITE_FONT_PROPERTY_ID_POSTSCRIPT_NAME = 6, + DWRITE_FONT_PROPERTY_ID_DESIGN_SCRIPT_LANGUAGE_TAG = 7, + DWRITE_FONT_PROPERTY_ID_SUPPORTED_SCRIPT_LANGUAGE_TAG = 8, + DWRITE_FONT_PROPERTY_ID_SEMANTIC_TAG = 9, + DWRITE_FONT_PROPERTY_ID_WEIGHT = 10, + DWRITE_FONT_PROPERTY_ID_STRETCH = 11, + DWRITE_FONT_PROPERTY_ID_STYLE = 12, + DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FACE_NAME = 13, + DWRITE_FONT_PROPERTY_ID_TOTAL = DWRITE_FONT_PROPERTY_ID_STYLE + 1, + DWRITE_FONT_PROPERTY_ID_TOTAL_RS3 = DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FACE_NAME + 1, + DWRITE_FONT_PROPERTY_ID_FAMILY_NAME = DWRITE_FONT_PROPERTY_ID_TYPOGRAPHIC_FAMILY_NAME, + DWRITE_FONT_PROPERTY_ID_PREFERRED_FAMILY_NAME = DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, + DWRITE_FONT_PROPERTY_ID_FACE_NAME = DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FACE_NAME +} DWRITE_FONT_PROPERTY_ID; +typedef struct DWRITE_FONT_PROPERTY { + DWRITE_FONT_PROPERTY_ID propertyId; + const WCHAR *propertyValue; + const WCHAR *localeName; +} DWRITE_FONT_PROPERTY; +typedef enum DWRITE_FONT_AXIS_TAG { + DWRITE_FONT_AXIS_TAG_WEIGHT = 0x74686777, + DWRITE_FONT_AXIS_TAG_WIDTH = 0x68746477, + DWRITE_FONT_AXIS_TAG_SLANT = 0x746e6c73, + DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE = 0x7a73706f, + DWRITE_FONT_AXIS_TAG_ITALIC = 0x6c617469 +} DWRITE_FONT_AXIS_TAG; +typedef enum DWRITE_FONT_SOURCE_TYPE { + DWRITE_FONT_SOURCE_TYPE_UNKNOWN = 0, + DWRITE_FONT_SOURCE_TYPE_PER_MACHINE = 1, + DWRITE_FONT_SOURCE_TYPE_PER_USER = 2, + DWRITE_FONT_SOURCE_TYPE_APPX_PACKAGE = 3, + DWRITE_FONT_SOURCE_TYPE_REMOTE_FONT_PROVIDER = 4 +} DWRITE_FONT_SOURCE_TYPE; +typedef struct DWRITE_FONT_AXIS_VALUE { + DWRITE_FONT_AXIS_TAG axisTag; + FLOAT value; +} DWRITE_FONT_AXIS_VALUE; +typedef struct DWRITE_FONT_AXIS_RANGE { + DWRITE_FONT_AXIS_TAG axisTag; + FLOAT minValue; + FLOAT maxValue; +} DWRITE_FONT_AXIS_RANGE; +typedef enum DWRITE_AUTOMATIC_FONT_AXES { + DWRITE_AUTOMATIC_FONT_AXES_NONE = 0, + DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE = 1 +} DWRITE_AUTOMATIC_FONT_AXES; +typedef enum DWRITE_FONT_AXIS_ATTRIBUTES { + DWRITE_FONT_AXIS_ATTRIBUTES_NONE = 0, + DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE = 1, + DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN = 2 +} DWRITE_FONT_AXIS_ATTRIBUTES; +typedef enum DWRITE_FONT_FAMILY_MODEL { + DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC = 0, + DWRITE_FONT_FAMILY_MODEL_WEIGHT_STRETCH_STYLE = 1 +} DWRITE_FONT_FAMILY_MODEL; +/***************************************************************************** + * IDWriteFontDownloadListener interface + */ +#ifndef __IDWriteFontDownloadListener_INTERFACE_DEFINED__ +#define __IDWriteFontDownloadListener_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontDownloadListener, 0xb06fe5b9, 0x43ec, 0x4393, 0x88,0x1b, 0xdb,0xe4,0xdc,0x72,0xfd,0xa7); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("b06fe5b9-43ec-4393-881b-dbe4dc72fda7") +IDWriteFontDownloadListener : public IUnknown +{ + virtual void STDMETHODCALLTYPE DownloadCompleted( + IDWriteFontDownloadQueue *queue, + IUnknown *context, + HRESULT result) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontDownloadListener, 0xb06fe5b9, 0x43ec, 0x4393, 0x88,0x1b, 0xdb,0xe4,0xdc,0x72,0xfd,0xa7) +#endif +#else +typedef struct IDWriteFontDownloadListenerVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontDownloadListener *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontDownloadListener *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontDownloadListener *This); + + /*** IDWriteFontDownloadListener methods ***/ + void (STDMETHODCALLTYPE *DownloadCompleted)( + IDWriteFontDownloadListener *This, + IDWriteFontDownloadQueue *queue, + IUnknown *context, + HRESULT result); + + END_INTERFACE +} IDWriteFontDownloadListenerVtbl; + +interface IDWriteFontDownloadListener { + CONST_VTBL IDWriteFontDownloadListenerVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontDownloadListener_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontDownloadListener_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontDownloadListener_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontDownloadListener methods ***/ +#define IDWriteFontDownloadListener_DownloadCompleted(This,queue,context,result) (This)->lpVtbl->DownloadCompleted(This,queue,context,result) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontDownloadListener_QueryInterface(IDWriteFontDownloadListener* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontDownloadListener_AddRef(IDWriteFontDownloadListener* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontDownloadListener_Release(IDWriteFontDownloadListener* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontDownloadListener methods ***/ +static FORCEINLINE void IDWriteFontDownloadListener_DownloadCompleted(IDWriteFontDownloadListener* This,IDWriteFontDownloadQueue *queue,IUnknown *context,HRESULT result) { + This->lpVtbl->DownloadCompleted(This,queue,context,result); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontDownloadListener_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontDownloadQueue interface + */ +#ifndef __IDWriteFontDownloadQueue_INTERFACE_DEFINED__ +#define __IDWriteFontDownloadQueue_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontDownloadQueue, 0xb71e6052, 0x5aea, 0x4fa3, 0x83,0x2e, 0xf6,0x0d,0x43,0x1f,0x7e,0x91); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("b71e6052-5aea-4fa3-832e-f60d431f7e91") +IDWriteFontDownloadQueue : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE AddListener( + IDWriteFontDownloadListener *listener, + UINT32 *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveListener( + UINT32 token) = 0; + + virtual WINBOOL STDMETHODCALLTYPE IsEmpty( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE BeginDownload( + IUnknown *context) = 0; + + virtual HRESULT STDMETHODCALLTYPE CancelDownload( + ) = 0; + + virtual UINT64 STDMETHODCALLTYPE GetGenerationCount( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontDownloadQueue, 0xb71e6052, 0x5aea, 0x4fa3, 0x83,0x2e, 0xf6,0x0d,0x43,0x1f,0x7e,0x91) +#endif +#else +typedef struct IDWriteFontDownloadQueueVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontDownloadQueue *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontDownloadQueue *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontDownloadQueue *This); + + /*** IDWriteFontDownloadQueue methods ***/ + HRESULT (STDMETHODCALLTYPE *AddListener)( + IDWriteFontDownloadQueue *This, + IDWriteFontDownloadListener *listener, + UINT32 *token); + + HRESULT (STDMETHODCALLTYPE *RemoveListener)( + IDWriteFontDownloadQueue *This, + UINT32 token); + + WINBOOL (STDMETHODCALLTYPE *IsEmpty)( + IDWriteFontDownloadQueue *This); + + HRESULT (STDMETHODCALLTYPE *BeginDownload)( + IDWriteFontDownloadQueue *This, + IUnknown *context); + + HRESULT (STDMETHODCALLTYPE *CancelDownload)( + IDWriteFontDownloadQueue *This); + + UINT64 (STDMETHODCALLTYPE *GetGenerationCount)( + IDWriteFontDownloadQueue *This); + + END_INTERFACE +} IDWriteFontDownloadQueueVtbl; + +interface IDWriteFontDownloadQueue { + CONST_VTBL IDWriteFontDownloadQueueVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontDownloadQueue_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontDownloadQueue_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontDownloadQueue_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontDownloadQueue methods ***/ +#define IDWriteFontDownloadQueue_AddListener(This,listener,token) (This)->lpVtbl->AddListener(This,listener,token) +#define IDWriteFontDownloadQueue_RemoveListener(This,token) (This)->lpVtbl->RemoveListener(This,token) +#define IDWriteFontDownloadQueue_IsEmpty(This) (This)->lpVtbl->IsEmpty(This) +#define IDWriteFontDownloadQueue_BeginDownload(This,context) (This)->lpVtbl->BeginDownload(This,context) +#define IDWriteFontDownloadQueue_CancelDownload(This) (This)->lpVtbl->CancelDownload(This) +#define IDWriteFontDownloadQueue_GetGenerationCount(This) (This)->lpVtbl->GetGenerationCount(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontDownloadQueue_QueryInterface(IDWriteFontDownloadQueue* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontDownloadQueue_AddRef(IDWriteFontDownloadQueue* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontDownloadQueue_Release(IDWriteFontDownloadQueue* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontDownloadQueue methods ***/ +static FORCEINLINE HRESULT IDWriteFontDownloadQueue_AddListener(IDWriteFontDownloadQueue* This,IDWriteFontDownloadListener *listener,UINT32 *token) { + return This->lpVtbl->AddListener(This,listener,token); +} +static FORCEINLINE HRESULT IDWriteFontDownloadQueue_RemoveListener(IDWriteFontDownloadQueue* This,UINT32 token) { + return This->lpVtbl->RemoveListener(This,token); +} +static FORCEINLINE WINBOOL IDWriteFontDownloadQueue_IsEmpty(IDWriteFontDownloadQueue* This) { + return This->lpVtbl->IsEmpty(This); +} +static FORCEINLINE HRESULT IDWriteFontDownloadQueue_BeginDownload(IDWriteFontDownloadQueue* This,IUnknown *context) { + return This->lpVtbl->BeginDownload(This,context); +} +static FORCEINLINE HRESULT IDWriteFontDownloadQueue_CancelDownload(IDWriteFontDownloadQueue* This) { + return This->lpVtbl->CancelDownload(This); +} +static FORCEINLINE UINT64 IDWriteFontDownloadQueue_GetGenerationCount(IDWriteFontDownloadQueue* This) { + return This->lpVtbl->GetGenerationCount(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontDownloadQueue_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteRenderingParams3 interface + */ +#ifndef __IDWriteRenderingParams3_INTERFACE_DEFINED__ +#define __IDWriteRenderingParams3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteRenderingParams3, 0xb7924baa, 0x391b, 0x412a, 0x8c,0x5c, 0xe4,0x4c,0xc2,0xd8,0x67,0xdc); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("b7924baa-391b-412a-8c5c-e44cc2d867dc") +IDWriteRenderingParams3 : public IDWriteRenderingParams2 +{ + virtual DWRITE_RENDERING_MODE1 STDMETHODCALLTYPE GetRenderingMode1( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteRenderingParams3, 0xb7924baa, 0x391b, 0x412a, 0x8c,0x5c, 0xe4,0x4c,0xc2,0xd8,0x67,0xdc) +#endif +#else +typedef struct IDWriteRenderingParams3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteRenderingParams3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteRenderingParams3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteRenderingParams3 *This); + + /*** IDWriteRenderingParams methods ***/ + FLOAT (STDMETHODCALLTYPE *GetGamma)( + IDWriteRenderingParams3 *This); + + FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)( + IDWriteRenderingParams3 *This); + + FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)( + IDWriteRenderingParams3 *This); + + DWRITE_PIXEL_GEOMETRY (STDMETHODCALLTYPE *GetPixelGeometry)( + IDWriteRenderingParams3 *This); + + DWRITE_RENDERING_MODE (STDMETHODCALLTYPE *GetRenderingMode)( + IDWriteRenderingParams3 *This); + + /*** IDWriteRenderingParams1 methods ***/ + FLOAT (STDMETHODCALLTYPE *GetGrayscaleEnhancedContrast)( + IDWriteRenderingParams3 *This); + + /*** IDWriteRenderingParams2 methods ***/ + DWRITE_GRID_FIT_MODE (STDMETHODCALLTYPE *GetGridFitMode)( + IDWriteRenderingParams3 *This); + + /*** IDWriteRenderingParams3 methods ***/ + DWRITE_RENDERING_MODE1 (STDMETHODCALLTYPE *GetRenderingMode1)( + IDWriteRenderingParams3 *This); + + END_INTERFACE +} IDWriteRenderingParams3Vtbl; + +interface IDWriteRenderingParams3 { + CONST_VTBL IDWriteRenderingParams3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteRenderingParams3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteRenderingParams3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteRenderingParams3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteRenderingParams methods ***/ +#define IDWriteRenderingParams3_GetGamma(This) (This)->lpVtbl->GetGamma(This) +#define IDWriteRenderingParams3_GetEnhancedContrast(This) (This)->lpVtbl->GetEnhancedContrast(This) +#define IDWriteRenderingParams3_GetClearTypeLevel(This) (This)->lpVtbl->GetClearTypeLevel(This) +#define IDWriteRenderingParams3_GetPixelGeometry(This) (This)->lpVtbl->GetPixelGeometry(This) +#define IDWriteRenderingParams3_GetRenderingMode(This) (This)->lpVtbl->GetRenderingMode(This) +/*** IDWriteRenderingParams1 methods ***/ +#define IDWriteRenderingParams3_GetGrayscaleEnhancedContrast(This) (This)->lpVtbl->GetGrayscaleEnhancedContrast(This) +/*** IDWriteRenderingParams2 methods ***/ +#define IDWriteRenderingParams3_GetGridFitMode(This) (This)->lpVtbl->GetGridFitMode(This) +/*** IDWriteRenderingParams3 methods ***/ +#define IDWriteRenderingParams3_GetRenderingMode1(This) (This)->lpVtbl->GetRenderingMode1(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteRenderingParams3_QueryInterface(IDWriteRenderingParams3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteRenderingParams3_AddRef(IDWriteRenderingParams3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteRenderingParams3_Release(IDWriteRenderingParams3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteRenderingParams methods ***/ +static FORCEINLINE FLOAT IDWriteRenderingParams3_GetGamma(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetGamma(This); +} +static FORCEINLINE FLOAT IDWriteRenderingParams3_GetEnhancedContrast(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetEnhancedContrast(This); +} +static FORCEINLINE FLOAT IDWriteRenderingParams3_GetClearTypeLevel(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetClearTypeLevel(This); +} +static FORCEINLINE DWRITE_PIXEL_GEOMETRY IDWriteRenderingParams3_GetPixelGeometry(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetPixelGeometry(This); +} +static FORCEINLINE DWRITE_RENDERING_MODE IDWriteRenderingParams3_GetRenderingMode(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetRenderingMode(This); +} +/*** IDWriteRenderingParams1 methods ***/ +static FORCEINLINE FLOAT IDWriteRenderingParams3_GetGrayscaleEnhancedContrast(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetGrayscaleEnhancedContrast(This); +} +/*** IDWriteRenderingParams2 methods ***/ +static FORCEINLINE DWRITE_GRID_FIT_MODE IDWriteRenderingParams3_GetGridFitMode(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetGridFitMode(This); +} +/*** IDWriteRenderingParams3 methods ***/ +static FORCEINLINE DWRITE_RENDERING_MODE1 IDWriteRenderingParams3_GetRenderingMode1(IDWriteRenderingParams3* This) { + return This->lpVtbl->GetRenderingMode1(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteRenderingParams3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteStringList interface + */ +#ifndef __IDWriteStringList_INTERFACE_DEFINED__ +#define __IDWriteStringList_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteStringList, 0xcfee3140, 0x1257, 0x47ca, 0x8b,0x85, 0x31,0xbf,0xcf,0x3f,0x2d,0x0e); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("cfee3140-1257-47ca-8b85-31bfcf3f2d0e") +IDWriteStringList : public IUnknown +{ + virtual UINT32 STDMETHODCALLTYPE GetCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLocaleNameLength( + UINT32 index, + UINT32 *length) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLocaleName( + UINT32 index, + WCHAR *name, + UINT32 size) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStringLength( + UINT32 index, + UINT32 *length) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetString( + UINT32 index, + WCHAR *string, + UINT32 size) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteStringList, 0xcfee3140, 0x1257, 0x47ca, 0x8b,0x85, 0x31,0xbf,0xcf,0x3f,0x2d,0x0e) +#endif +#else +typedef struct IDWriteStringListVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteStringList *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteStringList *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteStringList *This); + + /*** IDWriteStringList methods ***/ + UINT32 (STDMETHODCALLTYPE *GetCount)( + IDWriteStringList *This); + + HRESULT (STDMETHODCALLTYPE *GetLocaleNameLength)( + IDWriteStringList *This, + UINT32 index, + UINT32 *length); + + HRESULT (STDMETHODCALLTYPE *GetLocaleName)( + IDWriteStringList *This, + UINT32 index, + WCHAR *name, + UINT32 size); + + HRESULT (STDMETHODCALLTYPE *GetStringLength)( + IDWriteStringList *This, + UINT32 index, + UINT32 *length); + + HRESULT (STDMETHODCALLTYPE *GetString)( + IDWriteStringList *This, + UINT32 index, + WCHAR *string, + UINT32 size); + + END_INTERFACE +} IDWriteStringListVtbl; + +interface IDWriteStringList { + CONST_VTBL IDWriteStringListVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteStringList_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteStringList_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteStringList_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteStringList methods ***/ +#define IDWriteStringList_GetCount(This) (This)->lpVtbl->GetCount(This) +#define IDWriteStringList_GetLocaleNameLength(This,index,length) (This)->lpVtbl->GetLocaleNameLength(This,index,length) +#define IDWriteStringList_GetLocaleName(This,index,name,size) (This)->lpVtbl->GetLocaleName(This,index,name,size) +#define IDWriteStringList_GetStringLength(This,index,length) (This)->lpVtbl->GetStringLength(This,index,length) +#define IDWriteStringList_GetString(This,index,string,size) (This)->lpVtbl->GetString(This,index,string,size) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteStringList_QueryInterface(IDWriteStringList* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteStringList_AddRef(IDWriteStringList* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteStringList_Release(IDWriteStringList* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteStringList methods ***/ +static FORCEINLINE UINT32 IDWriteStringList_GetCount(IDWriteStringList* This) { + return This->lpVtbl->GetCount(This); +} +static FORCEINLINE HRESULT IDWriteStringList_GetLocaleNameLength(IDWriteStringList* This,UINT32 index,UINT32 *length) { + return This->lpVtbl->GetLocaleNameLength(This,index,length); +} +static FORCEINLINE HRESULT IDWriteStringList_GetLocaleName(IDWriteStringList* This,UINT32 index,WCHAR *name,UINT32 size) { + return This->lpVtbl->GetLocaleName(This,index,name,size); +} +static FORCEINLINE HRESULT IDWriteStringList_GetStringLength(IDWriteStringList* This,UINT32 index,UINT32 *length) { + return This->lpVtbl->GetStringLength(This,index,length); +} +static FORCEINLINE HRESULT IDWriteStringList_GetString(IDWriteStringList* This,UINT32 index,WCHAR *string,UINT32 size) { + return This->lpVtbl->GetString(This,index,string,size); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteStringList_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSet interface + */ +#ifndef __IDWriteFontSet_INTERFACE_DEFINED__ +#define __IDWriteFontSet_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSet, 0x53585141, 0xd9f8, 0x4095, 0x83,0x21, 0xd7,0x3c,0xf6,0xbd,0x11,0x6b); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("53585141-d9f8-4095-8321-d73cf6bd116b") +IDWriteFontSet : public IUnknown +{ + virtual UINT32 STDMETHODCALLTYPE GetFontCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + UINT32 index, + IDWriteFontFaceReference **reference) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindFontFaceReference( + IDWriteFontFaceReference *reference, + UINT32 *index, + WINBOOL *exists) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindFontFace( + IDWriteFontFace *fontface, + UINT32 *index, + WINBOOL *exists) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValues( + DWRITE_FONT_PROPERTY_ID id, + IDWriteStringList **values) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValues( + DWRITE_FONT_PROPERTY_ID id, + const WCHAR *preferred_locales, + IDWriteStringList **values) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValues( + UINT32 index, + DWRITE_FONT_PROPERTY_ID id, + WINBOOL *exists, + IDWriteLocalizedStrings **values) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPropertyOccurrenceCount( + const DWRITE_FONT_PROPERTY *property, + UINT32 *count) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMatchingFonts( + const WCHAR *family, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontSet **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMatchingFonts( + const DWRITE_FONT_PROPERTY *props, + UINT32 count, + IDWriteFontSet **fontset) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSet, 0x53585141, 0xd9f8, 0x4095, 0x83,0x21, 0xd7,0x3c,0xf6,0xbd,0x11,0x6b) +#endif +#else +typedef struct IDWriteFontSetVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSet *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSet *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSet *This); + + /*** IDWriteFontSet methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontSet *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontSet *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *FindFontFaceReference)( + IDWriteFontSet *This, + IDWriteFontFaceReference *reference, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *FindFontFace)( + IDWriteFontSet *This, + IDWriteFontFace *fontface, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues__)( + IDWriteFontSet *This, + DWRITE_FONT_PROPERTY_ID id, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues_)( + IDWriteFontSet *This, + DWRITE_FONT_PROPERTY_ID id, + const WCHAR *preferred_locales, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues)( + IDWriteFontSet *This, + UINT32 index, + DWRITE_FONT_PROPERTY_ID id, + WINBOOL *exists, + IDWriteLocalizedStrings **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyOccurrenceCount)( + IDWriteFontSet *This, + const DWRITE_FONT_PROPERTY *property, + UINT32 *count); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts_)( + IDWriteFontSet *This, + const WCHAR *family, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontSet *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 count, + IDWriteFontSet **fontset); + + END_INTERFACE +} IDWriteFontSetVtbl; + +interface IDWriteFontSet { + CONST_VTBL IDWriteFontSetVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSet_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSet_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSet_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSet methods ***/ +#define IDWriteFontSet_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +#define IDWriteFontSet_GetFontFaceReference(This,index,reference) (This)->lpVtbl->GetFontFaceReference(This,index,reference) +#define IDWriteFontSet_FindFontFaceReference(This,reference,index,exists) (This)->lpVtbl->FindFontFaceReference(This,reference,index,exists) +#define IDWriteFontSet_FindFontFace(This,fontface,index,exists) (This)->lpVtbl->FindFontFace(This,fontface,index,exists) +#define IDWriteFontSet_GetPropertyValues__(This,id,values) (This)->lpVtbl->GetPropertyValues__(This,id,values) +#define IDWriteFontSet_GetPropertyValues_(This,id,preferred_locales,values) (This)->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values) +#define IDWriteFontSet_GetPropertyValues(This,index,id,exists,values) (This)->lpVtbl->GetPropertyValues(This,index,id,exists,values) +#define IDWriteFontSet_GetPropertyOccurrenceCount(This,property,count) (This)->lpVtbl->GetPropertyOccurrenceCount(This,property,count) +#define IDWriteFontSet_GetMatchingFonts_(This,family,weight,stretch,style,fontset) (This)->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset) +#define IDWriteFontSet_GetMatchingFonts(This,props,count,fontset) (This)->lpVtbl->GetMatchingFonts(This,props,count,fontset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet_QueryInterface(IDWriteFontSet* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSet_AddRef(IDWriteFontSet* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSet_Release(IDWriteFontSet* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSet methods ***/ +static FORCEINLINE UINT32 IDWriteFontSet_GetFontCount(IDWriteFontSet* This) { + return This->lpVtbl->GetFontCount(This); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetFontFaceReference(IDWriteFontSet* This,UINT32 index,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,index,reference); +} +static FORCEINLINE HRESULT IDWriteFontSet_FindFontFaceReference(IDWriteFontSet* This,IDWriteFontFaceReference *reference,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFaceReference(This,reference,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet_FindFontFace(IDWriteFontSet* This,IDWriteFontFace *fontface,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFace(This,fontface,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetPropertyValues__(IDWriteFontSet* This,DWRITE_FONT_PROPERTY_ID id,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues__(This,id,values); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetPropertyValues_(IDWriteFontSet* This,DWRITE_FONT_PROPERTY_ID id,const WCHAR *preferred_locales,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetPropertyValues(IDWriteFontSet* This,UINT32 index,DWRITE_FONT_PROPERTY_ID id,WINBOOL *exists,IDWriteLocalizedStrings **values) { + return This->lpVtbl->GetPropertyValues(This,index,id,exists,values); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetPropertyOccurrenceCount(IDWriteFontSet* This,const DWRITE_FONT_PROPERTY *property,UINT32 *count) { + return This->lpVtbl->GetPropertyOccurrenceCount(This,property,count); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetMatchingFonts_(IDWriteFontSet* This,const WCHAR *family,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFontSet **fontset) { + return This->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet_GetMatchingFonts(IDWriteFontSet* This,const DWRITE_FONT_PROPERTY *props,UINT32 count,IDWriteFontSet **fontset) { + return This->lpVtbl->GetMatchingFonts(This,props,count,fontset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSet_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontResource interface + */ +#ifndef __IDWriteFontResource_INTERFACE_DEFINED__ +#define __IDWriteFontResource_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontResource, 0x1f803a76, 0x6871, 0x48e8, 0x98,0x7f, 0xb9,0x75,0x55,0x1c,0x50,0xf2); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("1f803a76-6871-48e8-987f-b975551c50f2") +IDWriteFontResource : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE GetFontFile( + IDWriteFontFile **fontfile) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontFaceIndex( + ) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontAxisCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDefaultFontAxisValues( + const DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisRanges( + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges) = 0; + + virtual DWRITE_FONT_AXIS_ATTRIBUTES STDMETHODCALLTYPE GetFontAxisAttributes( + UINT32 axis) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAxisNames( + UINT32 axis, + IDWriteLocalizedStrings **names) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetAxisValueNameCount( + UINT32 axis) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAxisValueNames( + UINT32 axis, + UINT32 axis_value, + DWRITE_FONT_AXIS_RANGE *axis_range, + IDWriteLocalizedStrings **names) = 0; + + virtual WINBOOL STDMETHODCALLTYPE HasVariations( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFace( + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontFace5 **fontface) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFaceReference( + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontFaceReference1 **reference) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontResource, 0x1f803a76, 0x6871, 0x48e8, 0x98,0x7f, 0xb9,0x75,0x55,0x1c,0x50,0xf2) +#endif +#else +typedef struct IDWriteFontResourceVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontResource *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontResource *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontResource *This); + + /*** IDWriteFontResource methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontFile)( + IDWriteFontResource *This, + IDWriteFontFile **fontfile); + + UINT32 (STDMETHODCALLTYPE *GetFontFaceIndex)( + IDWriteFontResource *This); + + UINT32 (STDMETHODCALLTYPE *GetFontAxisCount)( + IDWriteFontResource *This); + + HRESULT (STDMETHODCALLTYPE *GetDefaultFontAxisValues)( + IDWriteFontResource *This, + const DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges)( + IDWriteFontResource *This, + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges); + + DWRITE_FONT_AXIS_ATTRIBUTES (STDMETHODCALLTYPE *GetFontAxisAttributes)( + IDWriteFontResource *This, + UINT32 axis); + + HRESULT (STDMETHODCALLTYPE *GetAxisNames)( + IDWriteFontResource *This, + UINT32 axis, + IDWriteLocalizedStrings **names); + + UINT32 (STDMETHODCALLTYPE *GetAxisValueNameCount)( + IDWriteFontResource *This, + UINT32 axis); + + HRESULT (STDMETHODCALLTYPE *GetAxisValueNames)( + IDWriteFontResource *This, + UINT32 axis, + UINT32 axis_value, + DWRITE_FONT_AXIS_RANGE *axis_range, + IDWriteLocalizedStrings **names); + + WINBOOL (STDMETHODCALLTYPE *HasVariations)( + IDWriteFontResource *This); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontResource *This, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontFace5 **fontface); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFontResource *This, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontFaceReference1 **reference); + + END_INTERFACE +} IDWriteFontResourceVtbl; + +interface IDWriteFontResource { + CONST_VTBL IDWriteFontResourceVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontResource_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontResource_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontResource_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontResource methods ***/ +#define IDWriteFontResource_GetFontFile(This,fontfile) (This)->lpVtbl->GetFontFile(This,fontfile) +#define IDWriteFontResource_GetFontFaceIndex(This) (This)->lpVtbl->GetFontFaceIndex(This) +#define IDWriteFontResource_GetFontAxisCount(This) (This)->lpVtbl->GetFontAxisCount(This) +#define IDWriteFontResource_GetDefaultFontAxisValues(This,values,num_values) (This)->lpVtbl->GetDefaultFontAxisValues(This,values,num_values) +#define IDWriteFontResource_GetFontAxisRanges(This,ranges,num_ranges) (This)->lpVtbl->GetFontAxisRanges(This,ranges,num_ranges) +#define IDWriteFontResource_GetFontAxisAttributes(This,axis) (This)->lpVtbl->GetFontAxisAttributes(This,axis) +#define IDWriteFontResource_GetAxisNames(This,axis,names) (This)->lpVtbl->GetAxisNames(This,axis,names) +#define IDWriteFontResource_GetAxisValueNameCount(This,axis) (This)->lpVtbl->GetAxisValueNameCount(This,axis) +#define IDWriteFontResource_GetAxisValueNames(This,axis,axis_value,axis_range,names) (This)->lpVtbl->GetAxisValueNames(This,axis,axis_value,axis_range,names) +#define IDWriteFontResource_HasVariations(This) (This)->lpVtbl->HasVariations(This) +#define IDWriteFontResource_CreateFontFace(This,simulations,axis_values,num_values,fontface) (This)->lpVtbl->CreateFontFace(This,simulations,axis_values,num_values,fontface) +#define IDWriteFontResource_CreateFontFaceReference(This,simulations,axis_values,num_values,reference) (This)->lpVtbl->CreateFontFaceReference(This,simulations,axis_values,num_values,reference) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontResource_QueryInterface(IDWriteFontResource* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontResource_AddRef(IDWriteFontResource* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontResource_Release(IDWriteFontResource* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontResource methods ***/ +static FORCEINLINE HRESULT IDWriteFontResource_GetFontFile(IDWriteFontResource* This,IDWriteFontFile **fontfile) { + return This->lpVtbl->GetFontFile(This,fontfile); +} +static FORCEINLINE UINT32 IDWriteFontResource_GetFontFaceIndex(IDWriteFontResource* This) { + return This->lpVtbl->GetFontFaceIndex(This); +} +static FORCEINLINE UINT32 IDWriteFontResource_GetFontAxisCount(IDWriteFontResource* This) { + return This->lpVtbl->GetFontAxisCount(This); +} +static FORCEINLINE HRESULT IDWriteFontResource_GetDefaultFontAxisValues(IDWriteFontResource* This,const DWRITE_FONT_AXIS_VALUE *values,UINT32 num_values) { + return This->lpVtbl->GetDefaultFontAxisValues(This,values,num_values); +} +static FORCEINLINE HRESULT IDWriteFontResource_GetFontAxisRanges(IDWriteFontResource* This,const DWRITE_FONT_AXIS_RANGE *ranges,UINT32 num_ranges) { + return This->lpVtbl->GetFontAxisRanges(This,ranges,num_ranges); +} +static FORCEINLINE DWRITE_FONT_AXIS_ATTRIBUTES IDWriteFontResource_GetFontAxisAttributes(IDWriteFontResource* This,UINT32 axis) { + return This->lpVtbl->GetFontAxisAttributes(This,axis); +} +static FORCEINLINE HRESULT IDWriteFontResource_GetAxisNames(IDWriteFontResource* This,UINT32 axis,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetAxisNames(This,axis,names); +} +static FORCEINLINE UINT32 IDWriteFontResource_GetAxisValueNameCount(IDWriteFontResource* This,UINT32 axis) { + return This->lpVtbl->GetAxisValueNameCount(This,axis); +} +static FORCEINLINE HRESULT IDWriteFontResource_GetAxisValueNames(IDWriteFontResource* This,UINT32 axis,UINT32 axis_value,DWRITE_FONT_AXIS_RANGE *axis_range,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetAxisValueNames(This,axis,axis_value,axis_range,names); +} +static FORCEINLINE WINBOOL IDWriteFontResource_HasVariations(IDWriteFontResource* This) { + return This->lpVtbl->HasVariations(This); +} +static FORCEINLINE HRESULT IDWriteFontResource_CreateFontFace(IDWriteFontResource* This,DWRITE_FONT_SIMULATIONS simulations,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontFace5 **fontface) { + return This->lpVtbl->CreateFontFace(This,simulations,axis_values,num_values,fontface); +} +static FORCEINLINE HRESULT IDWriteFontResource_CreateFontFaceReference(IDWriteFontResource* This,DWRITE_FONT_SIMULATIONS simulations,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontFaceReference1 **reference) { + return This->lpVtbl->CreateFontFaceReference(This,simulations,axis_values,num_values,reference); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontResource_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSet1 interface + */ +#ifndef __IDWriteFontSet1_INTERFACE_DEFINED__ +#define __IDWriteFontSet1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSet1, 0x7e9fda85, 0x6c92, 0x4053, 0xbc,0x47, 0x7a,0xe3,0x53,0x0d,0xb4,0xd3); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("7e9fda85-6c92-4053-bc47-7ae3530db4d3") +IDWriteFontSet1 : public IDWriteFontSet +{ + virtual HRESULT STDMETHODCALLTYPE GetMatchingFonts( + const DWRITE_FONT_PROPERTY *property, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontSet1 **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFirstFontResources( + IDWriteFontSet1 **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFilteredFonts( + const UINT32 *indices, + UINT32 num_indices, + IDWriteFontSet1 **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFilteredFonts( + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + IDWriteFontSet1 **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFilteredFonts( + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_property, + IDWriteFontSet1 **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFilteredFontIndices( + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFilteredFontIndices( + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisRanges( + UINT32 font_index, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisRanges( + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + UINT32 index, + IDWriteFontFaceReference1 **reference) = 0; + + using IDWriteFontSet::GetFontFaceReference; + + virtual HRESULT STDMETHODCALLTYPE CreateFontResource( + UINT32 index, + IDWriteFontResource **resource) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFace( + UINT32 index, + IDWriteFontFace5 **fontface) = 0; + + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetFontLocality( + UINT32 index) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSet1, 0x7e9fda85, 0x6c92, 0x4053, 0xbc,0x47, 0x7a,0xe3,0x53,0x0d,0xb4,0xd3) +#endif +#else +typedef struct IDWriteFontSet1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSet1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSet1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSet1 *This); + + /*** IDWriteFontSet methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontSet1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontSet1 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *FindFontFaceReference)( + IDWriteFontSet1 *This, + IDWriteFontFaceReference *reference, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *FindFontFace)( + IDWriteFontSet1 *This, + IDWriteFontFace *fontface, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues__)( + IDWriteFontSet1 *This, + DWRITE_FONT_PROPERTY_ID id, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues_)( + IDWriteFontSet1 *This, + DWRITE_FONT_PROPERTY_ID id, + const WCHAR *preferred_locales, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues)( + IDWriteFontSet1 *This, + UINT32 index, + DWRITE_FONT_PROPERTY_ID id, + WINBOOL *exists, + IDWriteLocalizedStrings **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyOccurrenceCount)( + IDWriteFontSet1 *This, + const DWRITE_FONT_PROPERTY *property, + UINT32 *count); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts_)( + IDWriteFontSet1 *This, + const WCHAR *family, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontSet1 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 count, + IDWriteFontSet **fontset); + + /*** IDWriteFontSet1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetMatchingFonts)( + IDWriteFontSet1 *This, + const DWRITE_FONT_PROPERTY *property, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFirstFontResources)( + IDWriteFontSet1 *This, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts__)( + IDWriteFontSet1 *This, + const UINT32 *indices, + UINT32 num_indices, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts_)( + IDWriteFontSet1 *This, + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts)( + IDWriteFontSet1 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_property, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices_)( + IDWriteFontSet1 *This, + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices)( + IDWriteFontSet1 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges_)( + IDWriteFontSet1 *This, + UINT32 font_index, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges)( + IDWriteFontSet1 *This, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetFontFaceReference)( + IDWriteFontSet1 *This, + UINT32 index, + IDWriteFontFaceReference1 **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontResource)( + IDWriteFontSet1 *This, + UINT32 index, + IDWriteFontResource **resource); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontSet1 *This, + UINT32 index, + IDWriteFontFace5 **fontface); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontSet1 *This, + UINT32 index); + + END_INTERFACE +} IDWriteFontSet1Vtbl; + +interface IDWriteFontSet1 { + CONST_VTBL IDWriteFontSet1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSet1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSet1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSet1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSet methods ***/ +#define IDWriteFontSet1_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +#define IDWriteFontSet1_FindFontFaceReference(This,reference,index,exists) (This)->lpVtbl->FindFontFaceReference(This,reference,index,exists) +#define IDWriteFontSet1_FindFontFace(This,fontface,index,exists) (This)->lpVtbl->FindFontFace(This,fontface,index,exists) +#define IDWriteFontSet1_GetPropertyValues__(This,id,values) (This)->lpVtbl->GetPropertyValues__(This,id,values) +#define IDWriteFontSet1_GetPropertyValues_(This,id,preferred_locales,values) (This)->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values) +#define IDWriteFontSet1_GetPropertyValues(This,index,id,exists,values) (This)->lpVtbl->GetPropertyValues(This,index,id,exists,values) +#define IDWriteFontSet1_GetPropertyOccurrenceCount(This,property,count) (This)->lpVtbl->GetPropertyOccurrenceCount(This,property,count) +#define IDWriteFontSet1_GetMatchingFonts_(This,family,weight,stretch,style,fontset) (This)->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset) +/*** IDWriteFontSet1 methods ***/ +#define IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset) (This)->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset) +#define IDWriteFontSet1_GetFirstFontResources(This,fontset) (This)->lpVtbl->GetFirstFontResources(This,fontset) +#define IDWriteFontSet1_GetFilteredFonts__(This,indices,num_indices,fontset) (This)->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset) +#define IDWriteFontSet1_GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) (This)->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) +#define IDWriteFontSet1_GetFilteredFonts(This,props,num_properties,select_any_property,fontset) (This)->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset) +#define IDWriteFontSet1_GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet1_GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet1_GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet1_GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet1_GetFontFaceReference(This,index,reference) (This)->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference) +#define IDWriteFontSet1_CreateFontResource(This,index,resource) (This)->lpVtbl->CreateFontResource(This,index,resource) +#define IDWriteFontSet1_CreateFontFace(This,index,fontface) (This)->lpVtbl->CreateFontFace(This,index,fontface) +#define IDWriteFontSet1_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet1_QueryInterface(IDWriteFontSet1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSet1_AddRef(IDWriteFontSet1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSet1_Release(IDWriteFontSet1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSet methods ***/ +static FORCEINLINE UINT32 IDWriteFontSet1_GetFontCount(IDWriteFontSet1* This) { + return This->lpVtbl->GetFontCount(This); +} +static FORCEINLINE HRESULT IDWriteFontSet1_FindFontFaceReference(IDWriteFontSet1* This,IDWriteFontFaceReference *reference,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFaceReference(This,reference,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet1_FindFontFace(IDWriteFontSet1* This,IDWriteFontFace *fontface,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFace(This,fontface,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetPropertyValues__(IDWriteFontSet1* This,DWRITE_FONT_PROPERTY_ID id,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues__(This,id,values); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetPropertyValues_(IDWriteFontSet1* This,DWRITE_FONT_PROPERTY_ID id,const WCHAR *preferred_locales,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetPropertyValues(IDWriteFontSet1* This,UINT32 index,DWRITE_FONT_PROPERTY_ID id,WINBOOL *exists,IDWriteLocalizedStrings **values) { + return This->lpVtbl->GetPropertyValues(This,index,id,exists,values); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetPropertyOccurrenceCount(IDWriteFontSet1* This,const DWRITE_FONT_PROPERTY *property,UINT32 *count) { + return This->lpVtbl->GetPropertyOccurrenceCount(This,property,count); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetMatchingFonts_(IDWriteFontSet1* This,const WCHAR *family,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFontSet **fontset) { + return This->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset); +} +/*** IDWriteFontSet1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet1_GetMatchingFonts(IDWriteFontSet1* This,const DWRITE_FONT_PROPERTY *property,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFirstFontResources(IDWriteFontSet1* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFirstFontResources(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFilteredFonts__(IDWriteFontSet1* This,const UINT32 *indices,UINT32 num_indices,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFilteredFonts_(IDWriteFontSet1* This,const DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,WINBOOL select_any_range,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFilteredFonts(IDWriteFontSet1* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_property,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFilteredFontIndices_(IDWriteFontSet1* This,const DWRITE_FONT_AXIS_RANGE *ranges,UINT32 num_ranges,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFilteredFontIndices(IDWriteFontSet1* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFontAxisRanges_(IDWriteFontSet1* This,UINT32 font_index,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFontAxisRanges(IDWriteFontSet1* This,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet1_GetFontFaceReference(IDWriteFontSet1* This,UINT32 index,IDWriteFontFaceReference1 **reference) { + return This->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference); +} +static FORCEINLINE HRESULT IDWriteFontSet1_CreateFontResource(IDWriteFontSet1* This,UINT32 index,IDWriteFontResource **resource) { + return This->lpVtbl->CreateFontResource(This,index,resource); +} +static FORCEINLINE HRESULT IDWriteFontSet1_CreateFontFace(IDWriteFontSet1* This,UINT32 index,IDWriteFontFace5 **fontface) { + return This->lpVtbl->CreateFontFace(This,index,fontface); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFontSet1_GetFontLocality(IDWriteFontSet1* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSet1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFont3 interface + */ +#ifndef __IDWriteFont3_INTERFACE_DEFINED__ +#define __IDWriteFont3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFont3, 0x29748ed6, 0x8c9c, 0x4a6a, 0xbe,0x0b, 0xd9,0x12,0xe8,0x53,0x89,0x44); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("29748ed6-8c9c-4a6a-be0b-d912e8538944") +IDWriteFont3 : public IDWriteFont2 +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontFace( + IDWriteFontFace3 **fontface) = 0; + + using IDWriteFont::CreateFontFace; + + virtual WINBOOL STDMETHODCALLTYPE Equals( + IDWriteFont *font) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + IDWriteFontFaceReference **reference) = 0; + + virtual WINBOOL STDMETHODCALLTYPE HasCharacter( + UINT32 character) = 0; + + using IDWriteFont::HasCharacter; + + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetLocality( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFont3, 0x29748ed6, 0x8c9c, 0x4a6a, 0xbe,0x0b, 0xd9,0x12,0xe8,0x53,0x89,0x44) +#endif +#else +typedef struct IDWriteFont3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFont3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFont3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFont3 *This); + + /*** IDWriteFont methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontFamily)( + IDWriteFont3 *This, + IDWriteFontFamily **family); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetWeight)( + IDWriteFont3 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetStretch)( + IDWriteFont3 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetStyle)( + IDWriteFont3 *This); + + WINBOOL (STDMETHODCALLTYPE *IsSymbolFont)( + IDWriteFont3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFaceNames)( + IDWriteFont3 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetInformationalStrings)( + IDWriteFont3 *This, + DWRITE_INFORMATIONAL_STRING_ID stringid, + IDWriteLocalizedStrings **strings, + WINBOOL *exists); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFont3 *This); + + void (STDMETHODCALLTYPE *GetMetrics)( + IDWriteFont3 *This, + DWRITE_FONT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *HasCharacter)( + IDWriteFont3 *This, + UINT32 value, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFont3 *This, + IDWriteFontFace **face); + + /*** IDWriteFont1 methods ***/ + void (STDMETHODCALLTYPE *IDWriteFont1_GetMetrics)( + IDWriteFont3 *This, + DWRITE_FONT_METRICS1 *metrics); + + void (STDMETHODCALLTYPE *GetPanose)( + IDWriteFont3 *This, + DWRITE_PANOSE *panose); + + HRESULT (STDMETHODCALLTYPE *GetUnicodeRanges)( + IDWriteFont3 *This, + UINT32 max_count, + DWRITE_UNICODE_RANGE *ranges, + UINT32 *count); + + WINBOOL (STDMETHODCALLTYPE *IsMonospacedFont)( + IDWriteFont3 *This); + + /*** IDWriteFont2 methods ***/ + WINBOOL (STDMETHODCALLTYPE *IsColorFont)( + IDWriteFont3 *This); + + /*** IDWriteFont3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFont3_CreateFontFace)( + IDWriteFont3 *This, + IDWriteFontFace3 **fontface); + + WINBOOL (STDMETHODCALLTYPE *Equals)( + IDWriteFont3 *This, + IDWriteFont *font); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFont3 *This, + IDWriteFontFaceReference **reference); + + WINBOOL (STDMETHODCALLTYPE *IDWriteFont3_HasCharacter)( + IDWriteFont3 *This, + UINT32 character); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetLocality)( + IDWriteFont3 *This); + + END_INTERFACE +} IDWriteFont3Vtbl; + +interface IDWriteFont3 { + CONST_VTBL IDWriteFont3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFont3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFont3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFont3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFont methods ***/ +#define IDWriteFont3_GetFontFamily(This,family) (This)->lpVtbl->GetFontFamily(This,family) +#define IDWriteFont3_GetWeight(This) (This)->lpVtbl->GetWeight(This) +#define IDWriteFont3_GetStretch(This) (This)->lpVtbl->GetStretch(This) +#define IDWriteFont3_GetStyle(This) (This)->lpVtbl->GetStyle(This) +#define IDWriteFont3_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This) +#define IDWriteFont3_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names) +#define IDWriteFont3_GetInformationalStrings(This,stringid,strings,exists) (This)->lpVtbl->GetInformationalStrings(This,stringid,strings,exists) +#define IDWriteFont3_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +/*** IDWriteFont1 methods ***/ +#define IDWriteFont3_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteFont1_GetMetrics(This,metrics) +#define IDWriteFont3_GetPanose(This,panose) (This)->lpVtbl->GetPanose(This,panose) +#define IDWriteFont3_GetUnicodeRanges(This,max_count,ranges,count) (This)->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count) +#define IDWriteFont3_IsMonospacedFont(This) (This)->lpVtbl->IsMonospacedFont(This) +/*** IDWriteFont2 methods ***/ +#define IDWriteFont3_IsColorFont(This) (This)->lpVtbl->IsColorFont(This) +/*** IDWriteFont3 methods ***/ +#define IDWriteFont3_CreateFontFace(This,fontface) (This)->lpVtbl->IDWriteFont3_CreateFontFace(This,fontface) +#define IDWriteFont3_Equals(This,font) (This)->lpVtbl->Equals(This,font) +#define IDWriteFont3_GetFontFaceReference(This,reference) (This)->lpVtbl->GetFontFaceReference(This,reference) +#define IDWriteFont3_HasCharacter(This,character) (This)->lpVtbl->IDWriteFont3_HasCharacter(This,character) +#define IDWriteFont3_GetLocality(This) (This)->lpVtbl->GetLocality(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFont3_QueryInterface(IDWriteFont3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFont3_AddRef(IDWriteFont3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFont3_Release(IDWriteFont3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFont methods ***/ +static FORCEINLINE HRESULT IDWriteFont3_GetFontFamily(IDWriteFont3* This,IDWriteFontFamily **family) { + return This->lpVtbl->GetFontFamily(This,family); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteFont3_GetWeight(IDWriteFont3* This) { + return This->lpVtbl->GetWeight(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteFont3_GetStretch(IDWriteFont3* This) { + return This->lpVtbl->GetStretch(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteFont3_GetStyle(IDWriteFont3* This) { + return This->lpVtbl->GetStyle(This); +} +static FORCEINLINE WINBOOL IDWriteFont3_IsSymbolFont(IDWriteFont3* This) { + return This->lpVtbl->IsSymbolFont(This); +} +static FORCEINLINE HRESULT IDWriteFont3_GetFaceNames(IDWriteFont3* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFaceNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFont3_GetInformationalStrings(IDWriteFont3* This,DWRITE_INFORMATIONAL_STRING_ID stringid,IDWriteLocalizedStrings **strings,WINBOOL *exists) { + return This->lpVtbl->GetInformationalStrings(This,stringid,strings,exists); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFont3_GetSimulations(IDWriteFont3* This) { + return This->lpVtbl->GetSimulations(This); +} +/*** IDWriteFont1 methods ***/ +static FORCEINLINE void IDWriteFont3_GetMetrics(IDWriteFont3* This,DWRITE_FONT_METRICS1 *metrics) { + This->lpVtbl->IDWriteFont1_GetMetrics(This,metrics); +} +static FORCEINLINE void IDWriteFont3_GetPanose(IDWriteFont3* This,DWRITE_PANOSE *panose) { + This->lpVtbl->GetPanose(This,panose); +} +static FORCEINLINE HRESULT IDWriteFont3_GetUnicodeRanges(IDWriteFont3* This,UINT32 max_count,DWRITE_UNICODE_RANGE *ranges,UINT32 *count) { + return This->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count); +} +static FORCEINLINE WINBOOL IDWriteFont3_IsMonospacedFont(IDWriteFont3* This) { + return This->lpVtbl->IsMonospacedFont(This); +} +/*** IDWriteFont2 methods ***/ +static FORCEINLINE WINBOOL IDWriteFont3_IsColorFont(IDWriteFont3* This) { + return This->lpVtbl->IsColorFont(This); +} +/*** IDWriteFont3 methods ***/ +static FORCEINLINE HRESULT IDWriteFont3_CreateFontFace(IDWriteFont3* This,IDWriteFontFace3 **fontface) { + return This->lpVtbl->IDWriteFont3_CreateFontFace(This,fontface); +} +static FORCEINLINE WINBOOL IDWriteFont3_Equals(IDWriteFont3* This,IDWriteFont *font) { + return This->lpVtbl->Equals(This,font); +} +static FORCEINLINE HRESULT IDWriteFont3_GetFontFaceReference(IDWriteFont3* This,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,reference); +} +static FORCEINLINE WINBOOL IDWriteFont3_HasCharacter(IDWriteFont3* This,UINT32 character) { + return This->lpVtbl->IDWriteFont3_HasCharacter(This,character); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFont3_GetLocality(IDWriteFont3* This) { + return This->lpVtbl->GetLocality(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFont3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFamily1 interface + */ +#ifndef __IDWriteFontFamily1_INTERFACE_DEFINED__ +#define __IDWriteFontFamily1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFamily1, 0xda20d8ef, 0x812a, 0x4c43, 0x98,0x02, 0x62,0xec,0x4a,0xbd,0x7a,0xdf); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("da20d8ef-812a-4c43-9802-62ec4abd7adf") +IDWriteFontFamily1 : public IDWriteFontFamily +{ + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetFontLocality( + UINT32 index) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFont( + UINT32 index, + IDWriteFont3 **font) = 0; + + using IDWriteFontFamily::GetFont; + + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + UINT32 index, + IDWriteFontFaceReference **reference) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFamily1, 0xda20d8ef, 0x812a, 0x4c43, 0x98,0x02, 0x62,0xec,0x4a,0xbd,0x7a,0xdf) +#endif +#else +typedef struct IDWriteFontFamily1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFamily1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFamily1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFamily1 *This); + + /*** IDWriteFontList methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteFontFamily1 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontFamily1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFont)( + IDWriteFontFamily1 *This, + UINT32 index, + IDWriteFont **font); + + /*** IDWriteFontFamily methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFamilyNames)( + IDWriteFontFamily1 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetFirstMatchingFont)( + IDWriteFontFamily1 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFont **font); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontFamily1 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontList **fonts); + + /*** IDWriteFontFamily1 methods ***/ + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontFamily1 *This, + UINT32 index); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFamily1_GetFont)( + IDWriteFontFamily1 *This, + UINT32 index, + IDWriteFont3 **font); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontFamily1 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + END_INTERFACE +} IDWriteFontFamily1Vtbl; + +interface IDWriteFontFamily1 { + CONST_VTBL IDWriteFontFamily1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFamily1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFamily1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFamily1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontList methods ***/ +#define IDWriteFontFamily1_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteFontFamily1_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +/*** IDWriteFontFamily methods ***/ +#define IDWriteFontFamily1_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) +#define IDWriteFontFamily1_GetFirstMatchingFont(This,weight,stretch,style,font) (This)->lpVtbl->GetFirstMatchingFont(This,weight,stretch,style,font) +#define IDWriteFontFamily1_GetMatchingFonts(This,weight,stretch,style,fonts) (This)->lpVtbl->GetMatchingFonts(This,weight,stretch,style,fonts) +/*** IDWriteFontFamily1 methods ***/ +#define IDWriteFontFamily1_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +#define IDWriteFontFamily1_GetFont(This,index,font) (This)->lpVtbl->IDWriteFontFamily1_GetFont(This,index,font) +#define IDWriteFontFamily1_GetFontFaceReference(This,index,reference) (This)->lpVtbl->GetFontFaceReference(This,index,reference) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily1_QueryInterface(IDWriteFontFamily1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFamily1_AddRef(IDWriteFontFamily1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFamily1_Release(IDWriteFontFamily1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontList methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily1_GetFontCollection(IDWriteFontFamily1* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteFontFamily1_GetFontCount(IDWriteFontFamily1* This) { + return This->lpVtbl->GetFontCount(This); +} +/*** IDWriteFontFamily methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily1_GetFamilyNames(IDWriteFontFamily1* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFamilyNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFamily1_GetFirstMatchingFont(IDWriteFontFamily1* This,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFont **font) { + return This->lpVtbl->GetFirstMatchingFont(This,weight,stretch,style,font); +} +static FORCEINLINE HRESULT IDWriteFontFamily1_GetMatchingFonts(IDWriteFontFamily1* This,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFontList **fonts) { + return This->lpVtbl->GetMatchingFonts(This,weight,stretch,style,fonts); +} +/*** IDWriteFontFamily1 methods ***/ +static FORCEINLINE DWRITE_LOCALITY IDWriteFontFamily1_GetFontLocality(IDWriteFontFamily1* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +static FORCEINLINE HRESULT IDWriteFontFamily1_GetFont(IDWriteFontFamily1* This,UINT32 index,IDWriteFont3 **font) { + return This->lpVtbl->IDWriteFontFamily1_GetFont(This,index,font); +} +static FORCEINLINE HRESULT IDWriteFontFamily1_GetFontFaceReference(IDWriteFontFamily1* This,UINT32 index,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,index,reference); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFamily1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFamily2 interface + */ +#ifndef __IDWriteFontFamily2_INTERFACE_DEFINED__ +#define __IDWriteFontFamily2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFamily2, 0x3ed49e77, 0xa398, 0x4261, 0xb9,0xcf, 0xc1,0x26,0xc2,0x13,0x1e,0xf3); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("3ed49e77-a398-4261-b9cf-c126c2131ef3") +IDWriteFontFamily2 : public IDWriteFontFamily1 +{ + virtual HRESULT STDMETHODCALLTYPE GetMatchingFonts( + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontList2 **fontlist) = 0; + + using IDWriteFontFamily::GetMatchingFonts; + + virtual HRESULT STDMETHODCALLTYPE GetFontSet( + IDWriteFontSet1 **fontset) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFamily2, 0x3ed49e77, 0xa398, 0x4261, 0xb9,0xcf, 0xc1,0x26,0xc2,0x13,0x1e,0xf3) +#endif +#else +typedef struct IDWriteFontFamily2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFamily2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFamily2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFamily2 *This); + + /*** IDWriteFontList methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteFontFamily2 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontFamily2 *This); + + HRESULT (STDMETHODCALLTYPE *GetFont)( + IDWriteFontFamily2 *This, + UINT32 index, + IDWriteFont **font); + + /*** IDWriteFontFamily methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFamilyNames)( + IDWriteFontFamily2 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetFirstMatchingFont)( + IDWriteFontFamily2 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFont **font); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontFamily2 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontList **fonts); + + /*** IDWriteFontFamily1 methods ***/ + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontFamily2 *This, + UINT32 index); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFamily1_GetFont)( + IDWriteFontFamily2 *This, + UINT32 index, + IDWriteFont3 **font); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontFamily2 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + /*** IDWriteFontFamily2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontFamily2_GetMatchingFonts)( + IDWriteFontFamily2 *This, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontList2 **fontlist); + + HRESULT (STDMETHODCALLTYPE *GetFontSet)( + IDWriteFontFamily2 *This, + IDWriteFontSet1 **fontset); + + END_INTERFACE +} IDWriteFontFamily2Vtbl; + +interface IDWriteFontFamily2 { + CONST_VTBL IDWriteFontFamily2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFamily2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFamily2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFamily2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontList methods ***/ +#define IDWriteFontFamily2_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteFontFamily2_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +/*** IDWriteFontFamily methods ***/ +#define IDWriteFontFamily2_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) +#define IDWriteFontFamily2_GetFirstMatchingFont(This,weight,stretch,style,font) (This)->lpVtbl->GetFirstMatchingFont(This,weight,stretch,style,font) +/*** IDWriteFontFamily1 methods ***/ +#define IDWriteFontFamily2_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +#define IDWriteFontFamily2_GetFont(This,index,font) (This)->lpVtbl->IDWriteFontFamily1_GetFont(This,index,font) +#define IDWriteFontFamily2_GetFontFaceReference(This,index,reference) (This)->lpVtbl->GetFontFaceReference(This,index,reference) +/*** IDWriteFontFamily2 methods ***/ +#define IDWriteFontFamily2_GetMatchingFonts(This,axis_values,num_values,fontlist) (This)->lpVtbl->IDWriteFontFamily2_GetMatchingFonts(This,axis_values,num_values,fontlist) +#define IDWriteFontFamily2_GetFontSet(This,fontset) (This)->lpVtbl->GetFontSet(This,fontset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily2_QueryInterface(IDWriteFontFamily2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFamily2_AddRef(IDWriteFontFamily2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFamily2_Release(IDWriteFontFamily2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontList methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFontCollection(IDWriteFontFamily2* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteFontFamily2_GetFontCount(IDWriteFontFamily2* This) { + return This->lpVtbl->GetFontCount(This); +} +/*** IDWriteFontFamily methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFamilyNames(IDWriteFontFamily2* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFamilyNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFirstMatchingFont(IDWriteFontFamily2* This,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFont **font) { + return This->lpVtbl->GetFirstMatchingFont(This,weight,stretch,style,font); +} +/*** IDWriteFontFamily1 methods ***/ +static FORCEINLINE DWRITE_LOCALITY IDWriteFontFamily2_GetFontLocality(IDWriteFontFamily2* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFont(IDWriteFontFamily2* This,UINT32 index,IDWriteFont3 **font) { + return This->lpVtbl->IDWriteFontFamily1_GetFont(This,index,font); +} +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFontFaceReference(IDWriteFontFamily2* This,UINT32 index,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,index,reference); +} +/*** IDWriteFontFamily2 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFamily2_GetMatchingFonts(IDWriteFontFamily2* This,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontList2 **fontlist) { + return This->lpVtbl->IDWriteFontFamily2_GetMatchingFonts(This,axis_values,num_values,fontlist); +} +static FORCEINLINE HRESULT IDWriteFontFamily2_GetFontSet(IDWriteFontFamily2* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFontSet(This,fontset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFamily2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontCollection1 interface + */ +#ifndef __IDWriteFontCollection1_INTERFACE_DEFINED__ +#define __IDWriteFontCollection1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontCollection1, 0x53585141, 0xd9f8, 0x4095, 0x83,0x21, 0xd7,0x3c,0xf6,0xbd,0x11,0x6c); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("53585141-d9f8-4095-8321-d73cf6bd116c") +IDWriteFontCollection1 : public IDWriteFontCollection +{ + virtual HRESULT STDMETHODCALLTYPE GetFontSet( + IDWriteFontSet **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontFamily( + UINT32 index, + IDWriteFontFamily1 **family) = 0; + + using IDWriteFontCollection::GetFontFamily; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontCollection1, 0x53585141, 0xd9f8, 0x4095, 0x83,0x21, 0xd7,0x3c,0xf6,0xbd,0x11,0x6c) +#endif +#else +typedef struct IDWriteFontCollection1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontCollection1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontCollection1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontCollection1 *This); + + /*** IDWriteFontCollection methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontFamilyCount)( + IDWriteFontCollection1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamily)( + IDWriteFontCollection1 *This, + UINT32 index, + IDWriteFontFamily **family); + + HRESULT (STDMETHODCALLTYPE *FindFamilyName)( + IDWriteFontCollection1 *This, + const WCHAR *name, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetFontFromFontFace)( + IDWriteFontCollection1 *This, + IDWriteFontFace *face, + IDWriteFont **font); + + /*** IDWriteFontCollection1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontSet)( + IDWriteFontCollection1 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection1_GetFontFamily)( + IDWriteFontCollection1 *This, + UINT32 index, + IDWriteFontFamily1 **family); + + END_INTERFACE +} IDWriteFontCollection1Vtbl; + +interface IDWriteFontCollection1 { + CONST_VTBL IDWriteFontCollection1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontCollection1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontCollection1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontCollection1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontCollection methods ***/ +#define IDWriteFontCollection1_GetFontFamilyCount(This) (This)->lpVtbl->GetFontFamilyCount(This) +#define IDWriteFontCollection1_FindFamilyName(This,name,index,exists) (This)->lpVtbl->FindFamilyName(This,name,index,exists) +#define IDWriteFontCollection1_GetFontFromFontFace(This,face,font) (This)->lpVtbl->GetFontFromFontFace(This,face,font) +/*** IDWriteFontCollection1 methods ***/ +#define IDWriteFontCollection1_GetFontSet(This,fontset) (This)->lpVtbl->GetFontSet(This,fontset) +#define IDWriteFontCollection1_GetFontFamily(This,index,family) (This)->lpVtbl->IDWriteFontCollection1_GetFontFamily(This,index,family) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection1_QueryInterface(IDWriteFontCollection1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontCollection1_AddRef(IDWriteFontCollection1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontCollection1_Release(IDWriteFontCollection1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontCollection methods ***/ +static FORCEINLINE UINT32 IDWriteFontCollection1_GetFontFamilyCount(IDWriteFontCollection1* This) { + return This->lpVtbl->GetFontFamilyCount(This); +} +static FORCEINLINE HRESULT IDWriteFontCollection1_FindFamilyName(IDWriteFontCollection1* This,const WCHAR *name,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFamilyName(This,name,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontCollection1_GetFontFromFontFace(IDWriteFontCollection1* This,IDWriteFontFace *face,IDWriteFont **font) { + return This->lpVtbl->GetFontFromFontFace(This,face,font); +} +/*** IDWriteFontCollection1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection1_GetFontSet(IDWriteFontCollection1* This,IDWriteFontSet **fontset) { + return This->lpVtbl->GetFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontCollection1_GetFontFamily(IDWriteFontCollection1* This,UINT32 index,IDWriteFontFamily1 **family) { + return This->lpVtbl->IDWriteFontCollection1_GetFontFamily(This,index,family); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontCollection1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontCollection2 interface + */ +#ifndef __IDWriteFontCollection2_INTERFACE_DEFINED__ +#define __IDWriteFontCollection2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontCollection2, 0x514039c6, 0x4617, 0x4064, 0xbf,0x8b, 0x92,0xea,0x83,0xe5,0x06,0xe0); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("514039c6-4617-4064-bf8b-92ea83e506e0") +IDWriteFontCollection2 : public IDWriteFontCollection1 +{ + virtual HRESULT STDMETHODCALLTYPE GetFontFamily( + UINT32 index, + IDWriteFontFamily2 **family) = 0; + + using IDWriteFontCollection::GetFontFamily; + using IDWriteFontCollection1::GetFontFamily; + + virtual HRESULT STDMETHODCALLTYPE GetMatchingFonts( + const WCHAR *familyname, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontList2 **fontlist) = 0; + + virtual DWRITE_FONT_FAMILY_MODEL STDMETHODCALLTYPE GetFontFamilyModel( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontSet( + IDWriteFontSet1 **fontset) = 0; + + using IDWriteFontCollection1::GetFontSet; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontCollection2, 0x514039c6, 0x4617, 0x4064, 0xbf,0x8b, 0x92,0xea,0x83,0xe5,0x06,0xe0) +#endif +#else +typedef struct IDWriteFontCollection2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontCollection2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontCollection2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontCollection2 *This); + + /*** IDWriteFontCollection methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontFamilyCount)( + IDWriteFontCollection2 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamily)( + IDWriteFontCollection2 *This, + UINT32 index, + IDWriteFontFamily **family); + + HRESULT (STDMETHODCALLTYPE *FindFamilyName)( + IDWriteFontCollection2 *This, + const WCHAR *name, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetFontFromFontFace)( + IDWriteFontCollection2 *This, + IDWriteFontFace *face, + IDWriteFont **font); + + /*** IDWriteFontCollection1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontSet)( + IDWriteFontCollection2 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection1_GetFontFamily)( + IDWriteFontCollection2 *This, + UINT32 index, + IDWriteFontFamily1 **family); + + /*** IDWriteFontCollection2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection2_GetFontFamily)( + IDWriteFontCollection2 *This, + UINT32 index, + IDWriteFontFamily2 **family); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontCollection2 *This, + const WCHAR *familyname, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontList2 **fontlist); + + DWRITE_FONT_FAMILY_MODEL (STDMETHODCALLTYPE *GetFontFamilyModel)( + IDWriteFontCollection2 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection2_GetFontSet)( + IDWriteFontCollection2 *This, + IDWriteFontSet1 **fontset); + + END_INTERFACE +} IDWriteFontCollection2Vtbl; + +interface IDWriteFontCollection2 { + CONST_VTBL IDWriteFontCollection2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontCollection2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontCollection2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontCollection2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontCollection methods ***/ +#define IDWriteFontCollection2_GetFontFamilyCount(This) (This)->lpVtbl->GetFontFamilyCount(This) +#define IDWriteFontCollection2_FindFamilyName(This,name,index,exists) (This)->lpVtbl->FindFamilyName(This,name,index,exists) +#define IDWriteFontCollection2_GetFontFromFontFace(This,face,font) (This)->lpVtbl->GetFontFromFontFace(This,face,font) +/*** IDWriteFontCollection1 methods ***/ +/*** IDWriteFontCollection2 methods ***/ +#define IDWriteFontCollection2_GetFontFamily(This,index,family) (This)->lpVtbl->IDWriteFontCollection2_GetFontFamily(This,index,family) +#define IDWriteFontCollection2_GetMatchingFonts(This,familyname,axis_values,num_values,fontlist) (This)->lpVtbl->GetMatchingFonts(This,familyname,axis_values,num_values,fontlist) +#define IDWriteFontCollection2_GetFontFamilyModel(This) (This)->lpVtbl->GetFontFamilyModel(This) +#define IDWriteFontCollection2_GetFontSet(This,fontset) (This)->lpVtbl->IDWriteFontCollection2_GetFontSet(This,fontset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection2_QueryInterface(IDWriteFontCollection2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontCollection2_AddRef(IDWriteFontCollection2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontCollection2_Release(IDWriteFontCollection2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontCollection methods ***/ +static FORCEINLINE UINT32 IDWriteFontCollection2_GetFontFamilyCount(IDWriteFontCollection2* This) { + return This->lpVtbl->GetFontFamilyCount(This); +} +static FORCEINLINE HRESULT IDWriteFontCollection2_FindFamilyName(IDWriteFontCollection2* This,const WCHAR *name,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFamilyName(This,name,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontCollection2_GetFontFromFontFace(IDWriteFontCollection2* This,IDWriteFontFace *face,IDWriteFont **font) { + return This->lpVtbl->GetFontFromFontFace(This,face,font); +} +/*** IDWriteFontCollection1 methods ***/ +/*** IDWriteFontCollection2 methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection2_GetFontFamily(IDWriteFontCollection2* This,UINT32 index,IDWriteFontFamily2 **family) { + return This->lpVtbl->IDWriteFontCollection2_GetFontFamily(This,index,family); +} +static FORCEINLINE HRESULT IDWriteFontCollection2_GetMatchingFonts(IDWriteFontCollection2* This,const WCHAR *familyname,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontList2 **fontlist) { + return This->lpVtbl->GetMatchingFonts(This,familyname,axis_values,num_values,fontlist); +} +static FORCEINLINE DWRITE_FONT_FAMILY_MODEL IDWriteFontCollection2_GetFontFamilyModel(IDWriteFontCollection2* This) { + return This->lpVtbl->GetFontFamilyModel(This); +} +static FORCEINLINE HRESULT IDWriteFontCollection2_GetFontSet(IDWriteFontCollection2* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFontCollection2_GetFontSet(This,fontset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontCollection2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontCollection3 interface + */ +#ifndef __IDWriteFontCollection3_INTERFACE_DEFINED__ +#define __IDWriteFontCollection3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontCollection3, 0xa4d055a6, 0xf9e3, 0x4e25, 0x93,0xb7, 0x9e,0x30,0x9f,0x3a,0xf8,0xe9); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("a4d055a6-f9e3-4e25-93b7-9e309f3af8e9") +IDWriteFontCollection3 : public IDWriteFontCollection2 +{ + virtual HANDLE STDMETHODCALLTYPE GetExpirationEvent( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontCollection3, 0xa4d055a6, 0xf9e3, 0x4e25, 0x93,0xb7, 0x9e,0x30,0x9f,0x3a,0xf8,0xe9) +#endif +#else +typedef struct IDWriteFontCollection3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontCollection3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontCollection3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontCollection3 *This); + + /*** IDWriteFontCollection methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontFamilyCount)( + IDWriteFontCollection3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamily)( + IDWriteFontCollection3 *This, + UINT32 index, + IDWriteFontFamily **family); + + HRESULT (STDMETHODCALLTYPE *FindFamilyName)( + IDWriteFontCollection3 *This, + const WCHAR *name, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetFontFromFontFace)( + IDWriteFontCollection3 *This, + IDWriteFontFace *face, + IDWriteFont **font); + + /*** IDWriteFontCollection1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontSet)( + IDWriteFontCollection3 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection1_GetFontFamily)( + IDWriteFontCollection3 *This, + UINT32 index, + IDWriteFontFamily1 **family); + + /*** IDWriteFontCollection2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection2_GetFontFamily)( + IDWriteFontCollection3 *This, + UINT32 index, + IDWriteFontFamily2 **family); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontCollection3 *This, + const WCHAR *familyname, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontList2 **fontlist); + + DWRITE_FONT_FAMILY_MODEL (STDMETHODCALLTYPE *GetFontFamilyModel)( + IDWriteFontCollection3 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontCollection2_GetFontSet)( + IDWriteFontCollection3 *This, + IDWriteFontSet1 **fontset); + + /*** IDWriteFontCollection3 methods ***/ + HANDLE (STDMETHODCALLTYPE *GetExpirationEvent)( + IDWriteFontCollection3 *This); + + END_INTERFACE +} IDWriteFontCollection3Vtbl; + +interface IDWriteFontCollection3 { + CONST_VTBL IDWriteFontCollection3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontCollection3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontCollection3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontCollection3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontCollection methods ***/ +#define IDWriteFontCollection3_GetFontFamilyCount(This) (This)->lpVtbl->GetFontFamilyCount(This) +#define IDWriteFontCollection3_FindFamilyName(This,name,index,exists) (This)->lpVtbl->FindFamilyName(This,name,index,exists) +#define IDWriteFontCollection3_GetFontFromFontFace(This,face,font) (This)->lpVtbl->GetFontFromFontFace(This,face,font) +/*** IDWriteFontCollection1 methods ***/ +/*** IDWriteFontCollection2 methods ***/ +#define IDWriteFontCollection3_GetFontFamily(This,index,family) (This)->lpVtbl->IDWriteFontCollection2_GetFontFamily(This,index,family) +#define IDWriteFontCollection3_GetMatchingFonts(This,familyname,axis_values,num_values,fontlist) (This)->lpVtbl->GetMatchingFonts(This,familyname,axis_values,num_values,fontlist) +#define IDWriteFontCollection3_GetFontFamilyModel(This) (This)->lpVtbl->GetFontFamilyModel(This) +#define IDWriteFontCollection3_GetFontSet(This,fontset) (This)->lpVtbl->IDWriteFontCollection2_GetFontSet(This,fontset) +/*** IDWriteFontCollection3 methods ***/ +#define IDWriteFontCollection3_GetExpirationEvent(This) (This)->lpVtbl->GetExpirationEvent(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection3_QueryInterface(IDWriteFontCollection3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontCollection3_AddRef(IDWriteFontCollection3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontCollection3_Release(IDWriteFontCollection3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontCollection methods ***/ +static FORCEINLINE UINT32 IDWriteFontCollection3_GetFontFamilyCount(IDWriteFontCollection3* This) { + return This->lpVtbl->GetFontFamilyCount(This); +} +static FORCEINLINE HRESULT IDWriteFontCollection3_FindFamilyName(IDWriteFontCollection3* This,const WCHAR *name,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFamilyName(This,name,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontCollection3_GetFontFromFontFace(IDWriteFontCollection3* This,IDWriteFontFace *face,IDWriteFont **font) { + return This->lpVtbl->GetFontFromFontFace(This,face,font); +} +/*** IDWriteFontCollection1 methods ***/ +/*** IDWriteFontCollection2 methods ***/ +static FORCEINLINE HRESULT IDWriteFontCollection3_GetFontFamily(IDWriteFontCollection3* This,UINT32 index,IDWriteFontFamily2 **family) { + return This->lpVtbl->IDWriteFontCollection2_GetFontFamily(This,index,family); +} +static FORCEINLINE HRESULT IDWriteFontCollection3_GetMatchingFonts(IDWriteFontCollection3* This,const WCHAR *familyname,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontList2 **fontlist) { + return This->lpVtbl->GetMatchingFonts(This,familyname,axis_values,num_values,fontlist); +} +static FORCEINLINE DWRITE_FONT_FAMILY_MODEL IDWriteFontCollection3_GetFontFamilyModel(IDWriteFontCollection3* This) { + return This->lpVtbl->GetFontFamilyModel(This); +} +static FORCEINLINE HRESULT IDWriteFontCollection3_GetFontSet(IDWriteFontCollection3* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFontCollection2_GetFontSet(This,fontset); +} +/*** IDWriteFontCollection3 methods ***/ +static FORCEINLINE HANDLE IDWriteFontCollection3_GetExpirationEvent(IDWriteFontCollection3* This) { + return This->lpVtbl->GetExpirationEvent(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontCollection3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFaceReference interface + */ +#ifndef __IDWriteFontFaceReference_INTERFACE_DEFINED__ +#define __IDWriteFontFaceReference_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFaceReference, 0x5e7fa7ca, 0xdde3, 0x424c, 0x89,0xf0, 0x9f,0xcd,0x6f,0xed,0x58,0xcd); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("5e7fa7ca-dde3-424c-89f0-9fcd6fed58cd") +IDWriteFontFaceReference : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontFace( + IDWriteFontFace3 **fontface) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFaceWithSimulations( + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFace3 **fontface) = 0; + + virtual WINBOOL STDMETHODCALLTYPE Equals( + IDWriteFontFaceReference *reference) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontFaceIndex( + ) = 0; + + virtual DWRITE_FONT_SIMULATIONS STDMETHODCALLTYPE GetSimulations( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontFile( + IDWriteFontFile **fontfile) = 0; + + virtual UINT64 STDMETHODCALLTYPE GetLocalFileSize( + ) = 0; + + virtual UINT64 STDMETHODCALLTYPE GetFileSize( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFileTime( + FILETIME *writetime) = 0; + + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetLocality( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnqueueFontDownloadRequest( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnqueueCharacterDownloadRequest( + const WCHAR *chars, + UINT32 count) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnqueueGlyphDownloadRequest( + const UINT16 *glyphs, + UINT32 count) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnqueueFileFragmentDownloadRequest( + UINT64 offset, + UINT64 size) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFaceReference, 0x5e7fa7ca, 0xdde3, 0x424c, 0x89,0xf0, 0x9f,0xcd,0x6f,0xed,0x58,0xcd) +#endif +#else +typedef struct IDWriteFontFaceReferenceVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFaceReference *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFaceReference *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFaceReference *This); + + /*** IDWriteFontFaceReference methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontFaceReference *This, + IDWriteFontFace3 **fontface); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceWithSimulations)( + IDWriteFontFaceReference *This, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFace3 **fontface); + + WINBOOL (STDMETHODCALLTYPE *Equals)( + IDWriteFontFaceReference *This, + IDWriteFontFaceReference *reference); + + UINT32 (STDMETHODCALLTYPE *GetFontFaceIndex)( + IDWriteFontFaceReference *This); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFontFaceReference *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFile)( + IDWriteFontFaceReference *This, + IDWriteFontFile **fontfile); + + UINT64 (STDMETHODCALLTYPE *GetLocalFileSize)( + IDWriteFontFaceReference *This); + + UINT64 (STDMETHODCALLTYPE *GetFileSize)( + IDWriteFontFaceReference *This); + + HRESULT (STDMETHODCALLTYPE *GetFileTime)( + IDWriteFontFaceReference *This, + FILETIME *writetime); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetLocality)( + IDWriteFontFaceReference *This); + + HRESULT (STDMETHODCALLTYPE *EnqueueFontDownloadRequest)( + IDWriteFontFaceReference *This); + + HRESULT (STDMETHODCALLTYPE *EnqueueCharacterDownloadRequest)( + IDWriteFontFaceReference *This, + const WCHAR *chars, + UINT32 count); + + HRESULT (STDMETHODCALLTYPE *EnqueueGlyphDownloadRequest)( + IDWriteFontFaceReference *This, + const UINT16 *glyphs, + UINT32 count); + + HRESULT (STDMETHODCALLTYPE *EnqueueFileFragmentDownloadRequest)( + IDWriteFontFaceReference *This, + UINT64 offset, + UINT64 size); + + END_INTERFACE +} IDWriteFontFaceReferenceVtbl; + +interface IDWriteFontFaceReference { + CONST_VTBL IDWriteFontFaceReferenceVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFaceReference_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFaceReference_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFaceReference_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFaceReference methods ***/ +#define IDWriteFontFaceReference_CreateFontFace(This,fontface) (This)->lpVtbl->CreateFontFace(This,fontface) +#define IDWriteFontFaceReference_CreateFontFaceWithSimulations(This,simulations,fontface) (This)->lpVtbl->CreateFontFaceWithSimulations(This,simulations,fontface) +#define IDWriteFontFaceReference_Equals(This,reference) (This)->lpVtbl->Equals(This,reference) +#define IDWriteFontFaceReference_GetFontFaceIndex(This) (This)->lpVtbl->GetFontFaceIndex(This) +#define IDWriteFontFaceReference_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +#define IDWriteFontFaceReference_GetFontFile(This,fontfile) (This)->lpVtbl->GetFontFile(This,fontfile) +#define IDWriteFontFaceReference_GetLocalFileSize(This) (This)->lpVtbl->GetLocalFileSize(This) +#define IDWriteFontFaceReference_GetFileSize(This) (This)->lpVtbl->GetFileSize(This) +#define IDWriteFontFaceReference_GetFileTime(This,writetime) (This)->lpVtbl->GetFileTime(This,writetime) +#define IDWriteFontFaceReference_GetLocality(This) (This)->lpVtbl->GetLocality(This) +#define IDWriteFontFaceReference_EnqueueFontDownloadRequest(This) (This)->lpVtbl->EnqueueFontDownloadRequest(This) +#define IDWriteFontFaceReference_EnqueueCharacterDownloadRequest(This,chars,count) (This)->lpVtbl->EnqueueCharacterDownloadRequest(This,chars,count) +#define IDWriteFontFaceReference_EnqueueGlyphDownloadRequest(This,glyphs,count) (This)->lpVtbl->EnqueueGlyphDownloadRequest(This,glyphs,count) +#define IDWriteFontFaceReference_EnqueueFileFragmentDownloadRequest(This,offset,size) (This)->lpVtbl->EnqueueFileFragmentDownloadRequest(This,offset,size) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFaceReference_QueryInterface(IDWriteFontFaceReference* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFaceReference_AddRef(IDWriteFontFaceReference* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFaceReference_Release(IDWriteFontFaceReference* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFaceReference methods ***/ +static FORCEINLINE HRESULT IDWriteFontFaceReference_CreateFontFace(IDWriteFontFaceReference* This,IDWriteFontFace3 **fontface) { + return This->lpVtbl->CreateFontFace(This,fontface); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_CreateFontFaceWithSimulations(IDWriteFontFaceReference* This,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFace3 **fontface) { + return This->lpVtbl->CreateFontFaceWithSimulations(This,simulations,fontface); +} +static FORCEINLINE WINBOOL IDWriteFontFaceReference_Equals(IDWriteFontFaceReference* This,IDWriteFontFaceReference *reference) { + return This->lpVtbl->Equals(This,reference); +} +static FORCEINLINE UINT32 IDWriteFontFaceReference_GetFontFaceIndex(IDWriteFontFaceReference* This) { + return This->lpVtbl->GetFontFaceIndex(This); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFontFaceReference_GetSimulations(IDWriteFontFaceReference* This) { + return This->lpVtbl->GetSimulations(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_GetFontFile(IDWriteFontFaceReference* This,IDWriteFontFile **fontfile) { + return This->lpVtbl->GetFontFile(This,fontfile); +} +static FORCEINLINE UINT64 IDWriteFontFaceReference_GetLocalFileSize(IDWriteFontFaceReference* This) { + return This->lpVtbl->GetLocalFileSize(This); +} +static FORCEINLINE UINT64 IDWriteFontFaceReference_GetFileSize(IDWriteFontFaceReference* This) { + return This->lpVtbl->GetFileSize(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_GetFileTime(IDWriteFontFaceReference* This,FILETIME *writetime) { + return This->lpVtbl->GetFileTime(This,writetime); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFontFaceReference_GetLocality(IDWriteFontFaceReference* This) { + return This->lpVtbl->GetLocality(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_EnqueueFontDownloadRequest(IDWriteFontFaceReference* This) { + return This->lpVtbl->EnqueueFontDownloadRequest(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_EnqueueCharacterDownloadRequest(IDWriteFontFaceReference* This,const WCHAR *chars,UINT32 count) { + return This->lpVtbl->EnqueueCharacterDownloadRequest(This,chars,count); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_EnqueueGlyphDownloadRequest(IDWriteFontFaceReference* This,const UINT16 *glyphs,UINT32 count) { + return This->lpVtbl->EnqueueGlyphDownloadRequest(This,glyphs,count); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference_EnqueueFileFragmentDownloadRequest(IDWriteFontFaceReference* This,UINT64 offset,UINT64 size) { + return This->lpVtbl->EnqueueFileFragmentDownloadRequest(This,offset,size); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFaceReference_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFaceReference1 interface + */ +#ifndef __IDWriteFontFaceReference1_INTERFACE_DEFINED__ +#define __IDWriteFontFaceReference1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFaceReference1, 0xc081fe77, 0x2fd1, 0x41ac, 0xa5,0xa3, 0x34,0x98,0x3c,0x4b,0xa6,0x1a); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("c081fe77-2fd1-41ac-a5a3-34983c4ba61a") +IDWriteFontFaceReference1 : public IDWriteFontFaceReference +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontFace( + IDWriteFontFace5 **fontface) = 0; + + using IDWriteFontFaceReference::CreateFontFace; + + virtual UINT32 STDMETHODCALLTYPE GetFontAxisValueCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisValues( + DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFaceReference1, 0xc081fe77, 0x2fd1, 0x41ac, 0xa5,0xa3, 0x34,0x98,0x3c,0x4b,0xa6,0x1a) +#endif +#else +typedef struct IDWriteFontFaceReference1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFaceReference1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFaceReference1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFaceReference1 *This); + + /*** IDWriteFontFaceReference methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontFaceReference1 *This, + IDWriteFontFace3 **fontface); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceWithSimulations)( + IDWriteFontFaceReference1 *This, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFace3 **fontface); + + WINBOOL (STDMETHODCALLTYPE *Equals)( + IDWriteFontFaceReference1 *This, + IDWriteFontFaceReference *reference); + + UINT32 (STDMETHODCALLTYPE *GetFontFaceIndex)( + IDWriteFontFaceReference1 *This); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFontFaceReference1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFile)( + IDWriteFontFaceReference1 *This, + IDWriteFontFile **fontfile); + + UINT64 (STDMETHODCALLTYPE *GetLocalFileSize)( + IDWriteFontFaceReference1 *This); + + UINT64 (STDMETHODCALLTYPE *GetFileSize)( + IDWriteFontFaceReference1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFileTime)( + IDWriteFontFaceReference1 *This, + FILETIME *writetime); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetLocality)( + IDWriteFontFaceReference1 *This); + + HRESULT (STDMETHODCALLTYPE *EnqueueFontDownloadRequest)( + IDWriteFontFaceReference1 *This); + + HRESULT (STDMETHODCALLTYPE *EnqueueCharacterDownloadRequest)( + IDWriteFontFaceReference1 *This, + const WCHAR *chars, + UINT32 count); + + HRESULT (STDMETHODCALLTYPE *EnqueueGlyphDownloadRequest)( + IDWriteFontFaceReference1 *This, + const UINT16 *glyphs, + UINT32 count); + + HRESULT (STDMETHODCALLTYPE *EnqueueFileFragmentDownloadRequest)( + IDWriteFontFaceReference1 *This, + UINT64 offset, + UINT64 size); + + /*** IDWriteFontFaceReference1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontFaceReference1_CreateFontFace)( + IDWriteFontFaceReference1 *This, + IDWriteFontFace5 **fontface); + + UINT32 (STDMETHODCALLTYPE *GetFontAxisValueCount)( + IDWriteFontFaceReference1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisValues)( + IDWriteFontFaceReference1 *This, + DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values); + + END_INTERFACE +} IDWriteFontFaceReference1Vtbl; + +interface IDWriteFontFaceReference1 { + CONST_VTBL IDWriteFontFaceReference1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFaceReference1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFaceReference1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFaceReference1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFaceReference methods ***/ +#define IDWriteFontFaceReference1_CreateFontFaceWithSimulations(This,simulations,fontface) (This)->lpVtbl->CreateFontFaceWithSimulations(This,simulations,fontface) +#define IDWriteFontFaceReference1_Equals(This,reference) (This)->lpVtbl->Equals(This,reference) +#define IDWriteFontFaceReference1_GetFontFaceIndex(This) (This)->lpVtbl->GetFontFaceIndex(This) +#define IDWriteFontFaceReference1_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +#define IDWriteFontFaceReference1_GetFontFile(This,fontfile) (This)->lpVtbl->GetFontFile(This,fontfile) +#define IDWriteFontFaceReference1_GetLocalFileSize(This) (This)->lpVtbl->GetLocalFileSize(This) +#define IDWriteFontFaceReference1_GetFileSize(This) (This)->lpVtbl->GetFileSize(This) +#define IDWriteFontFaceReference1_GetFileTime(This,writetime) (This)->lpVtbl->GetFileTime(This,writetime) +#define IDWriteFontFaceReference1_GetLocality(This) (This)->lpVtbl->GetLocality(This) +#define IDWriteFontFaceReference1_EnqueueFontDownloadRequest(This) (This)->lpVtbl->EnqueueFontDownloadRequest(This) +#define IDWriteFontFaceReference1_EnqueueCharacterDownloadRequest(This,chars,count) (This)->lpVtbl->EnqueueCharacterDownloadRequest(This,chars,count) +#define IDWriteFontFaceReference1_EnqueueGlyphDownloadRequest(This,glyphs,count) (This)->lpVtbl->EnqueueGlyphDownloadRequest(This,glyphs,count) +#define IDWriteFontFaceReference1_EnqueueFileFragmentDownloadRequest(This,offset,size) (This)->lpVtbl->EnqueueFileFragmentDownloadRequest(This,offset,size) +/*** IDWriteFontFaceReference1 methods ***/ +#define IDWriteFontFaceReference1_CreateFontFace(This,fontface) (This)->lpVtbl->IDWriteFontFaceReference1_CreateFontFace(This,fontface) +#define IDWriteFontFaceReference1_GetFontAxisValueCount(This) (This)->lpVtbl->GetFontAxisValueCount(This) +#define IDWriteFontFaceReference1_GetFontAxisValues(This,values,num_values) (This)->lpVtbl->GetFontAxisValues(This,values,num_values) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFaceReference1_QueryInterface(IDWriteFontFaceReference1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFaceReference1_AddRef(IDWriteFontFaceReference1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFaceReference1_Release(IDWriteFontFaceReference1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFaceReference methods ***/ +static FORCEINLINE HRESULT IDWriteFontFaceReference1_CreateFontFaceWithSimulations(IDWriteFontFaceReference1* This,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFace3 **fontface) { + return This->lpVtbl->CreateFontFaceWithSimulations(This,simulations,fontface); +} +static FORCEINLINE WINBOOL IDWriteFontFaceReference1_Equals(IDWriteFontFaceReference1* This,IDWriteFontFaceReference *reference) { + return This->lpVtbl->Equals(This,reference); +} +static FORCEINLINE UINT32 IDWriteFontFaceReference1_GetFontFaceIndex(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetFontFaceIndex(This); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFontFaceReference1_GetSimulations(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetSimulations(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_GetFontFile(IDWriteFontFaceReference1* This,IDWriteFontFile **fontfile) { + return This->lpVtbl->GetFontFile(This,fontfile); +} +static FORCEINLINE UINT64 IDWriteFontFaceReference1_GetLocalFileSize(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetLocalFileSize(This); +} +static FORCEINLINE UINT64 IDWriteFontFaceReference1_GetFileSize(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetFileSize(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_GetFileTime(IDWriteFontFaceReference1* This,FILETIME *writetime) { + return This->lpVtbl->GetFileTime(This,writetime); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFontFaceReference1_GetLocality(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetLocality(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_EnqueueFontDownloadRequest(IDWriteFontFaceReference1* This) { + return This->lpVtbl->EnqueueFontDownloadRequest(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_EnqueueCharacterDownloadRequest(IDWriteFontFaceReference1* This,const WCHAR *chars,UINT32 count) { + return This->lpVtbl->EnqueueCharacterDownloadRequest(This,chars,count); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_EnqueueGlyphDownloadRequest(IDWriteFontFaceReference1* This,const UINT16 *glyphs,UINT32 count) { + return This->lpVtbl->EnqueueGlyphDownloadRequest(This,glyphs,count); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_EnqueueFileFragmentDownloadRequest(IDWriteFontFaceReference1* This,UINT64 offset,UINT64 size) { + return This->lpVtbl->EnqueueFileFragmentDownloadRequest(This,offset,size); +} +/*** IDWriteFontFaceReference1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFaceReference1_CreateFontFace(IDWriteFontFaceReference1* This,IDWriteFontFace5 **fontface) { + return This->lpVtbl->IDWriteFontFaceReference1_CreateFontFace(This,fontface); +} +static FORCEINLINE UINT32 IDWriteFontFaceReference1_GetFontAxisValueCount(IDWriteFontFaceReference1* This) { + return This->lpVtbl->GetFontAxisValueCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFaceReference1_GetFontAxisValues(IDWriteFontFaceReference1* This,DWRITE_FONT_AXIS_VALUE *values,UINT32 num_values) { + return This->lpVtbl->GetFontAxisValues(This,values,num_values); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFaceReference1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontList1 interface + */ +#ifndef __IDWriteFontList1_INTERFACE_DEFINED__ +#define __IDWriteFontList1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontList1, 0xda20d8ef, 0x812a, 0x4c43, 0x98,0x02, 0x62,0xec,0x4a,0xbd,0x7a,0xde); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("da20d8ef-812a-4c43-9802-62ec4abd7ade") +IDWriteFontList1 : public IDWriteFontList +{ + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetFontLocality( + UINT32 index) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFont( + UINT32 index, + IDWriteFont3 **font) = 0; + + using IDWriteFontList::GetFont; + + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + UINT32 index, + IDWriteFontFaceReference **reference) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontList1, 0xda20d8ef, 0x812a, 0x4c43, 0x98,0x02, 0x62,0xec,0x4a,0xbd,0x7a,0xde) +#endif +#else +typedef struct IDWriteFontList1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontList1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontList1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontList1 *This); + + /*** IDWriteFontList methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteFontList1 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontList1 *This); + + HRESULT (STDMETHODCALLTYPE *GetFont)( + IDWriteFontList1 *This, + UINT32 index, + IDWriteFont **font); + + /*** IDWriteFontList1 methods ***/ + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontList1 *This, + UINT32 index); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontList1_GetFont)( + IDWriteFontList1 *This, + UINT32 index, + IDWriteFont3 **font); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontList1 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + END_INTERFACE +} IDWriteFontList1Vtbl; + +interface IDWriteFontList1 { + CONST_VTBL IDWriteFontList1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontList1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontList1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontList1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontList methods ***/ +#define IDWriteFontList1_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteFontList1_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +/*** IDWriteFontList1 methods ***/ +#define IDWriteFontList1_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +#define IDWriteFontList1_GetFont(This,index,font) (This)->lpVtbl->IDWriteFontList1_GetFont(This,index,font) +#define IDWriteFontList1_GetFontFaceReference(This,index,reference) (This)->lpVtbl->GetFontFaceReference(This,index,reference) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontList1_QueryInterface(IDWriteFontList1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontList1_AddRef(IDWriteFontList1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontList1_Release(IDWriteFontList1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontList methods ***/ +static FORCEINLINE HRESULT IDWriteFontList1_GetFontCollection(IDWriteFontList1* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteFontList1_GetFontCount(IDWriteFontList1* This) { + return This->lpVtbl->GetFontCount(This); +} +/*** IDWriteFontList1 methods ***/ +static FORCEINLINE DWRITE_LOCALITY IDWriteFontList1_GetFontLocality(IDWriteFontList1* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +static FORCEINLINE HRESULT IDWriteFontList1_GetFont(IDWriteFontList1* This,UINT32 index,IDWriteFont3 **font) { + return This->lpVtbl->IDWriteFontList1_GetFont(This,index,font); +} +static FORCEINLINE HRESULT IDWriteFontList1_GetFontFaceReference(IDWriteFontList1* This,UINT32 index,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,index,reference); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontList1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontList2 interface + */ +#ifndef __IDWriteFontList2_INTERFACE_DEFINED__ +#define __IDWriteFontList2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontList2, 0xc0763a34, 0x77af, 0x445a, 0xb7,0x35, 0x08,0xc3,0x7b,0x0a,0x5b,0xf5); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("c0763a34-77af-445a-b735-08c37b0a5bf5") +IDWriteFontList2 : public IDWriteFontList1 +{ + virtual HRESULT STDMETHODCALLTYPE GetFontSet( + IDWriteFontSet1 **fontset) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontList2, 0xc0763a34, 0x77af, 0x445a, 0xb7,0x35, 0x08,0xc3,0x7b,0x0a,0x5b,0xf5) +#endif +#else +typedef struct IDWriteFontList2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontList2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontList2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontList2 *This); + + /*** IDWriteFontList methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteFontList2 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontList2 *This); + + HRESULT (STDMETHODCALLTYPE *GetFont)( + IDWriteFontList2 *This, + UINT32 index, + IDWriteFont **font); + + /*** IDWriteFontList1 methods ***/ + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontList2 *This, + UINT32 index); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontList1_GetFont)( + IDWriteFontList2 *This, + UINT32 index, + IDWriteFont3 **font); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontList2 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + /*** IDWriteFontList2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontSet)( + IDWriteFontList2 *This, + IDWriteFontSet1 **fontset); + + END_INTERFACE +} IDWriteFontList2Vtbl; + +interface IDWriteFontList2 { + CONST_VTBL IDWriteFontList2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontList2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontList2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontList2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontList methods ***/ +#define IDWriteFontList2_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteFontList2_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +/*** IDWriteFontList1 methods ***/ +#define IDWriteFontList2_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +#define IDWriteFontList2_GetFont(This,index,font) (This)->lpVtbl->IDWriteFontList1_GetFont(This,index,font) +#define IDWriteFontList2_GetFontFaceReference(This,index,reference) (This)->lpVtbl->GetFontFaceReference(This,index,reference) +/*** IDWriteFontList2 methods ***/ +#define IDWriteFontList2_GetFontSet(This,fontset) (This)->lpVtbl->GetFontSet(This,fontset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontList2_QueryInterface(IDWriteFontList2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontList2_AddRef(IDWriteFontList2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontList2_Release(IDWriteFontList2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontList methods ***/ +static FORCEINLINE HRESULT IDWriteFontList2_GetFontCollection(IDWriteFontList2* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteFontList2_GetFontCount(IDWriteFontList2* This) { + return This->lpVtbl->GetFontCount(This); +} +/*** IDWriteFontList1 methods ***/ +static FORCEINLINE DWRITE_LOCALITY IDWriteFontList2_GetFontLocality(IDWriteFontList2* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +static FORCEINLINE HRESULT IDWriteFontList2_GetFont(IDWriteFontList2* This,UINT32 index,IDWriteFont3 **font) { + return This->lpVtbl->IDWriteFontList1_GetFont(This,index,font); +} +static FORCEINLINE HRESULT IDWriteFontList2_GetFontFaceReference(IDWriteFontList2* This,UINT32 index,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,index,reference); +} +/*** IDWriteFontList2 methods ***/ +static FORCEINLINE HRESULT IDWriteFontList2_GetFontSet(IDWriteFontList2* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFontSet(This,fontset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontList2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSet2 interface + */ +#ifndef __IDWriteFontSet2_INTERFACE_DEFINED__ +#define __IDWriteFontSet2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSet2, 0xdc7ead19, 0xe54c, 0x43af, 0xb2,0xda, 0x4e,0x2b,0x79,0xba,0x3f,0x7f); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("dc7ead19-e54c-43af-b2da-4e2b79ba3f7f") +IDWriteFontSet2 : public IDWriteFontSet1 +{ + virtual HANDLE STDMETHODCALLTYPE GetExpirationEvent( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSet2, 0xdc7ead19, 0xe54c, 0x43af, 0xb2,0xda, 0x4e,0x2b,0x79,0xba,0x3f,0x7f) +#endif +#else +typedef struct IDWriteFontSet2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSet2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSet2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSet2 *This); + + /*** IDWriteFontSet methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontSet2 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontSet2 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *FindFontFaceReference)( + IDWriteFontSet2 *This, + IDWriteFontFaceReference *reference, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *FindFontFace)( + IDWriteFontSet2 *This, + IDWriteFontFace *fontface, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues__)( + IDWriteFontSet2 *This, + DWRITE_FONT_PROPERTY_ID id, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues_)( + IDWriteFontSet2 *This, + DWRITE_FONT_PROPERTY_ID id, + const WCHAR *preferred_locales, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues)( + IDWriteFontSet2 *This, + UINT32 index, + DWRITE_FONT_PROPERTY_ID id, + WINBOOL *exists, + IDWriteLocalizedStrings **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyOccurrenceCount)( + IDWriteFontSet2 *This, + const DWRITE_FONT_PROPERTY *property, + UINT32 *count); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts_)( + IDWriteFontSet2 *This, + const WCHAR *family, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontSet2 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 count, + IDWriteFontSet **fontset); + + /*** IDWriteFontSet1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetMatchingFonts)( + IDWriteFontSet2 *This, + const DWRITE_FONT_PROPERTY *property, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFirstFontResources)( + IDWriteFontSet2 *This, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts__)( + IDWriteFontSet2 *This, + const UINT32 *indices, + UINT32 num_indices, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts_)( + IDWriteFontSet2 *This, + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts)( + IDWriteFontSet2 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_property, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices_)( + IDWriteFontSet2 *This, + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices)( + IDWriteFontSet2 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges_)( + IDWriteFontSet2 *This, + UINT32 font_index, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges)( + IDWriteFontSet2 *This, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetFontFaceReference)( + IDWriteFontSet2 *This, + UINT32 index, + IDWriteFontFaceReference1 **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontResource)( + IDWriteFontSet2 *This, + UINT32 index, + IDWriteFontResource **resource); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontSet2 *This, + UINT32 index, + IDWriteFontFace5 **fontface); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontSet2 *This, + UINT32 index); + + /*** IDWriteFontSet2 methods ***/ + HANDLE (STDMETHODCALLTYPE *GetExpirationEvent)( + IDWriteFontSet2 *This); + + END_INTERFACE +} IDWriteFontSet2Vtbl; + +interface IDWriteFontSet2 { + CONST_VTBL IDWriteFontSet2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSet2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSet2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSet2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSet methods ***/ +#define IDWriteFontSet2_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +#define IDWriteFontSet2_FindFontFaceReference(This,reference,index,exists) (This)->lpVtbl->FindFontFaceReference(This,reference,index,exists) +#define IDWriteFontSet2_FindFontFace(This,fontface,index,exists) (This)->lpVtbl->FindFontFace(This,fontface,index,exists) +#define IDWriteFontSet2_GetPropertyValues__(This,id,values) (This)->lpVtbl->GetPropertyValues__(This,id,values) +#define IDWriteFontSet2_GetPropertyValues_(This,id,preferred_locales,values) (This)->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values) +#define IDWriteFontSet2_GetPropertyValues(This,index,id,exists,values) (This)->lpVtbl->GetPropertyValues(This,index,id,exists,values) +#define IDWriteFontSet2_GetPropertyOccurrenceCount(This,property,count) (This)->lpVtbl->GetPropertyOccurrenceCount(This,property,count) +#define IDWriteFontSet2_GetMatchingFonts_(This,family,weight,stretch,style,fontset) (This)->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset) +/*** IDWriteFontSet1 methods ***/ +#define IDWriteFontSet2_GetMatchingFonts(This,property,axis_values,num_values,fontset) (This)->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset) +#define IDWriteFontSet2_GetFirstFontResources(This,fontset) (This)->lpVtbl->GetFirstFontResources(This,fontset) +#define IDWriteFontSet2_GetFilteredFonts__(This,indices,num_indices,fontset) (This)->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset) +#define IDWriteFontSet2_GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) (This)->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) +#define IDWriteFontSet2_GetFilteredFonts(This,props,num_properties,select_any_property,fontset) (This)->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset) +#define IDWriteFontSet2_GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet2_GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet2_GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet2_GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet2_GetFontFaceReference(This,index,reference) (This)->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference) +#define IDWriteFontSet2_CreateFontResource(This,index,resource) (This)->lpVtbl->CreateFontResource(This,index,resource) +#define IDWriteFontSet2_CreateFontFace(This,index,fontface) (This)->lpVtbl->CreateFontFace(This,index,fontface) +#define IDWriteFontSet2_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +/*** IDWriteFontSet2 methods ***/ +#define IDWriteFontSet2_GetExpirationEvent(This) (This)->lpVtbl->GetExpirationEvent(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet2_QueryInterface(IDWriteFontSet2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSet2_AddRef(IDWriteFontSet2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSet2_Release(IDWriteFontSet2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSet methods ***/ +static FORCEINLINE UINT32 IDWriteFontSet2_GetFontCount(IDWriteFontSet2* This) { + return This->lpVtbl->GetFontCount(This); +} +static FORCEINLINE HRESULT IDWriteFontSet2_FindFontFaceReference(IDWriteFontSet2* This,IDWriteFontFaceReference *reference,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFaceReference(This,reference,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet2_FindFontFace(IDWriteFontSet2* This,IDWriteFontFace *fontface,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFace(This,fontface,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetPropertyValues__(IDWriteFontSet2* This,DWRITE_FONT_PROPERTY_ID id,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues__(This,id,values); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetPropertyValues_(IDWriteFontSet2* This,DWRITE_FONT_PROPERTY_ID id,const WCHAR *preferred_locales,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetPropertyValues(IDWriteFontSet2* This,UINT32 index,DWRITE_FONT_PROPERTY_ID id,WINBOOL *exists,IDWriteLocalizedStrings **values) { + return This->lpVtbl->GetPropertyValues(This,index,id,exists,values); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetPropertyOccurrenceCount(IDWriteFontSet2* This,const DWRITE_FONT_PROPERTY *property,UINT32 *count) { + return This->lpVtbl->GetPropertyOccurrenceCount(This,property,count); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetMatchingFonts_(IDWriteFontSet2* This,const WCHAR *family,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFontSet **fontset) { + return This->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset); +} +/*** IDWriteFontSet1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet2_GetMatchingFonts(IDWriteFontSet2* This,const DWRITE_FONT_PROPERTY *property,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFirstFontResources(IDWriteFontSet2* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFirstFontResources(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFilteredFonts__(IDWriteFontSet2* This,const UINT32 *indices,UINT32 num_indices,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFilteredFonts_(IDWriteFontSet2* This,const DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,WINBOOL select_any_range,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFilteredFonts(IDWriteFontSet2* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_property,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFilteredFontIndices_(IDWriteFontSet2* This,const DWRITE_FONT_AXIS_RANGE *ranges,UINT32 num_ranges,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFilteredFontIndices(IDWriteFontSet2* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFontAxisRanges_(IDWriteFontSet2* This,UINT32 font_index,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFontAxisRanges(IDWriteFontSet2* This,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet2_GetFontFaceReference(IDWriteFontSet2* This,UINT32 index,IDWriteFontFaceReference1 **reference) { + return This->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference); +} +static FORCEINLINE HRESULT IDWriteFontSet2_CreateFontResource(IDWriteFontSet2* This,UINT32 index,IDWriteFontResource **resource) { + return This->lpVtbl->CreateFontResource(This,index,resource); +} +static FORCEINLINE HRESULT IDWriteFontSet2_CreateFontFace(IDWriteFontSet2* This,UINT32 index,IDWriteFontFace5 **fontface) { + return This->lpVtbl->CreateFontFace(This,index,fontface); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFontSet2_GetFontLocality(IDWriteFontSet2* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +/*** IDWriteFontSet2 methods ***/ +static FORCEINLINE HANDLE IDWriteFontSet2_GetExpirationEvent(IDWriteFontSet2* This) { + return This->lpVtbl->GetExpirationEvent(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSet2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSet3 interface + */ +#ifndef __IDWriteFontSet3_INTERFACE_DEFINED__ +#define __IDWriteFontSet3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSet3, 0x7c073ef2, 0xa7f4, 0x4045, 0x8c,0x32, 0x8a,0xb8,0xae,0x64,0x0f,0x90); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("7c073ef2-a7f4-4045-8c32-8ab8ae640f90") +IDWriteFontSet3 : public IDWriteFontSet2 +{ + virtual DWRITE_FONT_SOURCE_TYPE STDMETHODCALLTYPE GetFontSourceType( + UINT32 index) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontSourceNameLength( + UINT32 index) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontSourceName( + UINT32 index, + WCHAR *buffer, + UINT32 buffer_size) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSet3, 0x7c073ef2, 0xa7f4, 0x4045, 0x8c,0x32, 0x8a,0xb8,0xae,0x64,0x0f,0x90) +#endif +#else +typedef struct IDWriteFontSet3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSet3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSet3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSet3 *This); + + /*** IDWriteFontSet methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontCount)( + IDWriteFontSet3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontSet3 *This, + UINT32 index, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *FindFontFaceReference)( + IDWriteFontSet3 *This, + IDWriteFontFaceReference *reference, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *FindFontFace)( + IDWriteFontSet3 *This, + IDWriteFontFace *fontface, + UINT32 *index, + WINBOOL *exists); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues__)( + IDWriteFontSet3 *This, + DWRITE_FONT_PROPERTY_ID id, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues_)( + IDWriteFontSet3 *This, + DWRITE_FONT_PROPERTY_ID id, + const WCHAR *preferred_locales, + IDWriteStringList **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyValues)( + IDWriteFontSet3 *This, + UINT32 index, + DWRITE_FONT_PROPERTY_ID id, + WINBOOL *exists, + IDWriteLocalizedStrings **values); + + HRESULT (STDMETHODCALLTYPE *GetPropertyOccurrenceCount)( + IDWriteFontSet3 *This, + const DWRITE_FONT_PROPERTY *property, + UINT32 *count); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts_)( + IDWriteFontSet3 *This, + const WCHAR *family, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFonts)( + IDWriteFontSet3 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 count, + IDWriteFontSet **fontset); + + /*** IDWriteFontSet1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetMatchingFonts)( + IDWriteFontSet3 *This, + const DWRITE_FONT_PROPERTY *property, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFirstFontResources)( + IDWriteFontSet3 *This, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts__)( + IDWriteFontSet3 *This, + const UINT32 *indices, + UINT32 num_indices, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts_)( + IDWriteFontSet3 *This, + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFonts)( + IDWriteFontSet3 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_property, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices_)( + IDWriteFontSet3 *This, + const DWRITE_FONT_AXIS_RANGE *ranges, + UINT32 num_ranges, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFilteredFontIndices)( + IDWriteFontSet3 *This, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties, + WINBOOL select_any_range, + UINT32 *indices, + UINT32 num_indices, + UINT32 *actual_num_indices); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges_)( + IDWriteFontSet3 *This, + UINT32 font_index, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisRanges)( + IDWriteFontSet3 *This, + DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + UINT32 *actual_num_ranges); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontSet1_GetFontFaceReference)( + IDWriteFontSet3 *This, + UINT32 index, + IDWriteFontFaceReference1 **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontResource)( + IDWriteFontSet3 *This, + UINT32 index, + IDWriteFontResource **resource); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFontSet3 *This, + UINT32 index, + IDWriteFontFace5 **fontface); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetFontLocality)( + IDWriteFontSet3 *This, + UINT32 index); + + /*** IDWriteFontSet2 methods ***/ + HANDLE (STDMETHODCALLTYPE *GetExpirationEvent)( + IDWriteFontSet3 *This); + + /*** IDWriteFontSet3 methods ***/ + DWRITE_FONT_SOURCE_TYPE (STDMETHODCALLTYPE *GetFontSourceType)( + IDWriteFontSet3 *This, + UINT32 index); + + UINT32 (STDMETHODCALLTYPE *GetFontSourceNameLength)( + IDWriteFontSet3 *This, + UINT32 index); + + HRESULT (STDMETHODCALLTYPE *GetFontSourceName)( + IDWriteFontSet3 *This, + UINT32 index, + WCHAR *buffer, + UINT32 buffer_size); + + END_INTERFACE +} IDWriteFontSet3Vtbl; + +interface IDWriteFontSet3 { + CONST_VTBL IDWriteFontSet3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSet3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSet3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSet3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSet methods ***/ +#define IDWriteFontSet3_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) +#define IDWriteFontSet3_FindFontFaceReference(This,reference,index,exists) (This)->lpVtbl->FindFontFaceReference(This,reference,index,exists) +#define IDWriteFontSet3_FindFontFace(This,fontface,index,exists) (This)->lpVtbl->FindFontFace(This,fontface,index,exists) +#define IDWriteFontSet3_GetPropertyValues__(This,id,values) (This)->lpVtbl->GetPropertyValues__(This,id,values) +#define IDWriteFontSet3_GetPropertyValues_(This,id,preferred_locales,values) (This)->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values) +#define IDWriteFontSet3_GetPropertyValues(This,index,id,exists,values) (This)->lpVtbl->GetPropertyValues(This,index,id,exists,values) +#define IDWriteFontSet3_GetPropertyOccurrenceCount(This,property,count) (This)->lpVtbl->GetPropertyOccurrenceCount(This,property,count) +#define IDWriteFontSet3_GetMatchingFonts_(This,family,weight,stretch,style,fontset) (This)->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset) +/*** IDWriteFontSet1 methods ***/ +#define IDWriteFontSet3_GetMatchingFonts(This,property,axis_values,num_values,fontset) (This)->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset) +#define IDWriteFontSet3_GetFirstFontResources(This,fontset) (This)->lpVtbl->GetFirstFontResources(This,fontset) +#define IDWriteFontSet3_GetFilteredFonts__(This,indices,num_indices,fontset) (This)->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset) +#define IDWriteFontSet3_GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) (This)->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset) +#define IDWriteFontSet3_GetFilteredFonts(This,props,num_properties,select_any_property,fontset) (This)->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset) +#define IDWriteFontSet3_GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet3_GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) (This)->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices) +#define IDWriteFontSet3_GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet3_GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) (This)->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges) +#define IDWriteFontSet3_GetFontFaceReference(This,index,reference) (This)->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference) +#define IDWriteFontSet3_CreateFontResource(This,index,resource) (This)->lpVtbl->CreateFontResource(This,index,resource) +#define IDWriteFontSet3_CreateFontFace(This,index,fontface) (This)->lpVtbl->CreateFontFace(This,index,fontface) +#define IDWriteFontSet3_GetFontLocality(This,index) (This)->lpVtbl->GetFontLocality(This,index) +/*** IDWriteFontSet2 methods ***/ +#define IDWriteFontSet3_GetExpirationEvent(This) (This)->lpVtbl->GetExpirationEvent(This) +/*** IDWriteFontSet3 methods ***/ +#define IDWriteFontSet3_GetFontSourceType(This,index) (This)->lpVtbl->GetFontSourceType(This,index) +#define IDWriteFontSet3_GetFontSourceNameLength(This,index) (This)->lpVtbl->GetFontSourceNameLength(This,index) +#define IDWriteFontSet3_GetFontSourceName(This,index,buffer,buffer_size) (This)->lpVtbl->GetFontSourceName(This,index,buffer,buffer_size) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet3_QueryInterface(IDWriteFontSet3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSet3_AddRef(IDWriteFontSet3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSet3_Release(IDWriteFontSet3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSet methods ***/ +static FORCEINLINE UINT32 IDWriteFontSet3_GetFontCount(IDWriteFontSet3* This) { + return This->lpVtbl->GetFontCount(This); +} +static FORCEINLINE HRESULT IDWriteFontSet3_FindFontFaceReference(IDWriteFontSet3* This,IDWriteFontFaceReference *reference,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFaceReference(This,reference,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet3_FindFontFace(IDWriteFontSet3* This,IDWriteFontFace *fontface,UINT32 *index,WINBOOL *exists) { + return This->lpVtbl->FindFontFace(This,fontface,index,exists); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetPropertyValues__(IDWriteFontSet3* This,DWRITE_FONT_PROPERTY_ID id,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues__(This,id,values); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetPropertyValues_(IDWriteFontSet3* This,DWRITE_FONT_PROPERTY_ID id,const WCHAR *preferred_locales,IDWriteStringList **values) { + return This->lpVtbl->GetPropertyValues_(This,id,preferred_locales,values); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetPropertyValues(IDWriteFontSet3* This,UINT32 index,DWRITE_FONT_PROPERTY_ID id,WINBOOL *exists,IDWriteLocalizedStrings **values) { + return This->lpVtbl->GetPropertyValues(This,index,id,exists,values); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetPropertyOccurrenceCount(IDWriteFontSet3* This,const DWRITE_FONT_PROPERTY *property,UINT32 *count) { + return This->lpVtbl->GetPropertyOccurrenceCount(This,property,count); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetMatchingFonts_(IDWriteFontSet3* This,const WCHAR *family,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STRETCH stretch,DWRITE_FONT_STYLE style,IDWriteFontSet **fontset) { + return This->lpVtbl->GetMatchingFonts_(This,family,weight,stretch,style,fontset); +} +/*** IDWriteFontSet1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontSet3_GetMatchingFonts(IDWriteFontSet3* This,const DWRITE_FONT_PROPERTY *property,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFontSet1_GetMatchingFonts(This,property,axis_values,num_values,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFirstFontResources(IDWriteFontSet3* This,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFirstFontResources(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFilteredFonts__(IDWriteFontSet3* This,const UINT32 *indices,UINT32 num_indices,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts__(This,indices,num_indices,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFilteredFonts_(IDWriteFontSet3* This,const DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,WINBOOL select_any_range,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts_(This,axis_ranges,num_ranges,select_any_range,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFilteredFonts(IDWriteFontSet3* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_property,IDWriteFontSet1 **fontset) { + return This->lpVtbl->GetFilteredFonts(This,props,num_properties,select_any_property,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFilteredFontIndices_(IDWriteFontSet3* This,const DWRITE_FONT_AXIS_RANGE *ranges,UINT32 num_ranges,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices_(This,ranges,num_ranges,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFilteredFontIndices(IDWriteFontSet3* This,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties,WINBOOL select_any_range,UINT32 *indices,UINT32 num_indices,UINT32 *actual_num_indices) { + return This->lpVtbl->GetFilteredFontIndices(This,props,num_properties,select_any_range,indices,num_indices,actual_num_indices); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFontAxisRanges_(IDWriteFontSet3* This,UINT32 font_index,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges_(This,font_index,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFontAxisRanges(IDWriteFontSet3* This,DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,UINT32 *actual_num_ranges) { + return This->lpVtbl->GetFontAxisRanges(This,axis_ranges,num_ranges,actual_num_ranges); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFontFaceReference(IDWriteFontSet3* This,UINT32 index,IDWriteFontFaceReference1 **reference) { + return This->lpVtbl->IDWriteFontSet1_GetFontFaceReference(This,index,reference); +} +static FORCEINLINE HRESULT IDWriteFontSet3_CreateFontResource(IDWriteFontSet3* This,UINT32 index,IDWriteFontResource **resource) { + return This->lpVtbl->CreateFontResource(This,index,resource); +} +static FORCEINLINE HRESULT IDWriteFontSet3_CreateFontFace(IDWriteFontSet3* This,UINT32 index,IDWriteFontFace5 **fontface) { + return This->lpVtbl->CreateFontFace(This,index,fontface); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteFontSet3_GetFontLocality(IDWriteFontSet3* This,UINT32 index) { + return This->lpVtbl->GetFontLocality(This,index); +} +/*** IDWriteFontSet2 methods ***/ +static FORCEINLINE HANDLE IDWriteFontSet3_GetExpirationEvent(IDWriteFontSet3* This) { + return This->lpVtbl->GetExpirationEvent(This); +} +/*** IDWriteFontSet3 methods ***/ +static FORCEINLINE DWRITE_FONT_SOURCE_TYPE IDWriteFontSet3_GetFontSourceType(IDWriteFontSet3* This,UINT32 index) { + return This->lpVtbl->GetFontSourceType(This,index); +} +static FORCEINLINE UINT32 IDWriteFontSet3_GetFontSourceNameLength(IDWriteFontSet3* This,UINT32 index) { + return This->lpVtbl->GetFontSourceNameLength(This,index); +} +static FORCEINLINE HRESULT IDWriteFontSet3_GetFontSourceName(IDWriteFontSet3* This,UINT32 index,WCHAR *buffer,UINT32 buffer_size) { + return This->lpVtbl->GetFontSourceName(This,index,buffer,buffer_size); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSet3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFace3 interface + */ +#ifndef __IDWriteFontFace3_INTERFACE_DEFINED__ +#define __IDWriteFontFace3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFace3, 0xd37d7598, 0x09be, 0x4222, 0xa2,0x36, 0x20,0x81,0x34,0x1c,0xc1,0xf2); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("d37d7598-09be-4222-a236-2081341cc1f2") +IDWriteFontFace3 : public IDWriteFontFace2 +{ + virtual HRESULT STDMETHODCALLTYPE GetFontFaceReference( + IDWriteFontFaceReference **reference) = 0; + + virtual void STDMETHODCALLTYPE GetPanose( + DWRITE_PANOSE *panose) = 0; + + virtual DWRITE_FONT_WEIGHT STDMETHODCALLTYPE GetWeight( + ) = 0; + + virtual DWRITE_FONT_STRETCH STDMETHODCALLTYPE GetStretch( + ) = 0; + + virtual DWRITE_FONT_STYLE STDMETHODCALLTYPE GetStyle( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFamilyNames( + IDWriteLocalizedStrings **names) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFaceNames( + IDWriteLocalizedStrings **names) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInformationalStrings( + DWRITE_INFORMATIONAL_STRING_ID stringid, + IDWriteLocalizedStrings **strings, + WINBOOL *exists) = 0; + + virtual WINBOOL STDMETHODCALLTYPE HasCharacter( + UINT32 character) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetRecommendedRenderingMode( + FLOAT emsize, + FLOAT dpi_x, + FLOAT dpi_y, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE1 *rendering_mode, + DWRITE_GRID_FIT_MODE *gridfit_mode) = 0; + + virtual WINBOOL STDMETHODCALLTYPE IsCharacterLocal( + UINT32 character) = 0; + + virtual WINBOOL STDMETHODCALLTYPE IsGlyphLocal( + UINT16 glyph) = 0; + + virtual HRESULT STDMETHODCALLTYPE AreCharactersLocal( + const WCHAR *characters, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local) = 0; + + virtual HRESULT STDMETHODCALLTYPE AreGlyphsLocal( + const UINT16 *glyphs, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFace3, 0xd37d7598, 0x09be, 0x4222, 0xa2,0x36, 0x20,0x81,0x34,0x1c,0xc1,0xf2) +#endif +#else +typedef struct IDWriteFontFace3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFace3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFace3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFace3 *This); + + /*** IDWriteFontFace methods ***/ + DWRITE_FONT_FACE_TYPE (STDMETHODCALLTYPE *GetType)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFiles)( + IDWriteFontFace3 *This, + UINT32 *number_of_files, + IDWriteFontFile **fontfiles); + + UINT32 (STDMETHODCALLTYPE *GetIndex)( + IDWriteFontFace3 *This); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFontFace3 *This); + + WINBOOL (STDMETHODCALLTYPE *IsSymbolFont)( + IDWriteFontFace3 *This); + + void (STDMETHODCALLTYPE *GetMetrics)( + IDWriteFontFace3 *This, + DWRITE_FONT_METRICS *metrics); + + UINT16 (STDMETHODCALLTYPE *GetGlyphCount)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphMetrics)( + IDWriteFontFace3 *This, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)( + IDWriteFontFace3 *This, + const UINT32 *codepoints, + UINT32 count, + UINT16 *glyph_indices); + + HRESULT (STDMETHODCALLTYPE *TryGetFontTable)( + IDWriteFontFace3 *This, + UINT32 table_tag, + const void **table_data, + UINT32 *table_size, + void **context, + WINBOOL *exists); + + void (STDMETHODCALLTYPE *ReleaseFontTable)( + IDWriteFontFace3 *This, + void *table_context); + + HRESULT (STDMETHODCALLTYPE *GetGlyphRunOutline)( + IDWriteFontFace3 *This, + FLOAT emSize, + const UINT16 *glyph_indices, + const FLOAT *glyph_advances, + const DWRITE_GLYPH_OFFSET *glyph_offsets, + UINT32 glyph_count, + WINBOOL is_sideways, + WINBOOL is_rtl, + IDWriteGeometrySink *geometrysink); + + HRESULT (STDMETHODCALLTYPE *GetRecommendedRenderingMode)( + IDWriteFontFace3 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + DWRITE_MEASURING_MODE mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *rendering_mode); + + using IDWriteFontFace2::GetRecommendedRenderingMode; + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleMetrics)( + IDWriteFontFace3 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)( + IDWriteFontFace3 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + /*** IDWriteFontFace1 methods ***/ + void (STDMETHODCALLTYPE *IDWriteFontFace1_GetMetrics)( + IDWriteFontFace3 *This, + DWRITE_FONT_METRICS1 *metrics); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetGdiCompatibleMetrics)( + IDWriteFontFace3 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS1 *metrics); + + void (STDMETHODCALLTYPE *GetCaretMetrics)( + IDWriteFontFace3 *This, + DWRITE_CARET_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetUnicodeRanges)( + IDWriteFontFace3 *This, + UINT32 max_count, + DWRITE_UNICODE_RANGE *ranges, + UINT32 *count); + + WINBOOL (STDMETHODCALLTYPE *IsMonospacedFont)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphAdvances)( + IDWriteFontFace3 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphAdvances)( + IDWriteFontFace3 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + WINBOOL is_sideways, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances); + + HRESULT (STDMETHODCALLTYPE *GetKerningPairAdjustments)( + IDWriteFontFace3 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *adjustments); + + WINBOOL (STDMETHODCALLTYPE *HasKerningPairs)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetRecommendedRenderingMode)( + IDWriteFontFace3 *This, + FLOAT font_emsize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_RENDERING_MODE *rendering_mode); + + HRESULT (STDMETHODCALLTYPE *GetVerticalGlyphVariants)( + IDWriteFontFace3 *This, + UINT32 glyph_count, + const UINT16 *nominal_indices, + UINT16 *vertical_indices); + + WINBOOL (STDMETHODCALLTYPE *HasVerticalGlyphVariants)( + IDWriteFontFace3 *This); + + /*** IDWriteFontFace2 methods ***/ + WINBOOL (STDMETHODCALLTYPE *IsColorFont)( + IDWriteFontFace3 *This); + + UINT32 (STDMETHODCALLTYPE *GetColorPaletteCount)( + IDWriteFontFace3 *This); + + UINT32 (STDMETHODCALLTYPE *GetPaletteEntryCount)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *GetPaletteEntries)( + IDWriteFontFace3 *This, + UINT32 palette_index, + UINT32 first_entry_index, + UINT32 entry_count, + DWRITE_COLOR_F *entries); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace2_GetRecommendedRenderingMode)( + IDWriteFontFace3 *This, + FLOAT fontEmSize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuringmode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *renderingmode, + DWRITE_GRID_FIT_MODE *gridfitmode); + + /*** IDWriteFontFace3 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontFace3 *This, + IDWriteFontFaceReference **reference); + + void (STDMETHODCALLTYPE *GetPanose)( + IDWriteFontFace3 *This, + DWRITE_PANOSE *panose); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetWeight)( + IDWriteFontFace3 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetStretch)( + IDWriteFontFace3 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetStyle)( + IDWriteFontFace3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFamilyNames)( + IDWriteFontFace3 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetFaceNames)( + IDWriteFontFace3 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetInformationalStrings)( + IDWriteFontFace3 *This, + DWRITE_INFORMATIONAL_STRING_ID stringid, + IDWriteLocalizedStrings **strings, + WINBOOL *exists); + + WINBOOL (STDMETHODCALLTYPE *HasCharacter)( + IDWriteFontFace3 *This, + UINT32 character); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace3_GetRecommendedRenderingMode)( + IDWriteFontFace3 *This, + FLOAT emsize, + FLOAT dpi_x, + FLOAT dpi_y, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE1 *rendering_mode, + DWRITE_GRID_FIT_MODE *gridfit_mode); + + WINBOOL (STDMETHODCALLTYPE *IsCharacterLocal)( + IDWriteFontFace3 *This, + UINT32 character); + + WINBOOL (STDMETHODCALLTYPE *IsGlyphLocal)( + IDWriteFontFace3 *This, + UINT16 glyph); + + HRESULT (STDMETHODCALLTYPE *AreCharactersLocal)( + IDWriteFontFace3 *This, + const WCHAR *characters, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + HRESULT (STDMETHODCALLTYPE *AreGlyphsLocal)( + IDWriteFontFace3 *This, + const UINT16 *glyphs, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + END_INTERFACE +} IDWriteFontFace3Vtbl; + +interface IDWriteFontFace3 { + CONST_VTBL IDWriteFontFace3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFace3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFace3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFace3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFace methods ***/ +#define IDWriteFontFace3_GetType(This) (This)->lpVtbl->GetType(This) +#define IDWriteFontFace3_GetFiles(This,number_of_files,fontfiles) (This)->lpVtbl->GetFiles(This,number_of_files,fontfiles) +#define IDWriteFontFace3_GetIndex(This) (This)->lpVtbl->GetIndex(This) +#define IDWriteFontFace3_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +#define IDWriteFontFace3_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This) +#define IDWriteFontFace3_GetGlyphCount(This) (This)->lpVtbl->GetGlyphCount(This) +#define IDWriteFontFace3_GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) +#define IDWriteFontFace3_GetGlyphIndices(This,codepoints,count,glyph_indices) (This)->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices) +#define IDWriteFontFace3_TryGetFontTable(This,table_tag,table_data,table_size,context,exists) (This)->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists) +#define IDWriteFontFace3_ReleaseFontTable(This,table_context) (This)->lpVtbl->ReleaseFontTable(This,table_context) +#define IDWriteFontFace3_GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) (This)->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) +#define IDWriteFontFace3_GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) +/*** IDWriteFontFace1 methods ***/ +#define IDWriteFontFace3_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics) +#define IDWriteFontFace3_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) (This)->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) +#define IDWriteFontFace3_GetCaretMetrics(This,metrics) (This)->lpVtbl->GetCaretMetrics(This,metrics) +#define IDWriteFontFace3_GetUnicodeRanges(This,max_count,ranges,count) (This)->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count) +#define IDWriteFontFace3_IsMonospacedFont(This) (This)->lpVtbl->IsMonospacedFont(This) +#define IDWriteFontFace3_GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) (This)->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) +#define IDWriteFontFace3_GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) (This)->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) +#define IDWriteFontFace3_GetKerningPairAdjustments(This,glyph_count,indices,adjustments) (This)->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments) +#define IDWriteFontFace3_HasKerningPairs(This) (This)->lpVtbl->HasKerningPairs(This) +#define IDWriteFontFace3_GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) (This)->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) +#define IDWriteFontFace3_HasVerticalGlyphVariants(This) (This)->lpVtbl->HasVerticalGlyphVariants(This) +/*** IDWriteFontFace2 methods ***/ +#define IDWriteFontFace3_IsColorFont(This) (This)->lpVtbl->IsColorFont(This) +#define IDWriteFontFace3_GetColorPaletteCount(This) (This)->lpVtbl->GetColorPaletteCount(This) +#define IDWriteFontFace3_GetPaletteEntryCount(This) (This)->lpVtbl->GetPaletteEntryCount(This) +#define IDWriteFontFace3_GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) (This)->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) +/*** IDWriteFontFace3 methods ***/ +#define IDWriteFontFace3_GetFontFaceReference(This,reference) (This)->lpVtbl->GetFontFaceReference(This,reference) +#define IDWriteFontFace3_GetPanose(This,panose) (This)->lpVtbl->GetPanose(This,panose) +#define IDWriteFontFace3_GetWeight(This) (This)->lpVtbl->GetWeight(This) +#define IDWriteFontFace3_GetStretch(This) (This)->lpVtbl->GetStretch(This) +#define IDWriteFontFace3_GetStyle(This) (This)->lpVtbl->GetStyle(This) +#define IDWriteFontFace3_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) +#define IDWriteFontFace3_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names) +#define IDWriteFontFace3_GetInformationalStrings(This,stringid,strings,exists) (This)->lpVtbl->GetInformationalStrings(This,stringid,strings,exists) +#define IDWriteFontFace3_HasCharacter(This,character) (This)->lpVtbl->HasCharacter(This,character) +#define IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) (This)->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) +#define IDWriteFontFace3_IsCharacterLocal(This,character) (This)->lpVtbl->IsCharacterLocal(This,character) +#define IDWriteFontFace3_IsGlyphLocal(This,glyph) (This)->lpVtbl->IsGlyphLocal(This,glyph) +#define IDWriteFontFace3_AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) (This)->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) +#define IDWriteFontFace3_AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) (This)->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace3_QueryInterface(IDWriteFontFace3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFace3_AddRef(IDWriteFontFace3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFace3_Release(IDWriteFontFace3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFace methods ***/ +static FORCEINLINE DWRITE_FONT_FACE_TYPE IDWriteFontFace3_GetType(IDWriteFontFace3* This) { + return This->lpVtbl->GetType(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetFiles(IDWriteFontFace3* This,UINT32 *number_of_files,IDWriteFontFile **fontfiles) { + return This->lpVtbl->GetFiles(This,number_of_files,fontfiles); +} +static FORCEINLINE UINT32 IDWriteFontFace3_GetIndex(IDWriteFontFace3* This) { + return This->lpVtbl->GetIndex(This); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFontFace3_GetSimulations(IDWriteFontFace3* This) { + return This->lpVtbl->GetSimulations(This); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_IsSymbolFont(IDWriteFontFace3* This) { + return This->lpVtbl->IsSymbolFont(This); +} +static FORCEINLINE UINT16 IDWriteFontFace3_GetGlyphCount(IDWriteFontFace3* This) { + return This->lpVtbl->GetGlyphCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetDesignGlyphMetrics(IDWriteFontFace3* This,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetGlyphIndices(IDWriteFontFace3* This,const UINT32 *codepoints,UINT32 count,UINT16 *glyph_indices) { + return This->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices); +} +static FORCEINLINE HRESULT IDWriteFontFace3_TryGetFontTable(IDWriteFontFace3* This,UINT32 table_tag,const void **table_data,UINT32 *table_size,void **context,WINBOOL *exists) { + return This->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists); +} +static FORCEINLINE void IDWriteFontFace3_ReleaseFontTable(IDWriteFontFace3* This,void *table_context) { + This->lpVtbl->ReleaseFontTable(This,table_context); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetGlyphRunOutline(IDWriteFontFace3* This,FLOAT emSize,const UINT16 *glyph_indices,const FLOAT *glyph_advances,const DWRITE_GLYPH_OFFSET *glyph_offsets,UINT32 glyph_count,WINBOOL is_sideways,WINBOOL is_rtl,IDWriteGeometrySink *geometrysink) { + return This->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetGdiCompatibleGlyphMetrics(IDWriteFontFace3* This,FLOAT emSize,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways); +} +/*** IDWriteFontFace1 methods ***/ +static FORCEINLINE void IDWriteFontFace3_GetMetrics(IDWriteFontFace3* This,DWRITE_FONT_METRICS1 *metrics) { + This->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetGdiCompatibleMetrics(IDWriteFontFace3* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,DWRITE_FONT_METRICS1 *metrics) { + return This->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics); +} +static FORCEINLINE void IDWriteFontFace3_GetCaretMetrics(IDWriteFontFace3* This,DWRITE_CARET_METRICS *metrics) { + This->lpVtbl->GetCaretMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetUnicodeRanges(IDWriteFontFace3* This,UINT32 max_count,DWRITE_UNICODE_RANGE *ranges,UINT32 *count) { + return This->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_IsMonospacedFont(IDWriteFontFace3* This) { + return This->lpVtbl->IsMonospacedFont(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetDesignGlyphAdvances(IDWriteFontFace3* This,UINT32 glyph_count,const UINT16 *indices,INT32 *advances,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetGdiCompatibleGlyphAdvances(IDWriteFontFace3* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,WINBOOL is_sideways,UINT32 glyph_count,const UINT16 *indices,INT32 *advances) { + return This->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetKerningPairAdjustments(IDWriteFontFace3* This,UINT32 glyph_count,const UINT16 *indices,INT32 *adjustments) { + return This->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_HasKerningPairs(IDWriteFontFace3* This) { + return This->lpVtbl->HasKerningPairs(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetVerticalGlyphVariants(IDWriteFontFace3* This,UINT32 glyph_count,const UINT16 *nominal_indices,UINT16 *vertical_indices) { + return This->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_HasVerticalGlyphVariants(IDWriteFontFace3* This) { + return This->lpVtbl->HasVerticalGlyphVariants(This); +} +/*** IDWriteFontFace2 methods ***/ +static FORCEINLINE WINBOOL IDWriteFontFace3_IsColorFont(IDWriteFontFace3* This) { + return This->lpVtbl->IsColorFont(This); +} +static FORCEINLINE UINT32 IDWriteFontFace3_GetColorPaletteCount(IDWriteFontFace3* This) { + return This->lpVtbl->GetColorPaletteCount(This); +} +static FORCEINLINE UINT32 IDWriteFontFace3_GetPaletteEntryCount(IDWriteFontFace3* This) { + return This->lpVtbl->GetPaletteEntryCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetPaletteEntries(IDWriteFontFace3* This,UINT32 palette_index,UINT32 first_entry_index,UINT32 entry_count,DWRITE_COLOR_F *entries) { + return This->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries); +} +/*** IDWriteFontFace3 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace3_GetFontFaceReference(IDWriteFontFace3* This,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,reference); +} +static FORCEINLINE void IDWriteFontFace3_GetPanose(IDWriteFontFace3* This,DWRITE_PANOSE *panose) { + This->lpVtbl->GetPanose(This,panose); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteFontFace3_GetWeight(IDWriteFontFace3* This) { + return This->lpVtbl->GetWeight(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteFontFace3_GetStretch(IDWriteFontFace3* This) { + return This->lpVtbl->GetStretch(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteFontFace3_GetStyle(IDWriteFontFace3* This) { + return This->lpVtbl->GetStyle(This); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetFamilyNames(IDWriteFontFace3* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFamilyNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetFaceNames(IDWriteFontFace3* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFaceNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetInformationalStrings(IDWriteFontFace3* This,DWRITE_INFORMATIONAL_STRING_ID stringid,IDWriteLocalizedStrings **strings,WINBOOL *exists) { + return This->lpVtbl->GetInformationalStrings(This,stringid,strings,exists); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_HasCharacter(IDWriteFontFace3* This,UINT32 character) { + return This->lpVtbl->HasCharacter(This,character); +} +static FORCEINLINE HRESULT IDWriteFontFace3_GetRecommendedRenderingMode(IDWriteFontFace3* This,FLOAT emsize,FLOAT dpi_x,FLOAT dpi_y,const DWRITE_MATRIX *transform,WINBOOL is_sideways,DWRITE_OUTLINE_THRESHOLD threshold,DWRITE_MEASURING_MODE measuring_mode,IDWriteRenderingParams *params,DWRITE_RENDERING_MODE1 *rendering_mode,DWRITE_GRID_FIT_MODE *gridfit_mode) { + return This->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_IsCharacterLocal(IDWriteFontFace3* This,UINT32 character) { + return This->lpVtbl->IsCharacterLocal(This,character); +} +static FORCEINLINE WINBOOL IDWriteFontFace3_IsGlyphLocal(IDWriteFontFace3* This,UINT16 glyph) { + return This->lpVtbl->IsGlyphLocal(This,glyph); +} +static FORCEINLINE HRESULT IDWriteFontFace3_AreCharactersLocal(IDWriteFontFace3* This,const WCHAR *characters,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local); +} +static FORCEINLINE HRESULT IDWriteFontFace3_AreGlyphsLocal(IDWriteFontFace3* This,const UINT16 *glyphs,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFace3_INTERFACE_DEFINED__ */ + +typedef struct DWRITE_LINE_METRICS1 { + UINT32 length; + UINT32 trailingWhitespaceLength; + UINT32 newlineLength; + FLOAT height; + FLOAT baseline; + WINBOOL isTrimmed; + FLOAT leadingBefore; + FLOAT leadingAfter; +} DWRITE_LINE_METRICS1; +typedef enum DWRITE_FONT_LINE_GAP_USAGE { + DWRITE_FONT_LINE_GAP_USAGE_DEFAULT = 0, + DWRITE_FONT_LINE_GAP_USAGE_DISABLED = 1, + DWRITE_FONT_LINE_GAP_USAGE_ENABLED = 2 +} DWRITE_FONT_LINE_GAP_USAGE; +typedef struct DWRITE_LINE_SPACING { + DWRITE_LINE_SPACING_METHOD method; + FLOAT height; + FLOAT baseline; + FLOAT leadingBefore; + DWRITE_FONT_LINE_GAP_USAGE fontLineGapUsage; +} DWRITE_LINE_SPACING; +/***************************************************************************** + * IDWriteTextFormat2 interface + */ +#ifndef __IDWriteTextFormat2_INTERFACE_DEFINED__ +#define __IDWriteTextFormat2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteTextFormat2, 0xf67e0edd, 0x9e3d, 0x4ecc, 0x8c,0x32, 0x41,0x83,0x25,0x3d,0xfe,0x70); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("f67e0edd-9e3d-4ecc-8c32-4183253dfe70") +IDWriteTextFormat2 : public IDWriteTextFormat1 +{ + virtual HRESULT STDMETHODCALLTYPE SetLineSpacing( + const DWRITE_LINE_SPACING *spacing) = 0; + + using IDWriteTextFormat1::SetLineSpacing; + + virtual HRESULT STDMETHODCALLTYPE GetLineSpacing( + DWRITE_LINE_SPACING *spacing) = 0; + + using IDWriteTextFormat1::GetLineSpacing; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteTextFormat2, 0xf67e0edd, 0x9e3d, 0x4ecc, 0x8c,0x32, 0x41,0x83,0x25,0x3d,0xfe,0x70) +#endif +#else +typedef struct IDWriteTextFormat2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteTextFormat2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteTextFormat2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteTextFormat2 *This); + + /*** IDWriteTextFormat methods ***/ + HRESULT (STDMETHODCALLTYPE *SetTextAlignment)( + IDWriteTextFormat2 *This, + DWRITE_TEXT_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetParagraphAlignment)( + IDWriteTextFormat2 *This, + DWRITE_PARAGRAPH_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetWordWrapping)( + IDWriteTextFormat2 *This, + DWRITE_WORD_WRAPPING wrapping); + + HRESULT (STDMETHODCALLTYPE *SetReadingDirection)( + IDWriteTextFormat2 *This, + DWRITE_READING_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetFlowDirection)( + IDWriteTextFormat2 *This, + DWRITE_FLOW_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetIncrementalTabStop)( + IDWriteTextFormat2 *This, + FLOAT tabstop); + + HRESULT (STDMETHODCALLTYPE *SetTrimming)( + IDWriteTextFormat2 *This, + const DWRITE_TRIMMING *trimming, + IDWriteInlineObject *trimming_sign); + + HRESULT (STDMETHODCALLTYPE *SetLineSpacing)( + IDWriteTextFormat2 *This, + DWRITE_LINE_SPACING_METHOD spacing, + FLOAT line_spacing, + FLOAT baseline); + + DWRITE_TEXT_ALIGNMENT (STDMETHODCALLTYPE *GetTextAlignment)( + IDWriteTextFormat2 *This); + + DWRITE_PARAGRAPH_ALIGNMENT (STDMETHODCALLTYPE *GetParagraphAlignment)( + IDWriteTextFormat2 *This); + + DWRITE_WORD_WRAPPING (STDMETHODCALLTYPE *GetWordWrapping)( + IDWriteTextFormat2 *This); + + DWRITE_READING_DIRECTION (STDMETHODCALLTYPE *GetReadingDirection)( + IDWriteTextFormat2 *This); + + DWRITE_FLOW_DIRECTION (STDMETHODCALLTYPE *GetFlowDirection)( + IDWriteTextFormat2 *This); + + FLOAT (STDMETHODCALLTYPE *GetIncrementalTabStop)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *GetTrimming)( + IDWriteTextFormat2 *This, + DWRITE_TRIMMING *options, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *GetLineSpacing)( + IDWriteTextFormat2 *This, + DWRITE_LINE_SPACING_METHOD *method, + FLOAT *spacing, + FLOAT *baseline); + + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteTextFormat2 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontFamilyNameLength)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamilyName)( + IDWriteTextFormat2 *This, + WCHAR *name, + UINT32 size); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetFontWeight)( + IDWriteTextFormat2 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetFontStyle)( + IDWriteTextFormat2 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetFontStretch)( + IDWriteTextFormat2 *This); + + FLOAT (STDMETHODCALLTYPE *GetFontSize)( + IDWriteTextFormat2 *This); + + UINT32 (STDMETHODCALLTYPE *GetLocaleNameLength)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *GetLocaleName)( + IDWriteTextFormat2 *This, + WCHAR *name, + UINT32 size); + + /*** IDWriteTextFormat1 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetVerticalGlyphOrientation)( + IDWriteTextFormat2 *This, + DWRITE_VERTICAL_GLYPH_ORIENTATION orientation); + + DWRITE_VERTICAL_GLYPH_ORIENTATION (STDMETHODCALLTYPE *GetVerticalGlyphOrientation)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *SetLastLineWrapping)( + IDWriteTextFormat2 *This, + WINBOOL lastline_wrapping_enabled); + + WINBOOL (STDMETHODCALLTYPE *GetLastLineWrapping)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *SetOpticalAlignment)( + IDWriteTextFormat2 *This, + DWRITE_OPTICAL_ALIGNMENT alignment); + + DWRITE_OPTICAL_ALIGNMENT (STDMETHODCALLTYPE *GetOpticalAlignment)( + IDWriteTextFormat2 *This); + + HRESULT (STDMETHODCALLTYPE *SetFontFallback)( + IDWriteTextFormat2 *This, + IDWriteFontFallback *fallback); + + HRESULT (STDMETHODCALLTYPE *GetFontFallback)( + IDWriteTextFormat2 *This, + IDWriteFontFallback **fallback); + + /*** IDWriteTextFormat2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteTextFormat2_SetLineSpacing)( + IDWriteTextFormat2 *This, + const DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextFormat2_GetLineSpacing)( + IDWriteTextFormat2 *This, + DWRITE_LINE_SPACING *spacing); + + END_INTERFACE +} IDWriteTextFormat2Vtbl; + +interface IDWriteTextFormat2 { + CONST_VTBL IDWriteTextFormat2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteTextFormat2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteTextFormat2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteTextFormat2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteTextFormat methods ***/ +#define IDWriteTextFormat2_SetTextAlignment(This,alignment) (This)->lpVtbl->SetTextAlignment(This,alignment) +#define IDWriteTextFormat2_SetParagraphAlignment(This,alignment) (This)->lpVtbl->SetParagraphAlignment(This,alignment) +#define IDWriteTextFormat2_SetWordWrapping(This,wrapping) (This)->lpVtbl->SetWordWrapping(This,wrapping) +#define IDWriteTextFormat2_SetReadingDirection(This,direction) (This)->lpVtbl->SetReadingDirection(This,direction) +#define IDWriteTextFormat2_SetFlowDirection(This,direction) (This)->lpVtbl->SetFlowDirection(This,direction) +#define IDWriteTextFormat2_SetIncrementalTabStop(This,tabstop) (This)->lpVtbl->SetIncrementalTabStop(This,tabstop) +#define IDWriteTextFormat2_SetTrimming(This,trimming,trimming_sign) (This)->lpVtbl->SetTrimming(This,trimming,trimming_sign) +#define IDWriteTextFormat2_GetTextAlignment(This) (This)->lpVtbl->GetTextAlignment(This) +#define IDWriteTextFormat2_GetParagraphAlignment(This) (This)->lpVtbl->GetParagraphAlignment(This) +#define IDWriteTextFormat2_GetWordWrapping(This) (This)->lpVtbl->GetWordWrapping(This) +#define IDWriteTextFormat2_GetReadingDirection(This) (This)->lpVtbl->GetReadingDirection(This) +#define IDWriteTextFormat2_GetFlowDirection(This) (This)->lpVtbl->GetFlowDirection(This) +#define IDWriteTextFormat2_GetIncrementalTabStop(This) (This)->lpVtbl->GetIncrementalTabStop(This) +#define IDWriteTextFormat2_GetTrimming(This,options,trimming_sign) (This)->lpVtbl->GetTrimming(This,options,trimming_sign) +#define IDWriteTextFormat2_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteTextFormat2_GetFontFamilyNameLength(This) (This)->lpVtbl->GetFontFamilyNameLength(This) +#define IDWriteTextFormat2_GetFontFamilyName(This,name,size) (This)->lpVtbl->GetFontFamilyName(This,name,size) +#define IDWriteTextFormat2_GetFontWeight(This) (This)->lpVtbl->GetFontWeight(This) +#define IDWriteTextFormat2_GetFontStyle(This) (This)->lpVtbl->GetFontStyle(This) +#define IDWriteTextFormat2_GetFontStretch(This) (This)->lpVtbl->GetFontStretch(This) +#define IDWriteTextFormat2_GetFontSize(This) (This)->lpVtbl->GetFontSize(This) +#define IDWriteTextFormat2_GetLocaleNameLength(This) (This)->lpVtbl->GetLocaleNameLength(This) +#define IDWriteTextFormat2_GetLocaleName(This,name,size) (This)->lpVtbl->GetLocaleName(This,name,size) +/*** IDWriteTextFormat1 methods ***/ +#define IDWriteTextFormat2_SetVerticalGlyphOrientation(This,orientation) (This)->lpVtbl->SetVerticalGlyphOrientation(This,orientation) +#define IDWriteTextFormat2_GetVerticalGlyphOrientation(This) (This)->lpVtbl->GetVerticalGlyphOrientation(This) +#define IDWriteTextFormat2_SetLastLineWrapping(This,lastline_wrapping_enabled) (This)->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled) +#define IDWriteTextFormat2_GetLastLineWrapping(This) (This)->lpVtbl->GetLastLineWrapping(This) +#define IDWriteTextFormat2_SetOpticalAlignment(This,alignment) (This)->lpVtbl->SetOpticalAlignment(This,alignment) +#define IDWriteTextFormat2_GetOpticalAlignment(This) (This)->lpVtbl->GetOpticalAlignment(This) +#define IDWriteTextFormat2_SetFontFallback(This,fallback) (This)->lpVtbl->SetFontFallback(This,fallback) +#define IDWriteTextFormat2_GetFontFallback(This,fallback) (This)->lpVtbl->GetFontFallback(This,fallback) +/*** IDWriteTextFormat2 methods ***/ +#define IDWriteTextFormat2_SetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextFormat2_SetLineSpacing(This,spacing) +#define IDWriteTextFormat2_GetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextFormat2_GetLineSpacing(This,spacing) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat2_QueryInterface(IDWriteTextFormat2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteTextFormat2_AddRef(IDWriteTextFormat2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteTextFormat2_Release(IDWriteTextFormat2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteTextFormat methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat2_SetTextAlignment(IDWriteTextFormat2* This,DWRITE_TEXT_ALIGNMENT alignment) { + return This->lpVtbl->SetTextAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetParagraphAlignment(IDWriteTextFormat2* This,DWRITE_PARAGRAPH_ALIGNMENT alignment) { + return This->lpVtbl->SetParagraphAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetWordWrapping(IDWriteTextFormat2* This,DWRITE_WORD_WRAPPING wrapping) { + return This->lpVtbl->SetWordWrapping(This,wrapping); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetReadingDirection(IDWriteTextFormat2* This,DWRITE_READING_DIRECTION direction) { + return This->lpVtbl->SetReadingDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetFlowDirection(IDWriteTextFormat2* This,DWRITE_FLOW_DIRECTION direction) { + return This->lpVtbl->SetFlowDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetIncrementalTabStop(IDWriteTextFormat2* This,FLOAT tabstop) { + return This->lpVtbl->SetIncrementalTabStop(This,tabstop); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetTrimming(IDWriteTextFormat2* This,const DWRITE_TRIMMING *trimming,IDWriteInlineObject *trimming_sign) { + return This->lpVtbl->SetTrimming(This,trimming,trimming_sign); +} +static FORCEINLINE DWRITE_TEXT_ALIGNMENT IDWriteTextFormat2_GetTextAlignment(IDWriteTextFormat2* This) { + return This->lpVtbl->GetTextAlignment(This); +} +static FORCEINLINE DWRITE_PARAGRAPH_ALIGNMENT IDWriteTextFormat2_GetParagraphAlignment(IDWriteTextFormat2* This) { + return This->lpVtbl->GetParagraphAlignment(This); +} +static FORCEINLINE DWRITE_WORD_WRAPPING IDWriteTextFormat2_GetWordWrapping(IDWriteTextFormat2* This) { + return This->lpVtbl->GetWordWrapping(This); +} +static FORCEINLINE DWRITE_READING_DIRECTION IDWriteTextFormat2_GetReadingDirection(IDWriteTextFormat2* This) { + return This->lpVtbl->GetReadingDirection(This); +} +static FORCEINLINE DWRITE_FLOW_DIRECTION IDWriteTextFormat2_GetFlowDirection(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFlowDirection(This); +} +static FORCEINLINE FLOAT IDWriteTextFormat2_GetIncrementalTabStop(IDWriteTextFormat2* This) { + return This->lpVtbl->GetIncrementalTabStop(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetTrimming(IDWriteTextFormat2* This,DWRITE_TRIMMING *options,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->GetTrimming(This,options,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetFontCollection(IDWriteTextFormat2* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteTextFormat2_GetFontFamilyNameLength(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFontFamilyNameLength(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetFontFamilyName(IDWriteTextFormat2* This,WCHAR *name,UINT32 size) { + return This->lpVtbl->GetFontFamilyName(This,name,size); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteTextFormat2_GetFontWeight(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFontWeight(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteTextFormat2_GetFontStyle(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFontStyle(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteTextFormat2_GetFontStretch(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFontStretch(This); +} +static FORCEINLINE FLOAT IDWriteTextFormat2_GetFontSize(IDWriteTextFormat2* This) { + return This->lpVtbl->GetFontSize(This); +} +static FORCEINLINE UINT32 IDWriteTextFormat2_GetLocaleNameLength(IDWriteTextFormat2* This) { + return This->lpVtbl->GetLocaleNameLength(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetLocaleName(IDWriteTextFormat2* This,WCHAR *name,UINT32 size) { + return This->lpVtbl->GetLocaleName(This,name,size); +} +/*** IDWriteTextFormat1 methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat2_SetVerticalGlyphOrientation(IDWriteTextFormat2* This,DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) { + return This->lpVtbl->SetVerticalGlyphOrientation(This,orientation); +} +static FORCEINLINE DWRITE_VERTICAL_GLYPH_ORIENTATION IDWriteTextFormat2_GetVerticalGlyphOrientation(IDWriteTextFormat2* This) { + return This->lpVtbl->GetVerticalGlyphOrientation(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetLastLineWrapping(IDWriteTextFormat2* This,WINBOOL lastline_wrapping_enabled) { + return This->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled); +} +static FORCEINLINE WINBOOL IDWriteTextFormat2_GetLastLineWrapping(IDWriteTextFormat2* This) { + return This->lpVtbl->GetLastLineWrapping(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetOpticalAlignment(IDWriteTextFormat2* This,DWRITE_OPTICAL_ALIGNMENT alignment) { + return This->lpVtbl->SetOpticalAlignment(This,alignment); +} +static FORCEINLINE DWRITE_OPTICAL_ALIGNMENT IDWriteTextFormat2_GetOpticalAlignment(IDWriteTextFormat2* This) { + return This->lpVtbl->GetOpticalAlignment(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_SetFontFallback(IDWriteTextFormat2* This,IDWriteFontFallback *fallback) { + return This->lpVtbl->SetFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetFontFallback(IDWriteTextFormat2* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetFontFallback(This,fallback); +} +/*** IDWriteTextFormat2 methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat2_SetLineSpacing(IDWriteTextFormat2* This,const DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextFormat2_SetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextFormat2_GetLineSpacing(IDWriteTextFormat2* This,DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextFormat2_GetLineSpacing(This,spacing); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteTextFormat2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteTextFormat3 interface + */ +#ifndef __IDWriteTextFormat3_INTERFACE_DEFINED__ +#define __IDWriteTextFormat3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteTextFormat3, 0x6d3b5641, 0xe550, 0x430d, 0xa8,0x5b, 0xb7,0xbf,0x48,0xa9,0x34,0x27); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("6d3b5641-e550-430d-a85b-b7bf48a93427") +IDWriteTextFormat3 : public IDWriteTextFormat2 +{ + virtual HRESULT STDMETHODCALLTYPE SetFontAxisValues( + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontAxisValueCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisValues( + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values) = 0; + + virtual DWRITE_AUTOMATIC_FONT_AXES STDMETHODCALLTYPE GetAutomaticFontAxes( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAutomaticFontAxes( + DWRITE_AUTOMATIC_FONT_AXES axes) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteTextFormat3, 0x6d3b5641, 0xe550, 0x430d, 0xa8,0x5b, 0xb7,0xbf,0x48,0xa9,0x34,0x27) +#endif +#else +typedef struct IDWriteTextFormat3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteTextFormat3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteTextFormat3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteTextFormat3 *This); + + /*** IDWriteTextFormat methods ***/ + HRESULT (STDMETHODCALLTYPE *SetTextAlignment)( + IDWriteTextFormat3 *This, + DWRITE_TEXT_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetParagraphAlignment)( + IDWriteTextFormat3 *This, + DWRITE_PARAGRAPH_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetWordWrapping)( + IDWriteTextFormat3 *This, + DWRITE_WORD_WRAPPING wrapping); + + HRESULT (STDMETHODCALLTYPE *SetReadingDirection)( + IDWriteTextFormat3 *This, + DWRITE_READING_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetFlowDirection)( + IDWriteTextFormat3 *This, + DWRITE_FLOW_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetIncrementalTabStop)( + IDWriteTextFormat3 *This, + FLOAT tabstop); + + HRESULT (STDMETHODCALLTYPE *SetTrimming)( + IDWriteTextFormat3 *This, + const DWRITE_TRIMMING *trimming, + IDWriteInlineObject *trimming_sign); + + HRESULT (STDMETHODCALLTYPE *SetLineSpacing)( + IDWriteTextFormat3 *This, + DWRITE_LINE_SPACING_METHOD spacing, + FLOAT line_spacing, + FLOAT baseline); + + DWRITE_TEXT_ALIGNMENT (STDMETHODCALLTYPE *GetTextAlignment)( + IDWriteTextFormat3 *This); + + DWRITE_PARAGRAPH_ALIGNMENT (STDMETHODCALLTYPE *GetParagraphAlignment)( + IDWriteTextFormat3 *This); + + DWRITE_WORD_WRAPPING (STDMETHODCALLTYPE *GetWordWrapping)( + IDWriteTextFormat3 *This); + + DWRITE_READING_DIRECTION (STDMETHODCALLTYPE *GetReadingDirection)( + IDWriteTextFormat3 *This); + + DWRITE_FLOW_DIRECTION (STDMETHODCALLTYPE *GetFlowDirection)( + IDWriteTextFormat3 *This); + + FLOAT (STDMETHODCALLTYPE *GetIncrementalTabStop)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *GetTrimming)( + IDWriteTextFormat3 *This, + DWRITE_TRIMMING *options, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *GetLineSpacing)( + IDWriteTextFormat3 *This, + DWRITE_LINE_SPACING_METHOD *method, + FLOAT *spacing, + FLOAT *baseline); + + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteTextFormat3 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontFamilyNameLength)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamilyName)( + IDWriteTextFormat3 *This, + WCHAR *name, + UINT32 size); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetFontWeight)( + IDWriteTextFormat3 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetFontStyle)( + IDWriteTextFormat3 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetFontStretch)( + IDWriteTextFormat3 *This); + + FLOAT (STDMETHODCALLTYPE *GetFontSize)( + IDWriteTextFormat3 *This); + + UINT32 (STDMETHODCALLTYPE *GetLocaleNameLength)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *GetLocaleName)( + IDWriteTextFormat3 *This, + WCHAR *name, + UINT32 size); + + /*** IDWriteTextFormat1 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetVerticalGlyphOrientation)( + IDWriteTextFormat3 *This, + DWRITE_VERTICAL_GLYPH_ORIENTATION orientation); + + DWRITE_VERTICAL_GLYPH_ORIENTATION (STDMETHODCALLTYPE *GetVerticalGlyphOrientation)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *SetLastLineWrapping)( + IDWriteTextFormat3 *This, + WINBOOL lastline_wrapping_enabled); + + WINBOOL (STDMETHODCALLTYPE *GetLastLineWrapping)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *SetOpticalAlignment)( + IDWriteTextFormat3 *This, + DWRITE_OPTICAL_ALIGNMENT alignment); + + DWRITE_OPTICAL_ALIGNMENT (STDMETHODCALLTYPE *GetOpticalAlignment)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *SetFontFallback)( + IDWriteTextFormat3 *This, + IDWriteFontFallback *fallback); + + HRESULT (STDMETHODCALLTYPE *GetFontFallback)( + IDWriteTextFormat3 *This, + IDWriteFontFallback **fallback); + + /*** IDWriteTextFormat2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteTextFormat2_SetLineSpacing)( + IDWriteTextFormat3 *This, + const DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextFormat2_GetLineSpacing)( + IDWriteTextFormat3 *This, + DWRITE_LINE_SPACING *spacing); + + /*** IDWriteTextFormat3 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetFontAxisValues)( + IDWriteTextFormat3 *This, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values); + + UINT32 (STDMETHODCALLTYPE *GetFontAxisValueCount)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisValues)( + IDWriteTextFormat3 *This, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values); + + DWRITE_AUTOMATIC_FONT_AXES (STDMETHODCALLTYPE *GetAutomaticFontAxes)( + IDWriteTextFormat3 *This); + + HRESULT (STDMETHODCALLTYPE *SetAutomaticFontAxes)( + IDWriteTextFormat3 *This, + DWRITE_AUTOMATIC_FONT_AXES axes); + + END_INTERFACE +} IDWriteTextFormat3Vtbl; + +interface IDWriteTextFormat3 { + CONST_VTBL IDWriteTextFormat3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteTextFormat3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteTextFormat3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteTextFormat3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteTextFormat methods ***/ +#define IDWriteTextFormat3_SetTextAlignment(This,alignment) (This)->lpVtbl->SetTextAlignment(This,alignment) +#define IDWriteTextFormat3_SetParagraphAlignment(This,alignment) (This)->lpVtbl->SetParagraphAlignment(This,alignment) +#define IDWriteTextFormat3_SetWordWrapping(This,wrapping) (This)->lpVtbl->SetWordWrapping(This,wrapping) +#define IDWriteTextFormat3_SetReadingDirection(This,direction) (This)->lpVtbl->SetReadingDirection(This,direction) +#define IDWriteTextFormat3_SetFlowDirection(This,direction) (This)->lpVtbl->SetFlowDirection(This,direction) +#define IDWriteTextFormat3_SetIncrementalTabStop(This,tabstop) (This)->lpVtbl->SetIncrementalTabStop(This,tabstop) +#define IDWriteTextFormat3_SetTrimming(This,trimming,trimming_sign) (This)->lpVtbl->SetTrimming(This,trimming,trimming_sign) +#define IDWriteTextFormat3_GetTextAlignment(This) (This)->lpVtbl->GetTextAlignment(This) +#define IDWriteTextFormat3_GetParagraphAlignment(This) (This)->lpVtbl->GetParagraphAlignment(This) +#define IDWriteTextFormat3_GetWordWrapping(This) (This)->lpVtbl->GetWordWrapping(This) +#define IDWriteTextFormat3_GetReadingDirection(This) (This)->lpVtbl->GetReadingDirection(This) +#define IDWriteTextFormat3_GetFlowDirection(This) (This)->lpVtbl->GetFlowDirection(This) +#define IDWriteTextFormat3_GetIncrementalTabStop(This) (This)->lpVtbl->GetIncrementalTabStop(This) +#define IDWriteTextFormat3_GetTrimming(This,options,trimming_sign) (This)->lpVtbl->GetTrimming(This,options,trimming_sign) +#define IDWriteTextFormat3_GetFontCollection(This,collection) (This)->lpVtbl->GetFontCollection(This,collection) +#define IDWriteTextFormat3_GetFontFamilyNameLength(This) (This)->lpVtbl->GetFontFamilyNameLength(This) +#define IDWriteTextFormat3_GetFontFamilyName(This,name,size) (This)->lpVtbl->GetFontFamilyName(This,name,size) +#define IDWriteTextFormat3_GetFontWeight(This) (This)->lpVtbl->GetFontWeight(This) +#define IDWriteTextFormat3_GetFontStyle(This) (This)->lpVtbl->GetFontStyle(This) +#define IDWriteTextFormat3_GetFontStretch(This) (This)->lpVtbl->GetFontStretch(This) +#define IDWriteTextFormat3_GetFontSize(This) (This)->lpVtbl->GetFontSize(This) +#define IDWriteTextFormat3_GetLocaleNameLength(This) (This)->lpVtbl->GetLocaleNameLength(This) +#define IDWriteTextFormat3_GetLocaleName(This,name,size) (This)->lpVtbl->GetLocaleName(This,name,size) +/*** IDWriteTextFormat1 methods ***/ +#define IDWriteTextFormat3_SetVerticalGlyphOrientation(This,orientation) (This)->lpVtbl->SetVerticalGlyphOrientation(This,orientation) +#define IDWriteTextFormat3_GetVerticalGlyphOrientation(This) (This)->lpVtbl->GetVerticalGlyphOrientation(This) +#define IDWriteTextFormat3_SetLastLineWrapping(This,lastline_wrapping_enabled) (This)->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled) +#define IDWriteTextFormat3_GetLastLineWrapping(This) (This)->lpVtbl->GetLastLineWrapping(This) +#define IDWriteTextFormat3_SetOpticalAlignment(This,alignment) (This)->lpVtbl->SetOpticalAlignment(This,alignment) +#define IDWriteTextFormat3_GetOpticalAlignment(This) (This)->lpVtbl->GetOpticalAlignment(This) +#define IDWriteTextFormat3_SetFontFallback(This,fallback) (This)->lpVtbl->SetFontFallback(This,fallback) +#define IDWriteTextFormat3_GetFontFallback(This,fallback) (This)->lpVtbl->GetFontFallback(This,fallback) +/*** IDWriteTextFormat2 methods ***/ +#define IDWriteTextFormat3_SetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextFormat2_SetLineSpacing(This,spacing) +#define IDWriteTextFormat3_GetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextFormat2_GetLineSpacing(This,spacing) +/*** IDWriteTextFormat3 methods ***/ +#define IDWriteTextFormat3_SetFontAxisValues(This,axis_values,num_values) (This)->lpVtbl->SetFontAxisValues(This,axis_values,num_values) +#define IDWriteTextFormat3_GetFontAxisValueCount(This) (This)->lpVtbl->GetFontAxisValueCount(This) +#define IDWriteTextFormat3_GetFontAxisValues(This,axis_values,num_values) (This)->lpVtbl->GetFontAxisValues(This,axis_values,num_values) +#define IDWriteTextFormat3_GetAutomaticFontAxes(This) (This)->lpVtbl->GetAutomaticFontAxes(This) +#define IDWriteTextFormat3_SetAutomaticFontAxes(This,axes) (This)->lpVtbl->SetAutomaticFontAxes(This,axes) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat3_QueryInterface(IDWriteTextFormat3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteTextFormat3_AddRef(IDWriteTextFormat3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteTextFormat3_Release(IDWriteTextFormat3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteTextFormat methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat3_SetTextAlignment(IDWriteTextFormat3* This,DWRITE_TEXT_ALIGNMENT alignment) { + return This->lpVtbl->SetTextAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetParagraphAlignment(IDWriteTextFormat3* This,DWRITE_PARAGRAPH_ALIGNMENT alignment) { + return This->lpVtbl->SetParagraphAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetWordWrapping(IDWriteTextFormat3* This,DWRITE_WORD_WRAPPING wrapping) { + return This->lpVtbl->SetWordWrapping(This,wrapping); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetReadingDirection(IDWriteTextFormat3* This,DWRITE_READING_DIRECTION direction) { + return This->lpVtbl->SetReadingDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetFlowDirection(IDWriteTextFormat3* This,DWRITE_FLOW_DIRECTION direction) { + return This->lpVtbl->SetFlowDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetIncrementalTabStop(IDWriteTextFormat3* This,FLOAT tabstop) { + return This->lpVtbl->SetIncrementalTabStop(This,tabstop); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetTrimming(IDWriteTextFormat3* This,const DWRITE_TRIMMING *trimming,IDWriteInlineObject *trimming_sign) { + return This->lpVtbl->SetTrimming(This,trimming,trimming_sign); +} +static FORCEINLINE DWRITE_TEXT_ALIGNMENT IDWriteTextFormat3_GetTextAlignment(IDWriteTextFormat3* This) { + return This->lpVtbl->GetTextAlignment(This); +} +static FORCEINLINE DWRITE_PARAGRAPH_ALIGNMENT IDWriteTextFormat3_GetParagraphAlignment(IDWriteTextFormat3* This) { + return This->lpVtbl->GetParagraphAlignment(This); +} +static FORCEINLINE DWRITE_WORD_WRAPPING IDWriteTextFormat3_GetWordWrapping(IDWriteTextFormat3* This) { + return This->lpVtbl->GetWordWrapping(This); +} +static FORCEINLINE DWRITE_READING_DIRECTION IDWriteTextFormat3_GetReadingDirection(IDWriteTextFormat3* This) { + return This->lpVtbl->GetReadingDirection(This); +} +static FORCEINLINE DWRITE_FLOW_DIRECTION IDWriteTextFormat3_GetFlowDirection(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFlowDirection(This); +} +static FORCEINLINE FLOAT IDWriteTextFormat3_GetIncrementalTabStop(IDWriteTextFormat3* This) { + return This->lpVtbl->GetIncrementalTabStop(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetTrimming(IDWriteTextFormat3* This,DWRITE_TRIMMING *options,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->GetTrimming(This,options,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetFontCollection(IDWriteTextFormat3* This,IDWriteFontCollection **collection) { + return This->lpVtbl->GetFontCollection(This,collection); +} +static FORCEINLINE UINT32 IDWriteTextFormat3_GetFontFamilyNameLength(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontFamilyNameLength(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetFontFamilyName(IDWriteTextFormat3* This,WCHAR *name,UINT32 size) { + return This->lpVtbl->GetFontFamilyName(This,name,size); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteTextFormat3_GetFontWeight(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontWeight(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteTextFormat3_GetFontStyle(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontStyle(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteTextFormat3_GetFontStretch(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontStretch(This); +} +static FORCEINLINE FLOAT IDWriteTextFormat3_GetFontSize(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontSize(This); +} +static FORCEINLINE UINT32 IDWriteTextFormat3_GetLocaleNameLength(IDWriteTextFormat3* This) { + return This->lpVtbl->GetLocaleNameLength(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetLocaleName(IDWriteTextFormat3* This,WCHAR *name,UINT32 size) { + return This->lpVtbl->GetLocaleName(This,name,size); +} +/*** IDWriteTextFormat1 methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat3_SetVerticalGlyphOrientation(IDWriteTextFormat3* This,DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) { + return This->lpVtbl->SetVerticalGlyphOrientation(This,orientation); +} +static FORCEINLINE DWRITE_VERTICAL_GLYPH_ORIENTATION IDWriteTextFormat3_GetVerticalGlyphOrientation(IDWriteTextFormat3* This) { + return This->lpVtbl->GetVerticalGlyphOrientation(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetLastLineWrapping(IDWriteTextFormat3* This,WINBOOL lastline_wrapping_enabled) { + return This->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled); +} +static FORCEINLINE WINBOOL IDWriteTextFormat3_GetLastLineWrapping(IDWriteTextFormat3* This) { + return This->lpVtbl->GetLastLineWrapping(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetOpticalAlignment(IDWriteTextFormat3* This,DWRITE_OPTICAL_ALIGNMENT alignment) { + return This->lpVtbl->SetOpticalAlignment(This,alignment); +} +static FORCEINLINE DWRITE_OPTICAL_ALIGNMENT IDWriteTextFormat3_GetOpticalAlignment(IDWriteTextFormat3* This) { + return This->lpVtbl->GetOpticalAlignment(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetFontFallback(IDWriteTextFormat3* This,IDWriteFontFallback *fallback) { + return This->lpVtbl->SetFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetFontFallback(IDWriteTextFormat3* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetFontFallback(This,fallback); +} +/*** IDWriteTextFormat2 methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat3_SetLineSpacing(IDWriteTextFormat3* This,const DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextFormat2_SetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetLineSpacing(IDWriteTextFormat3* This,DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextFormat2_GetLineSpacing(This,spacing); +} +/*** IDWriteTextFormat3 methods ***/ +static FORCEINLINE HRESULT IDWriteTextFormat3_SetFontAxisValues(IDWriteTextFormat3* This,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values) { + return This->lpVtbl->SetFontAxisValues(This,axis_values,num_values); +} +static FORCEINLINE UINT32 IDWriteTextFormat3_GetFontAxisValueCount(IDWriteTextFormat3* This) { + return This->lpVtbl->GetFontAxisValueCount(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_GetFontAxisValues(IDWriteTextFormat3* This,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values) { + return This->lpVtbl->GetFontAxisValues(This,axis_values,num_values); +} +static FORCEINLINE DWRITE_AUTOMATIC_FONT_AXES IDWriteTextFormat3_GetAutomaticFontAxes(IDWriteTextFormat3* This) { + return This->lpVtbl->GetAutomaticFontAxes(This); +} +static FORCEINLINE HRESULT IDWriteTextFormat3_SetAutomaticFontAxes(IDWriteTextFormat3* This,DWRITE_AUTOMATIC_FONT_AXES axes) { + return This->lpVtbl->SetAutomaticFontAxes(This,axes); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteTextFormat3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteTextLayout3 interface + */ +#ifndef __IDWriteTextLayout3_INTERFACE_DEFINED__ +#define __IDWriteTextLayout3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteTextLayout3, 0x07ddcd52, 0x020e, 0x4de8, 0xac,0x33, 0x6c,0x95,0x3d,0x83,0xf9,0x2d); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("07ddcd52-020e-4de8-ac33-6c953d83f92d") +IDWriteTextLayout3 : public IDWriteTextLayout2 +{ + virtual HRESULT STDMETHODCALLTYPE InvalidateLayout( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLineSpacing( + const DWRITE_LINE_SPACING *spacing) = 0; + + using IDWriteTextLayout2::SetLineSpacing; + + virtual HRESULT STDMETHODCALLTYPE GetLineSpacing( + DWRITE_LINE_SPACING *spacing) = 0; + + using IDWriteTextLayout2::GetLineSpacing; + + virtual HRESULT STDMETHODCALLTYPE GetLineMetrics( + DWRITE_LINE_METRICS1 *metrics, + UINT32 max_count, + UINT32 *count) = 0; + + using IDWriteTextLayout2::GetLineMetrics; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteTextLayout3, 0x07ddcd52, 0x020e, 0x4de8, 0xac,0x33, 0x6c,0x95,0x3d,0x83,0xf9,0x2d) +#endif +#else +typedef struct IDWriteTextLayout3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteTextLayout3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteTextLayout3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteTextLayout3 *This); + + /*** IDWriteTextFormat methods ***/ + HRESULT (STDMETHODCALLTYPE *SetTextAlignment)( + IDWriteTextLayout3 *This, + DWRITE_TEXT_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetParagraphAlignment)( + IDWriteTextLayout3 *This, + DWRITE_PARAGRAPH_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetWordWrapping)( + IDWriteTextLayout3 *This, + DWRITE_WORD_WRAPPING wrapping); + + HRESULT (STDMETHODCALLTYPE *SetReadingDirection)( + IDWriteTextLayout3 *This, + DWRITE_READING_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetFlowDirection)( + IDWriteTextLayout3 *This, + DWRITE_FLOW_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetIncrementalTabStop)( + IDWriteTextLayout3 *This, + FLOAT tabstop); + + HRESULT (STDMETHODCALLTYPE *SetTrimming)( + IDWriteTextLayout3 *This, + const DWRITE_TRIMMING *trimming, + IDWriteInlineObject *trimming_sign); + + HRESULT (STDMETHODCALLTYPE *SetLineSpacing)( + IDWriteTextLayout3 *This, + DWRITE_LINE_SPACING_METHOD spacing, + FLOAT line_spacing, + FLOAT baseline); + + DWRITE_TEXT_ALIGNMENT (STDMETHODCALLTYPE *GetTextAlignment)( + IDWriteTextLayout3 *This); + + DWRITE_PARAGRAPH_ALIGNMENT (STDMETHODCALLTYPE *GetParagraphAlignment)( + IDWriteTextLayout3 *This); + + DWRITE_WORD_WRAPPING (STDMETHODCALLTYPE *GetWordWrapping)( + IDWriteTextLayout3 *This); + + DWRITE_READING_DIRECTION (STDMETHODCALLTYPE *GetReadingDirection)( + IDWriteTextLayout3 *This); + + DWRITE_FLOW_DIRECTION (STDMETHODCALLTYPE *GetFlowDirection)( + IDWriteTextLayout3 *This); + + FLOAT (STDMETHODCALLTYPE *GetIncrementalTabStop)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *GetTrimming)( + IDWriteTextLayout3 *This, + DWRITE_TRIMMING *options, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *GetLineSpacing)( + IDWriteTextLayout3 *This, + DWRITE_LINE_SPACING_METHOD *method, + FLOAT *spacing, + FLOAT *baseline); + + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteTextLayout3 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontFamilyNameLength)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamilyName)( + IDWriteTextLayout3 *This, + WCHAR *name, + UINT32 size); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetFontWeight)( + IDWriteTextLayout3 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetFontStyle)( + IDWriteTextLayout3 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetFontStretch)( + IDWriteTextLayout3 *This); + + FLOAT (STDMETHODCALLTYPE *GetFontSize)( + IDWriteTextLayout3 *This); + + UINT32 (STDMETHODCALLTYPE *GetLocaleNameLength)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *GetLocaleName)( + IDWriteTextLayout3 *This, + WCHAR *name, + UINT32 size); + + /*** IDWriteTextLayout methods ***/ + HRESULT (STDMETHODCALLTYPE *SetMaxWidth)( + IDWriteTextLayout3 *This, + FLOAT maxWidth); + + HRESULT (STDMETHODCALLTYPE *SetMaxHeight)( + IDWriteTextLayout3 *This, + FLOAT maxHeight); + + HRESULT (STDMETHODCALLTYPE *SetFontCollection)( + IDWriteTextLayout3 *This, + IDWriteFontCollection *collection, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontFamilyName)( + IDWriteTextLayout3 *This, + const WCHAR *name, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontWeight)( + IDWriteTextLayout3 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontStyle)( + IDWriteTextLayout3 *This, + DWRITE_FONT_STYLE style, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontStretch)( + IDWriteTextLayout3 *This, + DWRITE_FONT_STRETCH stretch, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontSize)( + IDWriteTextLayout3 *This, + FLOAT size, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetUnderline)( + IDWriteTextLayout3 *This, + WINBOOL underline, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetStrikethrough)( + IDWriteTextLayout3 *This, + WINBOOL strikethrough, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetDrawingEffect)( + IDWriteTextLayout3 *This, + IUnknown *effect, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetInlineObject)( + IDWriteTextLayout3 *This, + IDWriteInlineObject *object, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetTypography)( + IDWriteTextLayout3 *This, + IDWriteTypography *typography, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetLocaleName)( + IDWriteTextLayout3 *This, + const WCHAR *locale, + DWRITE_TEXT_RANGE range); + + FLOAT (STDMETHODCALLTYPE *GetMaxWidth)( + IDWriteTextLayout3 *This); + + FLOAT (STDMETHODCALLTYPE *GetMaxHeight)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontCollection)( + IDWriteTextLayout3 *This, + UINT32 pos, + IDWriteFontCollection **collection, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontFamilyNameLength)( + IDWriteTextLayout3 *This, + UINT32 pos, + UINT32 *len, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontFamilyName)( + IDWriteTextLayout3 *This, + UINT32 position, + WCHAR *name, + UINT32 name_size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontWeight)( + IDWriteTextLayout3 *This, + UINT32 position, + DWRITE_FONT_WEIGHT *weight, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontStyle)( + IDWriteTextLayout3 *This, + UINT32 currentPosition, + DWRITE_FONT_STYLE *style, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontStretch)( + IDWriteTextLayout3 *This, + UINT32 position, + DWRITE_FONT_STRETCH *stretch, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontSize)( + IDWriteTextLayout3 *This, + UINT32 position, + FLOAT *size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetUnderline)( + IDWriteTextLayout3 *This, + UINT32 position, + WINBOOL *has_underline, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetStrikethrough)( + IDWriteTextLayout3 *This, + UINT32 position, + WINBOOL *has_strikethrough, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetDrawingEffect)( + IDWriteTextLayout3 *This, + UINT32 position, + IUnknown **effect, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetInlineObject)( + IDWriteTextLayout3 *This, + UINT32 position, + IDWriteInlineObject **object, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetTypography)( + IDWriteTextLayout3 *This, + UINT32 position, + IDWriteTypography **typography, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetLocaleNameLength)( + IDWriteTextLayout3 *This, + UINT32 position, + UINT32 *length, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetLocaleName)( + IDWriteTextLayout3 *This, + UINT32 position, + WCHAR *name, + UINT32 name_size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *Draw)( + IDWriteTextLayout3 *This, + void *context, + IDWriteTextRenderer *renderer, + FLOAT originX, + FLOAT originY); + + HRESULT (STDMETHODCALLTYPE *GetLineMetrics)( + IDWriteTextLayout3 *This, + DWRITE_LINE_METRICS *metrics, + UINT32 max_count, + UINT32 *actual_count); + + HRESULT (STDMETHODCALLTYPE *GetMetrics)( + IDWriteTextLayout3 *This, + DWRITE_TEXT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetOverhangMetrics)( + IDWriteTextLayout3 *This, + DWRITE_OVERHANG_METRICS *overhangs); + + HRESULT (STDMETHODCALLTYPE *GetClusterMetrics)( + IDWriteTextLayout3 *This, + DWRITE_CLUSTER_METRICS *metrics, + UINT32 max_count, + UINT32 *act_count); + + HRESULT (STDMETHODCALLTYPE *DetermineMinWidth)( + IDWriteTextLayout3 *This, + FLOAT *min_width); + + HRESULT (STDMETHODCALLTYPE *HitTestPoint)( + IDWriteTextLayout3 *This, + FLOAT pointX, + FLOAT pointY, + WINBOOL *is_trailinghit, + WINBOOL *is_inside, + DWRITE_HIT_TEST_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *HitTestTextPosition)( + IDWriteTextLayout3 *This, + UINT32 textPosition, + WINBOOL is_trailinghit, + FLOAT *pointX, + FLOAT *pointY, + DWRITE_HIT_TEST_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *HitTestTextRange)( + IDWriteTextLayout3 *This, + UINT32 textPosition, + UINT32 textLength, + FLOAT originX, + FLOAT originY, + DWRITE_HIT_TEST_METRICS *metrics, + UINT32 max_metricscount, + UINT32 *actual_metricscount); + + /*** IDWriteTextLayout1 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetPairKerning)( + IDWriteTextLayout3 *This, + WINBOOL is_pairkerning_enabled, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *GetPairKerning)( + IDWriteTextLayout3 *This, + UINT32 position, + WINBOOL *is_pairkerning_enabled, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *SetCharacterSpacing)( + IDWriteTextLayout3 *This, + FLOAT leading_spacing, + FLOAT trailing_spacing, + FLOAT minimum_advance_width, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *GetCharacterSpacing)( + IDWriteTextLayout3 *This, + UINT32 position, + FLOAT *leading_spacing, + FLOAT *trailing_spacing, + FLOAT *minimum_advance_width, + DWRITE_TEXT_RANGE *range); + + /*** IDWriteTextLayout2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout2_GetMetrics)( + IDWriteTextLayout3 *This, + DWRITE_TEXT_METRICS1 *metrics); + + HRESULT (STDMETHODCALLTYPE *SetVerticalGlyphOrientation)( + IDWriteTextLayout3 *This, + DWRITE_VERTICAL_GLYPH_ORIENTATION orientation); + + DWRITE_VERTICAL_GLYPH_ORIENTATION (STDMETHODCALLTYPE *GetVerticalGlyphOrientation)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *SetLastLineWrapping)( + IDWriteTextLayout3 *This, + WINBOOL lastline_wrapping_enabled); + + WINBOOL (STDMETHODCALLTYPE *GetLastLineWrapping)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *SetOpticalAlignment)( + IDWriteTextLayout3 *This, + DWRITE_OPTICAL_ALIGNMENT alignment); + + DWRITE_OPTICAL_ALIGNMENT (STDMETHODCALLTYPE *GetOpticalAlignment)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *SetFontFallback)( + IDWriteTextLayout3 *This, + IDWriteFontFallback *fallback); + + HRESULT (STDMETHODCALLTYPE *GetFontFallback)( + IDWriteTextLayout3 *This, + IDWriteFontFallback **fallback); + + /*** IDWriteTextLayout3 methods ***/ + HRESULT (STDMETHODCALLTYPE *InvalidateLayout)( + IDWriteTextLayout3 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_SetLineSpacing)( + IDWriteTextLayout3 *This, + const DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_GetLineSpacing)( + IDWriteTextLayout3 *This, + DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_GetLineMetrics)( + IDWriteTextLayout3 *This, + DWRITE_LINE_METRICS1 *metrics, + UINT32 max_count, + UINT32 *count); + + END_INTERFACE +} IDWriteTextLayout3Vtbl; + +interface IDWriteTextLayout3 { + CONST_VTBL IDWriteTextLayout3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteTextLayout3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteTextLayout3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteTextLayout3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteTextFormat methods ***/ +#define IDWriteTextLayout3_SetTextAlignment(This,alignment) (This)->lpVtbl->SetTextAlignment(This,alignment) +#define IDWriteTextLayout3_SetParagraphAlignment(This,alignment) (This)->lpVtbl->SetParagraphAlignment(This,alignment) +#define IDWriteTextLayout3_SetWordWrapping(This,wrapping) (This)->lpVtbl->SetWordWrapping(This,wrapping) +#define IDWriteTextLayout3_SetReadingDirection(This,direction) (This)->lpVtbl->SetReadingDirection(This,direction) +#define IDWriteTextLayout3_SetFlowDirection(This,direction) (This)->lpVtbl->SetFlowDirection(This,direction) +#define IDWriteTextLayout3_SetIncrementalTabStop(This,tabstop) (This)->lpVtbl->SetIncrementalTabStop(This,tabstop) +#define IDWriteTextLayout3_SetTrimming(This,trimming,trimming_sign) (This)->lpVtbl->SetTrimming(This,trimming,trimming_sign) +#define IDWriteTextLayout3_GetTextAlignment(This) (This)->lpVtbl->GetTextAlignment(This) +#define IDWriteTextLayout3_GetParagraphAlignment(This) (This)->lpVtbl->GetParagraphAlignment(This) +#define IDWriteTextLayout3_GetWordWrapping(This) (This)->lpVtbl->GetWordWrapping(This) +#define IDWriteTextLayout3_GetReadingDirection(This) (This)->lpVtbl->GetReadingDirection(This) +#define IDWriteTextLayout3_GetFlowDirection(This) (This)->lpVtbl->GetFlowDirection(This) +#define IDWriteTextLayout3_GetIncrementalTabStop(This) (This)->lpVtbl->GetIncrementalTabStop(This) +#define IDWriteTextLayout3_GetTrimming(This,options,trimming_sign) (This)->lpVtbl->GetTrimming(This,options,trimming_sign) +/*** IDWriteTextLayout methods ***/ +#define IDWriteTextLayout3_SetMaxWidth(This,maxWidth) (This)->lpVtbl->SetMaxWidth(This,maxWidth) +#define IDWriteTextLayout3_SetMaxHeight(This,maxHeight) (This)->lpVtbl->SetMaxHeight(This,maxHeight) +#define IDWriteTextLayout3_SetFontCollection(This,collection,range) (This)->lpVtbl->SetFontCollection(This,collection,range) +#define IDWriteTextLayout3_SetFontFamilyName(This,name,range) (This)->lpVtbl->SetFontFamilyName(This,name,range) +#define IDWriteTextLayout3_SetFontWeight(This,weight,range) (This)->lpVtbl->SetFontWeight(This,weight,range) +#define IDWriteTextLayout3_SetFontStyle(This,style,range) (This)->lpVtbl->SetFontStyle(This,style,range) +#define IDWriteTextLayout3_SetFontStretch(This,stretch,range) (This)->lpVtbl->SetFontStretch(This,stretch,range) +#define IDWriteTextLayout3_SetFontSize(This,size,range) (This)->lpVtbl->SetFontSize(This,size,range) +#define IDWriteTextLayout3_SetUnderline(This,underline,range) (This)->lpVtbl->SetUnderline(This,underline,range) +#define IDWriteTextLayout3_SetStrikethrough(This,strikethrough,range) (This)->lpVtbl->SetStrikethrough(This,strikethrough,range) +#define IDWriteTextLayout3_SetDrawingEffect(This,effect,range) (This)->lpVtbl->SetDrawingEffect(This,effect,range) +#define IDWriteTextLayout3_SetInlineObject(This,object,range) (This)->lpVtbl->SetInlineObject(This,object,range) +#define IDWriteTextLayout3_SetTypography(This,typography,range) (This)->lpVtbl->SetTypography(This,typography,range) +#define IDWriteTextLayout3_SetLocaleName(This,locale,range) (This)->lpVtbl->SetLocaleName(This,locale,range) +#define IDWriteTextLayout3_GetMaxWidth(This) (This)->lpVtbl->GetMaxWidth(This) +#define IDWriteTextLayout3_GetMaxHeight(This) (This)->lpVtbl->GetMaxHeight(This) +#define IDWriteTextLayout3_GetFontCollection(This,pos,collection,range) (This)->lpVtbl->IDWriteTextLayout_GetFontCollection(This,pos,collection,range) +#define IDWriteTextLayout3_GetFontFamilyNameLength(This,pos,len,range) (This)->lpVtbl->IDWriteTextLayout_GetFontFamilyNameLength(This,pos,len,range) +#define IDWriteTextLayout3_GetFontFamilyName(This,position,name,name_size,range) (This)->lpVtbl->IDWriteTextLayout_GetFontFamilyName(This,position,name,name_size,range) +#define IDWriteTextLayout3_GetFontWeight(This,position,weight,range) (This)->lpVtbl->IDWriteTextLayout_GetFontWeight(This,position,weight,range) +#define IDWriteTextLayout3_GetFontStyle(This,currentPosition,style,range) (This)->lpVtbl->IDWriteTextLayout_GetFontStyle(This,currentPosition,style,range) +#define IDWriteTextLayout3_GetFontStretch(This,position,stretch,range) (This)->lpVtbl->IDWriteTextLayout_GetFontStretch(This,position,stretch,range) +#define IDWriteTextLayout3_GetFontSize(This,position,size,range) (This)->lpVtbl->IDWriteTextLayout_GetFontSize(This,position,size,range) +#define IDWriteTextLayout3_GetUnderline(This,position,has_underline,range) (This)->lpVtbl->GetUnderline(This,position,has_underline,range) +#define IDWriteTextLayout3_GetStrikethrough(This,position,has_strikethrough,range) (This)->lpVtbl->GetStrikethrough(This,position,has_strikethrough,range) +#define IDWriteTextLayout3_GetDrawingEffect(This,position,effect,range) (This)->lpVtbl->GetDrawingEffect(This,position,effect,range) +#define IDWriteTextLayout3_GetInlineObject(This,position,object,range) (This)->lpVtbl->GetInlineObject(This,position,object,range) +#define IDWriteTextLayout3_GetTypography(This,position,typography,range) (This)->lpVtbl->GetTypography(This,position,typography,range) +#define IDWriteTextLayout3_GetLocaleNameLength(This,position,length,range) (This)->lpVtbl->IDWriteTextLayout_GetLocaleNameLength(This,position,length,range) +#define IDWriteTextLayout3_GetLocaleName(This,position,name,name_size,range) (This)->lpVtbl->IDWriteTextLayout_GetLocaleName(This,position,name,name_size,range) +#define IDWriteTextLayout3_Draw(This,context,renderer,originX,originY) (This)->lpVtbl->Draw(This,context,renderer,originX,originY) +#define IDWriteTextLayout3_GetOverhangMetrics(This,overhangs) (This)->lpVtbl->GetOverhangMetrics(This,overhangs) +#define IDWriteTextLayout3_GetClusterMetrics(This,metrics,max_count,act_count) (This)->lpVtbl->GetClusterMetrics(This,metrics,max_count,act_count) +#define IDWriteTextLayout3_DetermineMinWidth(This,min_width) (This)->lpVtbl->DetermineMinWidth(This,min_width) +#define IDWriteTextLayout3_HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics) (This)->lpVtbl->HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics) +#define IDWriteTextLayout3_HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics) (This)->lpVtbl->HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics) +#define IDWriteTextLayout3_HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount) (This)->lpVtbl->HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount) +/*** IDWriteTextLayout1 methods ***/ +#define IDWriteTextLayout3_SetPairKerning(This,is_pairkerning_enabled,range) (This)->lpVtbl->SetPairKerning(This,is_pairkerning_enabled,range) +#define IDWriteTextLayout3_GetPairKerning(This,position,is_pairkerning_enabled,range) (This)->lpVtbl->GetPairKerning(This,position,is_pairkerning_enabled,range) +#define IDWriteTextLayout3_SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range) (This)->lpVtbl->SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range) +#define IDWriteTextLayout3_GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range) (This)->lpVtbl->GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range) +/*** IDWriteTextLayout2 methods ***/ +#define IDWriteTextLayout3_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteTextLayout2_GetMetrics(This,metrics) +#define IDWriteTextLayout3_SetVerticalGlyphOrientation(This,orientation) (This)->lpVtbl->SetVerticalGlyphOrientation(This,orientation) +#define IDWriteTextLayout3_GetVerticalGlyphOrientation(This) (This)->lpVtbl->GetVerticalGlyphOrientation(This) +#define IDWriteTextLayout3_SetLastLineWrapping(This,lastline_wrapping_enabled) (This)->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled) +#define IDWriteTextLayout3_GetLastLineWrapping(This) (This)->lpVtbl->GetLastLineWrapping(This) +#define IDWriteTextLayout3_SetOpticalAlignment(This,alignment) (This)->lpVtbl->SetOpticalAlignment(This,alignment) +#define IDWriteTextLayout3_GetOpticalAlignment(This) (This)->lpVtbl->GetOpticalAlignment(This) +#define IDWriteTextLayout3_SetFontFallback(This,fallback) (This)->lpVtbl->SetFontFallback(This,fallback) +#define IDWriteTextLayout3_GetFontFallback(This,fallback) (This)->lpVtbl->GetFontFallback(This,fallback) +/*** IDWriteTextLayout3 methods ***/ +#define IDWriteTextLayout3_InvalidateLayout(This) (This)->lpVtbl->InvalidateLayout(This) +#define IDWriteTextLayout3_SetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextLayout3_SetLineSpacing(This,spacing) +#define IDWriteTextLayout3_GetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextLayout3_GetLineSpacing(This,spacing) +#define IDWriteTextLayout3_GetLineMetrics(This,metrics,max_count,count) (This)->lpVtbl->IDWriteTextLayout3_GetLineMetrics(This,metrics,max_count,count) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_QueryInterface(IDWriteTextLayout3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteTextLayout3_AddRef(IDWriteTextLayout3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteTextLayout3_Release(IDWriteTextLayout3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteTextFormat methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_SetTextAlignment(IDWriteTextLayout3* This,DWRITE_TEXT_ALIGNMENT alignment) { + return This->lpVtbl->SetTextAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetParagraphAlignment(IDWriteTextLayout3* This,DWRITE_PARAGRAPH_ALIGNMENT alignment) { + return This->lpVtbl->SetParagraphAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetWordWrapping(IDWriteTextLayout3* This,DWRITE_WORD_WRAPPING wrapping) { + return This->lpVtbl->SetWordWrapping(This,wrapping); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetReadingDirection(IDWriteTextLayout3* This,DWRITE_READING_DIRECTION direction) { + return This->lpVtbl->SetReadingDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFlowDirection(IDWriteTextLayout3* This,DWRITE_FLOW_DIRECTION direction) { + return This->lpVtbl->SetFlowDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetIncrementalTabStop(IDWriteTextLayout3* This,FLOAT tabstop) { + return This->lpVtbl->SetIncrementalTabStop(This,tabstop); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetTrimming(IDWriteTextLayout3* This,const DWRITE_TRIMMING *trimming,IDWriteInlineObject *trimming_sign) { + return This->lpVtbl->SetTrimming(This,trimming,trimming_sign); +} +static FORCEINLINE DWRITE_TEXT_ALIGNMENT IDWriteTextLayout3_GetTextAlignment(IDWriteTextLayout3* This) { + return This->lpVtbl->GetTextAlignment(This); +} +static FORCEINLINE DWRITE_PARAGRAPH_ALIGNMENT IDWriteTextLayout3_GetParagraphAlignment(IDWriteTextLayout3* This) { + return This->lpVtbl->GetParagraphAlignment(This); +} +static FORCEINLINE DWRITE_WORD_WRAPPING IDWriteTextLayout3_GetWordWrapping(IDWriteTextLayout3* This) { + return This->lpVtbl->GetWordWrapping(This); +} +static FORCEINLINE DWRITE_READING_DIRECTION IDWriteTextLayout3_GetReadingDirection(IDWriteTextLayout3* This) { + return This->lpVtbl->GetReadingDirection(This); +} +static FORCEINLINE DWRITE_FLOW_DIRECTION IDWriteTextLayout3_GetFlowDirection(IDWriteTextLayout3* This) { + return This->lpVtbl->GetFlowDirection(This); +} +static FORCEINLINE FLOAT IDWriteTextLayout3_GetIncrementalTabStop(IDWriteTextLayout3* This) { + return This->lpVtbl->GetIncrementalTabStop(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetTrimming(IDWriteTextLayout3* This,DWRITE_TRIMMING *options,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->GetTrimming(This,options,trimming_sign); +} +/*** IDWriteTextLayout methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_SetMaxWidth(IDWriteTextLayout3* This,FLOAT maxWidth) { + return This->lpVtbl->SetMaxWidth(This,maxWidth); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetMaxHeight(IDWriteTextLayout3* This,FLOAT maxHeight) { + return This->lpVtbl->SetMaxHeight(This,maxHeight); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontCollection(IDWriteTextLayout3* This,IDWriteFontCollection *collection,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontCollection(This,collection,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontFamilyName(IDWriteTextLayout3* This,const WCHAR *name,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontFamilyName(This,name,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontWeight(IDWriteTextLayout3* This,DWRITE_FONT_WEIGHT weight,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontWeight(This,weight,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontStyle(IDWriteTextLayout3* This,DWRITE_FONT_STYLE style,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontStyle(This,style,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontStretch(IDWriteTextLayout3* This,DWRITE_FONT_STRETCH stretch,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontStretch(This,stretch,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontSize(IDWriteTextLayout3* This,FLOAT size,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontSize(This,size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetUnderline(IDWriteTextLayout3* This,WINBOOL underline,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetUnderline(This,underline,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetStrikethrough(IDWriteTextLayout3* This,WINBOOL strikethrough,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetStrikethrough(This,strikethrough,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetDrawingEffect(IDWriteTextLayout3* This,IUnknown *effect,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetDrawingEffect(This,effect,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetInlineObject(IDWriteTextLayout3* This,IDWriteInlineObject *object,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetInlineObject(This,object,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetTypography(IDWriteTextLayout3* This,IDWriteTypography *typography,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetTypography(This,typography,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetLocaleName(IDWriteTextLayout3* This,const WCHAR *locale,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetLocaleName(This,locale,range); +} +static FORCEINLINE FLOAT IDWriteTextLayout3_GetMaxWidth(IDWriteTextLayout3* This) { + return This->lpVtbl->GetMaxWidth(This); +} +static FORCEINLINE FLOAT IDWriteTextLayout3_GetMaxHeight(IDWriteTextLayout3* This) { + return This->lpVtbl->GetMaxHeight(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontCollection(IDWriteTextLayout3* This,UINT32 pos,IDWriteFontCollection **collection,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontCollection(This,pos,collection,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontFamilyNameLength(IDWriteTextLayout3* This,UINT32 pos,UINT32 *len,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontFamilyNameLength(This,pos,len,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontFamilyName(IDWriteTextLayout3* This,UINT32 position,WCHAR *name,UINT32 name_size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontFamilyName(This,position,name,name_size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontWeight(IDWriteTextLayout3* This,UINT32 position,DWRITE_FONT_WEIGHT *weight,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontWeight(This,position,weight,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontStyle(IDWriteTextLayout3* This,UINT32 currentPosition,DWRITE_FONT_STYLE *style,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontStyle(This,currentPosition,style,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontStretch(IDWriteTextLayout3* This,UINT32 position,DWRITE_FONT_STRETCH *stretch,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontStretch(This,position,stretch,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontSize(IDWriteTextLayout3* This,UINT32 position,FLOAT *size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontSize(This,position,size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetUnderline(IDWriteTextLayout3* This,UINT32 position,WINBOOL *has_underline,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetUnderline(This,position,has_underline,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetStrikethrough(IDWriteTextLayout3* This,UINT32 position,WINBOOL *has_strikethrough,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetStrikethrough(This,position,has_strikethrough,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetDrawingEffect(IDWriteTextLayout3* This,UINT32 position,IUnknown **effect,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetDrawingEffect(This,position,effect,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetInlineObject(IDWriteTextLayout3* This,UINT32 position,IDWriteInlineObject **object,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetInlineObject(This,position,object,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetTypography(IDWriteTextLayout3* This,UINT32 position,IDWriteTypography **typography,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetTypography(This,position,typography,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetLocaleNameLength(IDWriteTextLayout3* This,UINT32 position,UINT32 *length,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetLocaleNameLength(This,position,length,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetLocaleName(IDWriteTextLayout3* This,UINT32 position,WCHAR *name,UINT32 name_size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetLocaleName(This,position,name,name_size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_Draw(IDWriteTextLayout3* This,void *context,IDWriteTextRenderer *renderer,FLOAT originX,FLOAT originY) { + return This->lpVtbl->Draw(This,context,renderer,originX,originY); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetOverhangMetrics(IDWriteTextLayout3* This,DWRITE_OVERHANG_METRICS *overhangs) { + return This->lpVtbl->GetOverhangMetrics(This,overhangs); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetClusterMetrics(IDWriteTextLayout3* This,DWRITE_CLUSTER_METRICS *metrics,UINT32 max_count,UINT32 *act_count) { + return This->lpVtbl->GetClusterMetrics(This,metrics,max_count,act_count); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_DetermineMinWidth(IDWriteTextLayout3* This,FLOAT *min_width) { + return This->lpVtbl->DetermineMinWidth(This,min_width); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_HitTestPoint(IDWriteTextLayout3* This,FLOAT pointX,FLOAT pointY,WINBOOL *is_trailinghit,WINBOOL *is_inside,DWRITE_HIT_TEST_METRICS *metrics) { + return This->lpVtbl->HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_HitTestTextPosition(IDWriteTextLayout3* This,UINT32 textPosition,WINBOOL is_trailinghit,FLOAT *pointX,FLOAT *pointY,DWRITE_HIT_TEST_METRICS *metrics) { + return This->lpVtbl->HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_HitTestTextRange(IDWriteTextLayout3* This,UINT32 textPosition,UINT32 textLength,FLOAT originX,FLOAT originY,DWRITE_HIT_TEST_METRICS *metrics,UINT32 max_metricscount,UINT32 *actual_metricscount) { + return This->lpVtbl->HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount); +} +/*** IDWriteTextLayout1 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_SetPairKerning(IDWriteTextLayout3* This,WINBOOL is_pairkerning_enabled,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetPairKerning(This,is_pairkerning_enabled,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetPairKerning(IDWriteTextLayout3* This,UINT32 position,WINBOOL *is_pairkerning_enabled,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetPairKerning(This,position,is_pairkerning_enabled,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetCharacterSpacing(IDWriteTextLayout3* This,FLOAT leading_spacing,FLOAT trailing_spacing,FLOAT minimum_advance_width,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetCharacterSpacing(IDWriteTextLayout3* This,UINT32 position,FLOAT *leading_spacing,FLOAT *trailing_spacing,FLOAT *minimum_advance_width,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range); +} +/*** IDWriteTextLayout2 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_GetMetrics(IDWriteTextLayout3* This,DWRITE_TEXT_METRICS1 *metrics) { + return This->lpVtbl->IDWriteTextLayout2_GetMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetVerticalGlyphOrientation(IDWriteTextLayout3* This,DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) { + return This->lpVtbl->SetVerticalGlyphOrientation(This,orientation); +} +static FORCEINLINE DWRITE_VERTICAL_GLYPH_ORIENTATION IDWriteTextLayout3_GetVerticalGlyphOrientation(IDWriteTextLayout3* This) { + return This->lpVtbl->GetVerticalGlyphOrientation(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetLastLineWrapping(IDWriteTextLayout3* This,WINBOOL lastline_wrapping_enabled) { + return This->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled); +} +static FORCEINLINE WINBOOL IDWriteTextLayout3_GetLastLineWrapping(IDWriteTextLayout3* This) { + return This->lpVtbl->GetLastLineWrapping(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetOpticalAlignment(IDWriteTextLayout3* This,DWRITE_OPTICAL_ALIGNMENT alignment) { + return This->lpVtbl->SetOpticalAlignment(This,alignment); +} +static FORCEINLINE DWRITE_OPTICAL_ALIGNMENT IDWriteTextLayout3_GetOpticalAlignment(IDWriteTextLayout3* This) { + return This->lpVtbl->GetOpticalAlignment(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetFontFallback(IDWriteTextLayout3* This,IDWriteFontFallback *fallback) { + return This->lpVtbl->SetFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetFontFallback(IDWriteTextLayout3* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetFontFallback(This,fallback); +} +/*** IDWriteTextLayout3 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout3_InvalidateLayout(IDWriteTextLayout3* This) { + return This->lpVtbl->InvalidateLayout(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_SetLineSpacing(IDWriteTextLayout3* This,const DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextLayout3_SetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetLineSpacing(IDWriteTextLayout3* This,DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextLayout3_GetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextLayout3_GetLineMetrics(IDWriteTextLayout3* This,DWRITE_LINE_METRICS1 *metrics,UINT32 max_count,UINT32 *count) { + return This->lpVtbl->IDWriteTextLayout3_GetLineMetrics(This,metrics,max_count,count); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteTextLayout3_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteTextLayout4 interface + */ +#ifndef __IDWriteTextLayout4_INTERFACE_DEFINED__ +#define __IDWriteTextLayout4_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteTextLayout4, 0x05a9bf42, 0x223f, 0x4441, 0xb5,0xfb, 0x82,0x63,0x68,0x5f,0x55,0xe9); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("05a9bf42-223f-4441-b5fb-8263685f55e9") +IDWriteTextLayout4 : public IDWriteTextLayout3 +{ + virtual HRESULT STDMETHODCALLTYPE SetFontAxisValues( + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + DWRITE_TEXT_RANGE range) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFontAxisValueCount( + UINT32 pos) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisValues( + UINT32 pos, + DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values, + DWRITE_TEXT_RANGE *range) = 0; + + virtual DWRITE_AUTOMATIC_FONT_AXES STDMETHODCALLTYPE GetAutomaticFontAxes( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAutomaticFontAxes( + DWRITE_AUTOMATIC_FONT_AXES axes) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteTextLayout4, 0x05a9bf42, 0x223f, 0x4441, 0xb5,0xfb, 0x82,0x63,0x68,0x5f,0x55,0xe9) +#endif +#else +typedef struct IDWriteTextLayout4Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteTextLayout4 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteTextLayout4 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteTextLayout4 *This); + + /*** IDWriteTextFormat methods ***/ + HRESULT (STDMETHODCALLTYPE *SetTextAlignment)( + IDWriteTextLayout4 *This, + DWRITE_TEXT_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetParagraphAlignment)( + IDWriteTextLayout4 *This, + DWRITE_PARAGRAPH_ALIGNMENT alignment); + + HRESULT (STDMETHODCALLTYPE *SetWordWrapping)( + IDWriteTextLayout4 *This, + DWRITE_WORD_WRAPPING wrapping); + + HRESULT (STDMETHODCALLTYPE *SetReadingDirection)( + IDWriteTextLayout4 *This, + DWRITE_READING_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetFlowDirection)( + IDWriteTextLayout4 *This, + DWRITE_FLOW_DIRECTION direction); + + HRESULT (STDMETHODCALLTYPE *SetIncrementalTabStop)( + IDWriteTextLayout4 *This, + FLOAT tabstop); + + HRESULT (STDMETHODCALLTYPE *SetTrimming)( + IDWriteTextLayout4 *This, + const DWRITE_TRIMMING *trimming, + IDWriteInlineObject *trimming_sign); + + HRESULT (STDMETHODCALLTYPE *SetLineSpacing)( + IDWriteTextLayout4 *This, + DWRITE_LINE_SPACING_METHOD spacing, + FLOAT line_spacing, + FLOAT baseline); + + DWRITE_TEXT_ALIGNMENT (STDMETHODCALLTYPE *GetTextAlignment)( + IDWriteTextLayout4 *This); + + DWRITE_PARAGRAPH_ALIGNMENT (STDMETHODCALLTYPE *GetParagraphAlignment)( + IDWriteTextLayout4 *This); + + DWRITE_WORD_WRAPPING (STDMETHODCALLTYPE *GetWordWrapping)( + IDWriteTextLayout4 *This); + + DWRITE_READING_DIRECTION (STDMETHODCALLTYPE *GetReadingDirection)( + IDWriteTextLayout4 *This); + + DWRITE_FLOW_DIRECTION (STDMETHODCALLTYPE *GetFlowDirection)( + IDWriteTextLayout4 *This); + + FLOAT (STDMETHODCALLTYPE *GetIncrementalTabStop)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *GetTrimming)( + IDWriteTextLayout4 *This, + DWRITE_TRIMMING *options, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *GetLineSpacing)( + IDWriteTextLayout4 *This, + DWRITE_LINE_SPACING_METHOD *method, + FLOAT *spacing, + FLOAT *baseline); + + HRESULT (STDMETHODCALLTYPE *GetFontCollection)( + IDWriteTextLayout4 *This, + IDWriteFontCollection **collection); + + UINT32 (STDMETHODCALLTYPE *GetFontFamilyNameLength)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontFamilyName)( + IDWriteTextLayout4 *This, + WCHAR *name, + UINT32 size); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetFontWeight)( + IDWriteTextLayout4 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetFontStyle)( + IDWriteTextLayout4 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetFontStretch)( + IDWriteTextLayout4 *This); + + FLOAT (STDMETHODCALLTYPE *GetFontSize)( + IDWriteTextLayout4 *This); + + UINT32 (STDMETHODCALLTYPE *GetLocaleNameLength)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *GetLocaleName)( + IDWriteTextLayout4 *This, + WCHAR *name, + UINT32 size); + + /*** IDWriteTextLayout methods ***/ + HRESULT (STDMETHODCALLTYPE *SetMaxWidth)( + IDWriteTextLayout4 *This, + FLOAT maxWidth); + + HRESULT (STDMETHODCALLTYPE *SetMaxHeight)( + IDWriteTextLayout4 *This, + FLOAT maxHeight); + + HRESULT (STDMETHODCALLTYPE *SetFontCollection)( + IDWriteTextLayout4 *This, + IDWriteFontCollection *collection, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontFamilyName)( + IDWriteTextLayout4 *This, + const WCHAR *name, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontWeight)( + IDWriteTextLayout4 *This, + DWRITE_FONT_WEIGHT weight, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontStyle)( + IDWriteTextLayout4 *This, + DWRITE_FONT_STYLE style, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontStretch)( + IDWriteTextLayout4 *This, + DWRITE_FONT_STRETCH stretch, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetFontSize)( + IDWriteTextLayout4 *This, + FLOAT size, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetUnderline)( + IDWriteTextLayout4 *This, + WINBOOL underline, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetStrikethrough)( + IDWriteTextLayout4 *This, + WINBOOL strikethrough, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetDrawingEffect)( + IDWriteTextLayout4 *This, + IUnknown *effect, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetInlineObject)( + IDWriteTextLayout4 *This, + IDWriteInlineObject *object, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetTypography)( + IDWriteTextLayout4 *This, + IDWriteTypography *typography, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *SetLocaleName)( + IDWriteTextLayout4 *This, + const WCHAR *locale, + DWRITE_TEXT_RANGE range); + + FLOAT (STDMETHODCALLTYPE *GetMaxWidth)( + IDWriteTextLayout4 *This); + + FLOAT (STDMETHODCALLTYPE *GetMaxHeight)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontCollection)( + IDWriteTextLayout4 *This, + UINT32 pos, + IDWriteFontCollection **collection, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontFamilyNameLength)( + IDWriteTextLayout4 *This, + UINT32 pos, + UINT32 *len, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontFamilyName)( + IDWriteTextLayout4 *This, + UINT32 position, + WCHAR *name, + UINT32 name_size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontWeight)( + IDWriteTextLayout4 *This, + UINT32 position, + DWRITE_FONT_WEIGHT *weight, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontStyle)( + IDWriteTextLayout4 *This, + UINT32 currentPosition, + DWRITE_FONT_STYLE *style, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontStretch)( + IDWriteTextLayout4 *This, + UINT32 position, + DWRITE_FONT_STRETCH *stretch, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetFontSize)( + IDWriteTextLayout4 *This, + UINT32 position, + FLOAT *size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetUnderline)( + IDWriteTextLayout4 *This, + UINT32 position, + WINBOOL *has_underline, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetStrikethrough)( + IDWriteTextLayout4 *This, + UINT32 position, + WINBOOL *has_strikethrough, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetDrawingEffect)( + IDWriteTextLayout4 *This, + UINT32 position, + IUnknown **effect, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetInlineObject)( + IDWriteTextLayout4 *This, + UINT32 position, + IDWriteInlineObject **object, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *GetTypography)( + IDWriteTextLayout4 *This, + UINT32 position, + IDWriteTypography **typography, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetLocaleNameLength)( + IDWriteTextLayout4 *This, + UINT32 position, + UINT32 *length, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout_GetLocaleName)( + IDWriteTextLayout4 *This, + UINT32 position, + WCHAR *name, + UINT32 name_size, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *Draw)( + IDWriteTextLayout4 *This, + void *context, + IDWriteTextRenderer *renderer, + FLOAT originX, + FLOAT originY); + + HRESULT (STDMETHODCALLTYPE *GetLineMetrics)( + IDWriteTextLayout4 *This, + DWRITE_LINE_METRICS *metrics, + UINT32 max_count, + UINT32 *actual_count); + + HRESULT (STDMETHODCALLTYPE *GetMetrics)( + IDWriteTextLayout4 *This, + DWRITE_TEXT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetOverhangMetrics)( + IDWriteTextLayout4 *This, + DWRITE_OVERHANG_METRICS *overhangs); + + HRESULT (STDMETHODCALLTYPE *GetClusterMetrics)( + IDWriteTextLayout4 *This, + DWRITE_CLUSTER_METRICS *metrics, + UINT32 max_count, + UINT32 *act_count); + + HRESULT (STDMETHODCALLTYPE *DetermineMinWidth)( + IDWriteTextLayout4 *This, + FLOAT *min_width); + + HRESULT (STDMETHODCALLTYPE *HitTestPoint)( + IDWriteTextLayout4 *This, + FLOAT pointX, + FLOAT pointY, + WINBOOL *is_trailinghit, + WINBOOL *is_inside, + DWRITE_HIT_TEST_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *HitTestTextPosition)( + IDWriteTextLayout4 *This, + UINT32 textPosition, + WINBOOL is_trailinghit, + FLOAT *pointX, + FLOAT *pointY, + DWRITE_HIT_TEST_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *HitTestTextRange)( + IDWriteTextLayout4 *This, + UINT32 textPosition, + UINT32 textLength, + FLOAT originX, + FLOAT originY, + DWRITE_HIT_TEST_METRICS *metrics, + UINT32 max_metricscount, + UINT32 *actual_metricscount); + + /*** IDWriteTextLayout1 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetPairKerning)( + IDWriteTextLayout4 *This, + WINBOOL is_pairkerning_enabled, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *GetPairKerning)( + IDWriteTextLayout4 *This, + UINT32 position, + WINBOOL *is_pairkerning_enabled, + DWRITE_TEXT_RANGE *range); + + HRESULT (STDMETHODCALLTYPE *SetCharacterSpacing)( + IDWriteTextLayout4 *This, + FLOAT leading_spacing, + FLOAT trailing_spacing, + FLOAT minimum_advance_width, + DWRITE_TEXT_RANGE range); + + HRESULT (STDMETHODCALLTYPE *GetCharacterSpacing)( + IDWriteTextLayout4 *This, + UINT32 position, + FLOAT *leading_spacing, + FLOAT *trailing_spacing, + FLOAT *minimum_advance_width, + DWRITE_TEXT_RANGE *range); + + /*** IDWriteTextLayout2 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout2_GetMetrics)( + IDWriteTextLayout4 *This, + DWRITE_TEXT_METRICS1 *metrics); + + HRESULT (STDMETHODCALLTYPE *SetVerticalGlyphOrientation)( + IDWriteTextLayout4 *This, + DWRITE_VERTICAL_GLYPH_ORIENTATION orientation); + + DWRITE_VERTICAL_GLYPH_ORIENTATION (STDMETHODCALLTYPE *GetVerticalGlyphOrientation)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *SetLastLineWrapping)( + IDWriteTextLayout4 *This, + WINBOOL lastline_wrapping_enabled); + + WINBOOL (STDMETHODCALLTYPE *GetLastLineWrapping)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *SetOpticalAlignment)( + IDWriteTextLayout4 *This, + DWRITE_OPTICAL_ALIGNMENT alignment); + + DWRITE_OPTICAL_ALIGNMENT (STDMETHODCALLTYPE *GetOpticalAlignment)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *SetFontFallback)( + IDWriteTextLayout4 *This, + IDWriteFontFallback *fallback); + + HRESULT (STDMETHODCALLTYPE *GetFontFallback)( + IDWriteTextLayout4 *This, + IDWriteFontFallback **fallback); + + /*** IDWriteTextLayout3 methods ***/ + HRESULT (STDMETHODCALLTYPE *InvalidateLayout)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_SetLineSpacing)( + IDWriteTextLayout4 *This, + const DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_GetLineSpacing)( + IDWriteTextLayout4 *This, + DWRITE_LINE_SPACING *spacing); + + HRESULT (STDMETHODCALLTYPE *IDWriteTextLayout3_GetLineMetrics)( + IDWriteTextLayout4 *This, + DWRITE_LINE_METRICS1 *metrics, + UINT32 max_count, + UINT32 *count); + + /*** IDWriteTextLayout4 methods ***/ + HRESULT (STDMETHODCALLTYPE *SetFontAxisValues)( + IDWriteTextLayout4 *This, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + DWRITE_TEXT_RANGE range); + + UINT32 (STDMETHODCALLTYPE *GetFontAxisValueCount)( + IDWriteTextLayout4 *This, + UINT32 pos); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisValues)( + IDWriteTextLayout4 *This, + UINT32 pos, + DWRITE_FONT_AXIS_VALUE *values, + UINT32 num_values, + DWRITE_TEXT_RANGE *range); + + DWRITE_AUTOMATIC_FONT_AXES (STDMETHODCALLTYPE *GetAutomaticFontAxes)( + IDWriteTextLayout4 *This); + + HRESULT (STDMETHODCALLTYPE *SetAutomaticFontAxes)( + IDWriteTextLayout4 *This, + DWRITE_AUTOMATIC_FONT_AXES axes); + + END_INTERFACE +} IDWriteTextLayout4Vtbl; + +interface IDWriteTextLayout4 { + CONST_VTBL IDWriteTextLayout4Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteTextLayout4_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteTextLayout4_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteTextLayout4_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteTextFormat methods ***/ +#define IDWriteTextLayout4_SetTextAlignment(This,alignment) (This)->lpVtbl->SetTextAlignment(This,alignment) +#define IDWriteTextLayout4_SetParagraphAlignment(This,alignment) (This)->lpVtbl->SetParagraphAlignment(This,alignment) +#define IDWriteTextLayout4_SetWordWrapping(This,wrapping) (This)->lpVtbl->SetWordWrapping(This,wrapping) +#define IDWriteTextLayout4_SetReadingDirection(This,direction) (This)->lpVtbl->SetReadingDirection(This,direction) +#define IDWriteTextLayout4_SetFlowDirection(This,direction) (This)->lpVtbl->SetFlowDirection(This,direction) +#define IDWriteTextLayout4_SetIncrementalTabStop(This,tabstop) (This)->lpVtbl->SetIncrementalTabStop(This,tabstop) +#define IDWriteTextLayout4_SetTrimming(This,trimming,trimming_sign) (This)->lpVtbl->SetTrimming(This,trimming,trimming_sign) +#define IDWriteTextLayout4_GetTextAlignment(This) (This)->lpVtbl->GetTextAlignment(This) +#define IDWriteTextLayout4_GetParagraphAlignment(This) (This)->lpVtbl->GetParagraphAlignment(This) +#define IDWriteTextLayout4_GetWordWrapping(This) (This)->lpVtbl->GetWordWrapping(This) +#define IDWriteTextLayout4_GetReadingDirection(This) (This)->lpVtbl->GetReadingDirection(This) +#define IDWriteTextLayout4_GetFlowDirection(This) (This)->lpVtbl->GetFlowDirection(This) +#define IDWriteTextLayout4_GetIncrementalTabStop(This) (This)->lpVtbl->GetIncrementalTabStop(This) +#define IDWriteTextLayout4_GetTrimming(This,options,trimming_sign) (This)->lpVtbl->GetTrimming(This,options,trimming_sign) +/*** IDWriteTextLayout methods ***/ +#define IDWriteTextLayout4_SetMaxWidth(This,maxWidth) (This)->lpVtbl->SetMaxWidth(This,maxWidth) +#define IDWriteTextLayout4_SetMaxHeight(This,maxHeight) (This)->lpVtbl->SetMaxHeight(This,maxHeight) +#define IDWriteTextLayout4_SetFontCollection(This,collection,range) (This)->lpVtbl->SetFontCollection(This,collection,range) +#define IDWriteTextLayout4_SetFontFamilyName(This,name,range) (This)->lpVtbl->SetFontFamilyName(This,name,range) +#define IDWriteTextLayout4_SetFontWeight(This,weight,range) (This)->lpVtbl->SetFontWeight(This,weight,range) +#define IDWriteTextLayout4_SetFontStyle(This,style,range) (This)->lpVtbl->SetFontStyle(This,style,range) +#define IDWriteTextLayout4_SetFontStretch(This,stretch,range) (This)->lpVtbl->SetFontStretch(This,stretch,range) +#define IDWriteTextLayout4_SetFontSize(This,size,range) (This)->lpVtbl->SetFontSize(This,size,range) +#define IDWriteTextLayout4_SetUnderline(This,underline,range) (This)->lpVtbl->SetUnderline(This,underline,range) +#define IDWriteTextLayout4_SetStrikethrough(This,strikethrough,range) (This)->lpVtbl->SetStrikethrough(This,strikethrough,range) +#define IDWriteTextLayout4_SetDrawingEffect(This,effect,range) (This)->lpVtbl->SetDrawingEffect(This,effect,range) +#define IDWriteTextLayout4_SetInlineObject(This,object,range) (This)->lpVtbl->SetInlineObject(This,object,range) +#define IDWriteTextLayout4_SetTypography(This,typography,range) (This)->lpVtbl->SetTypography(This,typography,range) +#define IDWriteTextLayout4_SetLocaleName(This,locale,range) (This)->lpVtbl->SetLocaleName(This,locale,range) +#define IDWriteTextLayout4_GetMaxWidth(This) (This)->lpVtbl->GetMaxWidth(This) +#define IDWriteTextLayout4_GetMaxHeight(This) (This)->lpVtbl->GetMaxHeight(This) +#define IDWriteTextLayout4_GetFontCollection(This,pos,collection,range) (This)->lpVtbl->IDWriteTextLayout_GetFontCollection(This,pos,collection,range) +#define IDWriteTextLayout4_GetFontFamilyNameLength(This,pos,len,range) (This)->lpVtbl->IDWriteTextLayout_GetFontFamilyNameLength(This,pos,len,range) +#define IDWriteTextLayout4_GetFontFamilyName(This,position,name,name_size,range) (This)->lpVtbl->IDWriteTextLayout_GetFontFamilyName(This,position,name,name_size,range) +#define IDWriteTextLayout4_GetFontWeight(This,position,weight,range) (This)->lpVtbl->IDWriteTextLayout_GetFontWeight(This,position,weight,range) +#define IDWriteTextLayout4_GetFontStyle(This,currentPosition,style,range) (This)->lpVtbl->IDWriteTextLayout_GetFontStyle(This,currentPosition,style,range) +#define IDWriteTextLayout4_GetFontStretch(This,position,stretch,range) (This)->lpVtbl->IDWriteTextLayout_GetFontStretch(This,position,stretch,range) +#define IDWriteTextLayout4_GetFontSize(This,position,size,range) (This)->lpVtbl->IDWriteTextLayout_GetFontSize(This,position,size,range) +#define IDWriteTextLayout4_GetUnderline(This,position,has_underline,range) (This)->lpVtbl->GetUnderline(This,position,has_underline,range) +#define IDWriteTextLayout4_GetStrikethrough(This,position,has_strikethrough,range) (This)->lpVtbl->GetStrikethrough(This,position,has_strikethrough,range) +#define IDWriteTextLayout4_GetDrawingEffect(This,position,effect,range) (This)->lpVtbl->GetDrawingEffect(This,position,effect,range) +#define IDWriteTextLayout4_GetInlineObject(This,position,object,range) (This)->lpVtbl->GetInlineObject(This,position,object,range) +#define IDWriteTextLayout4_GetTypography(This,position,typography,range) (This)->lpVtbl->GetTypography(This,position,typography,range) +#define IDWriteTextLayout4_GetLocaleNameLength(This,position,length,range) (This)->lpVtbl->IDWriteTextLayout_GetLocaleNameLength(This,position,length,range) +#define IDWriteTextLayout4_GetLocaleName(This,position,name,name_size,range) (This)->lpVtbl->IDWriteTextLayout_GetLocaleName(This,position,name,name_size,range) +#define IDWriteTextLayout4_Draw(This,context,renderer,originX,originY) (This)->lpVtbl->Draw(This,context,renderer,originX,originY) +#define IDWriteTextLayout4_GetOverhangMetrics(This,overhangs) (This)->lpVtbl->GetOverhangMetrics(This,overhangs) +#define IDWriteTextLayout4_GetClusterMetrics(This,metrics,max_count,act_count) (This)->lpVtbl->GetClusterMetrics(This,metrics,max_count,act_count) +#define IDWriteTextLayout4_DetermineMinWidth(This,min_width) (This)->lpVtbl->DetermineMinWidth(This,min_width) +#define IDWriteTextLayout4_HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics) (This)->lpVtbl->HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics) +#define IDWriteTextLayout4_HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics) (This)->lpVtbl->HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics) +#define IDWriteTextLayout4_HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount) (This)->lpVtbl->HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount) +/*** IDWriteTextLayout1 methods ***/ +#define IDWriteTextLayout4_SetPairKerning(This,is_pairkerning_enabled,range) (This)->lpVtbl->SetPairKerning(This,is_pairkerning_enabled,range) +#define IDWriteTextLayout4_GetPairKerning(This,position,is_pairkerning_enabled,range) (This)->lpVtbl->GetPairKerning(This,position,is_pairkerning_enabled,range) +#define IDWriteTextLayout4_SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range) (This)->lpVtbl->SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range) +#define IDWriteTextLayout4_GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range) (This)->lpVtbl->GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range) +/*** IDWriteTextLayout2 methods ***/ +#define IDWriteTextLayout4_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteTextLayout2_GetMetrics(This,metrics) +#define IDWriteTextLayout4_SetVerticalGlyphOrientation(This,orientation) (This)->lpVtbl->SetVerticalGlyphOrientation(This,orientation) +#define IDWriteTextLayout4_GetVerticalGlyphOrientation(This) (This)->lpVtbl->GetVerticalGlyphOrientation(This) +#define IDWriteTextLayout4_SetLastLineWrapping(This,lastline_wrapping_enabled) (This)->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled) +#define IDWriteTextLayout4_GetLastLineWrapping(This) (This)->lpVtbl->GetLastLineWrapping(This) +#define IDWriteTextLayout4_SetOpticalAlignment(This,alignment) (This)->lpVtbl->SetOpticalAlignment(This,alignment) +#define IDWriteTextLayout4_GetOpticalAlignment(This) (This)->lpVtbl->GetOpticalAlignment(This) +#define IDWriteTextLayout4_SetFontFallback(This,fallback) (This)->lpVtbl->SetFontFallback(This,fallback) +#define IDWriteTextLayout4_GetFontFallback(This,fallback) (This)->lpVtbl->GetFontFallback(This,fallback) +/*** IDWriteTextLayout3 methods ***/ +#define IDWriteTextLayout4_InvalidateLayout(This) (This)->lpVtbl->InvalidateLayout(This) +#define IDWriteTextLayout4_SetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextLayout3_SetLineSpacing(This,spacing) +#define IDWriteTextLayout4_GetLineSpacing(This,spacing) (This)->lpVtbl->IDWriteTextLayout3_GetLineSpacing(This,spacing) +#define IDWriteTextLayout4_GetLineMetrics(This,metrics,max_count,count) (This)->lpVtbl->IDWriteTextLayout3_GetLineMetrics(This,metrics,max_count,count) +/*** IDWriteTextLayout4 methods ***/ +#define IDWriteTextLayout4_SetFontAxisValues(This,axis_values,num_values,range) (This)->lpVtbl->SetFontAxisValues(This,axis_values,num_values,range) +#define IDWriteTextLayout4_GetFontAxisValueCount(This,pos) (This)->lpVtbl->GetFontAxisValueCount(This,pos) +#define IDWriteTextLayout4_GetFontAxisValues(This,pos,values,num_values,range) (This)->lpVtbl->GetFontAxisValues(This,pos,values,num_values,range) +#define IDWriteTextLayout4_GetAutomaticFontAxes(This) (This)->lpVtbl->GetAutomaticFontAxes(This) +#define IDWriteTextLayout4_SetAutomaticFontAxes(This,axes) (This)->lpVtbl->SetAutomaticFontAxes(This,axes) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_QueryInterface(IDWriteTextLayout4* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteTextLayout4_AddRef(IDWriteTextLayout4* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteTextLayout4_Release(IDWriteTextLayout4* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteTextFormat methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_SetTextAlignment(IDWriteTextLayout4* This,DWRITE_TEXT_ALIGNMENT alignment) { + return This->lpVtbl->SetTextAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetParagraphAlignment(IDWriteTextLayout4* This,DWRITE_PARAGRAPH_ALIGNMENT alignment) { + return This->lpVtbl->SetParagraphAlignment(This,alignment); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetWordWrapping(IDWriteTextLayout4* This,DWRITE_WORD_WRAPPING wrapping) { + return This->lpVtbl->SetWordWrapping(This,wrapping); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetReadingDirection(IDWriteTextLayout4* This,DWRITE_READING_DIRECTION direction) { + return This->lpVtbl->SetReadingDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFlowDirection(IDWriteTextLayout4* This,DWRITE_FLOW_DIRECTION direction) { + return This->lpVtbl->SetFlowDirection(This,direction); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetIncrementalTabStop(IDWriteTextLayout4* This,FLOAT tabstop) { + return This->lpVtbl->SetIncrementalTabStop(This,tabstop); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetTrimming(IDWriteTextLayout4* This,const DWRITE_TRIMMING *trimming,IDWriteInlineObject *trimming_sign) { + return This->lpVtbl->SetTrimming(This,trimming,trimming_sign); +} +static FORCEINLINE DWRITE_TEXT_ALIGNMENT IDWriteTextLayout4_GetTextAlignment(IDWriteTextLayout4* This) { + return This->lpVtbl->GetTextAlignment(This); +} +static FORCEINLINE DWRITE_PARAGRAPH_ALIGNMENT IDWriteTextLayout4_GetParagraphAlignment(IDWriteTextLayout4* This) { + return This->lpVtbl->GetParagraphAlignment(This); +} +static FORCEINLINE DWRITE_WORD_WRAPPING IDWriteTextLayout4_GetWordWrapping(IDWriteTextLayout4* This) { + return This->lpVtbl->GetWordWrapping(This); +} +static FORCEINLINE DWRITE_READING_DIRECTION IDWriteTextLayout4_GetReadingDirection(IDWriteTextLayout4* This) { + return This->lpVtbl->GetReadingDirection(This); +} +static FORCEINLINE DWRITE_FLOW_DIRECTION IDWriteTextLayout4_GetFlowDirection(IDWriteTextLayout4* This) { + return This->lpVtbl->GetFlowDirection(This); +} +static FORCEINLINE FLOAT IDWriteTextLayout4_GetIncrementalTabStop(IDWriteTextLayout4* This) { + return This->lpVtbl->GetIncrementalTabStop(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetTrimming(IDWriteTextLayout4* This,DWRITE_TRIMMING *options,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->GetTrimming(This,options,trimming_sign); +} +/*** IDWriteTextLayout methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_SetMaxWidth(IDWriteTextLayout4* This,FLOAT maxWidth) { + return This->lpVtbl->SetMaxWidth(This,maxWidth); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetMaxHeight(IDWriteTextLayout4* This,FLOAT maxHeight) { + return This->lpVtbl->SetMaxHeight(This,maxHeight); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontCollection(IDWriteTextLayout4* This,IDWriteFontCollection *collection,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontCollection(This,collection,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontFamilyName(IDWriteTextLayout4* This,const WCHAR *name,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontFamilyName(This,name,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontWeight(IDWriteTextLayout4* This,DWRITE_FONT_WEIGHT weight,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontWeight(This,weight,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontStyle(IDWriteTextLayout4* This,DWRITE_FONT_STYLE style,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontStyle(This,style,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontStretch(IDWriteTextLayout4* This,DWRITE_FONT_STRETCH stretch,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontStretch(This,stretch,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontSize(IDWriteTextLayout4* This,FLOAT size,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontSize(This,size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetUnderline(IDWriteTextLayout4* This,WINBOOL underline,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetUnderline(This,underline,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetStrikethrough(IDWriteTextLayout4* This,WINBOOL strikethrough,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetStrikethrough(This,strikethrough,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetDrawingEffect(IDWriteTextLayout4* This,IUnknown *effect,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetDrawingEffect(This,effect,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetInlineObject(IDWriteTextLayout4* This,IDWriteInlineObject *object,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetInlineObject(This,object,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetTypography(IDWriteTextLayout4* This,IDWriteTypography *typography,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetTypography(This,typography,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetLocaleName(IDWriteTextLayout4* This,const WCHAR *locale,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetLocaleName(This,locale,range); +} +static FORCEINLINE FLOAT IDWriteTextLayout4_GetMaxWidth(IDWriteTextLayout4* This) { + return This->lpVtbl->GetMaxWidth(This); +} +static FORCEINLINE FLOAT IDWriteTextLayout4_GetMaxHeight(IDWriteTextLayout4* This) { + return This->lpVtbl->GetMaxHeight(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontCollection(IDWriteTextLayout4* This,UINT32 pos,IDWriteFontCollection **collection,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontCollection(This,pos,collection,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontFamilyNameLength(IDWriteTextLayout4* This,UINT32 pos,UINT32 *len,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontFamilyNameLength(This,pos,len,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontFamilyName(IDWriteTextLayout4* This,UINT32 position,WCHAR *name,UINT32 name_size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontFamilyName(This,position,name,name_size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontWeight(IDWriteTextLayout4* This,UINT32 position,DWRITE_FONT_WEIGHT *weight,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontWeight(This,position,weight,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontStyle(IDWriteTextLayout4* This,UINT32 currentPosition,DWRITE_FONT_STYLE *style,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontStyle(This,currentPosition,style,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontStretch(IDWriteTextLayout4* This,UINT32 position,DWRITE_FONT_STRETCH *stretch,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontStretch(This,position,stretch,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontSize(IDWriteTextLayout4* This,UINT32 position,FLOAT *size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetFontSize(This,position,size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetUnderline(IDWriteTextLayout4* This,UINT32 position,WINBOOL *has_underline,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetUnderline(This,position,has_underline,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetStrikethrough(IDWriteTextLayout4* This,UINT32 position,WINBOOL *has_strikethrough,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetStrikethrough(This,position,has_strikethrough,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetDrawingEffect(IDWriteTextLayout4* This,UINT32 position,IUnknown **effect,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetDrawingEffect(This,position,effect,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetInlineObject(IDWriteTextLayout4* This,UINT32 position,IDWriteInlineObject **object,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetInlineObject(This,position,object,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetTypography(IDWriteTextLayout4* This,UINT32 position,IDWriteTypography **typography,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetTypography(This,position,typography,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetLocaleNameLength(IDWriteTextLayout4* This,UINT32 position,UINT32 *length,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetLocaleNameLength(This,position,length,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetLocaleName(IDWriteTextLayout4* This,UINT32 position,WCHAR *name,UINT32 name_size,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->IDWriteTextLayout_GetLocaleName(This,position,name,name_size,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_Draw(IDWriteTextLayout4* This,void *context,IDWriteTextRenderer *renderer,FLOAT originX,FLOAT originY) { + return This->lpVtbl->Draw(This,context,renderer,originX,originY); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetOverhangMetrics(IDWriteTextLayout4* This,DWRITE_OVERHANG_METRICS *overhangs) { + return This->lpVtbl->GetOverhangMetrics(This,overhangs); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetClusterMetrics(IDWriteTextLayout4* This,DWRITE_CLUSTER_METRICS *metrics,UINT32 max_count,UINT32 *act_count) { + return This->lpVtbl->GetClusterMetrics(This,metrics,max_count,act_count); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_DetermineMinWidth(IDWriteTextLayout4* This,FLOAT *min_width) { + return This->lpVtbl->DetermineMinWidth(This,min_width); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_HitTestPoint(IDWriteTextLayout4* This,FLOAT pointX,FLOAT pointY,WINBOOL *is_trailinghit,WINBOOL *is_inside,DWRITE_HIT_TEST_METRICS *metrics) { + return This->lpVtbl->HitTestPoint(This,pointX,pointY,is_trailinghit,is_inside,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_HitTestTextPosition(IDWriteTextLayout4* This,UINT32 textPosition,WINBOOL is_trailinghit,FLOAT *pointX,FLOAT *pointY,DWRITE_HIT_TEST_METRICS *metrics) { + return This->lpVtbl->HitTestTextPosition(This,textPosition,is_trailinghit,pointX,pointY,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_HitTestTextRange(IDWriteTextLayout4* This,UINT32 textPosition,UINT32 textLength,FLOAT originX,FLOAT originY,DWRITE_HIT_TEST_METRICS *metrics,UINT32 max_metricscount,UINT32 *actual_metricscount) { + return This->lpVtbl->HitTestTextRange(This,textPosition,textLength,originX,originY,metrics,max_metricscount,actual_metricscount); +} +/*** IDWriteTextLayout1 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_SetPairKerning(IDWriteTextLayout4* This,WINBOOL is_pairkerning_enabled,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetPairKerning(This,is_pairkerning_enabled,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetPairKerning(IDWriteTextLayout4* This,UINT32 position,WINBOOL *is_pairkerning_enabled,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetPairKerning(This,position,is_pairkerning_enabled,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetCharacterSpacing(IDWriteTextLayout4* This,FLOAT leading_spacing,FLOAT trailing_spacing,FLOAT minimum_advance_width,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetCharacterSpacing(This,leading_spacing,trailing_spacing,minimum_advance_width,range); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetCharacterSpacing(IDWriteTextLayout4* This,UINT32 position,FLOAT *leading_spacing,FLOAT *trailing_spacing,FLOAT *minimum_advance_width,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetCharacterSpacing(This,position,leading_spacing,trailing_spacing,minimum_advance_width,range); +} +/*** IDWriteTextLayout2 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_GetMetrics(IDWriteTextLayout4* This,DWRITE_TEXT_METRICS1 *metrics) { + return This->lpVtbl->IDWriteTextLayout2_GetMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetVerticalGlyphOrientation(IDWriteTextLayout4* This,DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) { + return This->lpVtbl->SetVerticalGlyphOrientation(This,orientation); +} +static FORCEINLINE DWRITE_VERTICAL_GLYPH_ORIENTATION IDWriteTextLayout4_GetVerticalGlyphOrientation(IDWriteTextLayout4* This) { + return This->lpVtbl->GetVerticalGlyphOrientation(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetLastLineWrapping(IDWriteTextLayout4* This,WINBOOL lastline_wrapping_enabled) { + return This->lpVtbl->SetLastLineWrapping(This,lastline_wrapping_enabled); +} +static FORCEINLINE WINBOOL IDWriteTextLayout4_GetLastLineWrapping(IDWriteTextLayout4* This) { + return This->lpVtbl->GetLastLineWrapping(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetOpticalAlignment(IDWriteTextLayout4* This,DWRITE_OPTICAL_ALIGNMENT alignment) { + return This->lpVtbl->SetOpticalAlignment(This,alignment); +} +static FORCEINLINE DWRITE_OPTICAL_ALIGNMENT IDWriteTextLayout4_GetOpticalAlignment(IDWriteTextLayout4* This) { + return This->lpVtbl->GetOpticalAlignment(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontFallback(IDWriteTextLayout4* This,IDWriteFontFallback *fallback) { + return This->lpVtbl->SetFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontFallback(IDWriteTextLayout4* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetFontFallback(This,fallback); +} +/*** IDWriteTextLayout3 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_InvalidateLayout(IDWriteTextLayout4* This) { + return This->lpVtbl->InvalidateLayout(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetLineSpacing(IDWriteTextLayout4* This,const DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextLayout3_SetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetLineSpacing(IDWriteTextLayout4* This,DWRITE_LINE_SPACING *spacing) { + return This->lpVtbl->IDWriteTextLayout3_GetLineSpacing(This,spacing); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetLineMetrics(IDWriteTextLayout4* This,DWRITE_LINE_METRICS1 *metrics,UINT32 max_count,UINT32 *count) { + return This->lpVtbl->IDWriteTextLayout3_GetLineMetrics(This,metrics,max_count,count); +} +/*** IDWriteTextLayout4 methods ***/ +static FORCEINLINE HRESULT IDWriteTextLayout4_SetFontAxisValues(IDWriteTextLayout4* This,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,DWRITE_TEXT_RANGE range) { + return This->lpVtbl->SetFontAxisValues(This,axis_values,num_values,range); +} +static FORCEINLINE UINT32 IDWriteTextLayout4_GetFontAxisValueCount(IDWriteTextLayout4* This,UINT32 pos) { + return This->lpVtbl->GetFontAxisValueCount(This,pos); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_GetFontAxisValues(IDWriteTextLayout4* This,UINT32 pos,DWRITE_FONT_AXIS_VALUE *values,UINT32 num_values,DWRITE_TEXT_RANGE *range) { + return This->lpVtbl->GetFontAxisValues(This,pos,values,num_values,range); +} +static FORCEINLINE DWRITE_AUTOMATIC_FONT_AXES IDWriteTextLayout4_GetAutomaticFontAxes(IDWriteTextLayout4* This) { + return This->lpVtbl->GetAutomaticFontAxes(This); +} +static FORCEINLINE HRESULT IDWriteTextLayout4_SetAutomaticFontAxes(IDWriteTextLayout4* This,DWRITE_AUTOMATIC_FONT_AXES axes) { + return This->lpVtbl->SetAutomaticFontAxes(This,axes); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteTextLayout4_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFallback1 interface + */ +#ifndef __IDWriteFontFallback1_INTERFACE_DEFINED__ +#define __IDWriteFontFallback1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFallback1, 0x2397599d, 0xdd0d, 0x4681, 0xbd,0x6a, 0xf4,0xf3,0x1e,0xaa,0xde,0x77); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("2397599d-dd0d-4681-bd6a-f4f31eaade77") +IDWriteFontFallback1 : public IDWriteFontFallback +{ + virtual HRESULT STDMETHODCALLTYPE MapCharacters( + IDWriteTextAnalysisSource *source, + UINT32 pos, + UINT32 length, + IDWriteFontCollection *base_collection, + const WCHAR *familyname, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + UINT32 *mapped_length, + FLOAT *scale, + IDWriteFontFace5 **fontface) = 0; + + using IDWriteFontFallback::MapCharacters; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFallback1, 0x2397599d, 0xdd0d, 0x4681, 0xbd,0x6a, 0xf4,0xf3,0x1e,0xaa,0xde,0x77) +#endif +#else +typedef struct IDWriteFontFallback1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFallback1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFallback1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFallback1 *This); + + /*** IDWriteFontFallback methods ***/ + HRESULT (STDMETHODCALLTYPE *MapCharacters)( + IDWriteFontFallback1 *This, + IDWriteTextAnalysisSource *source, + UINT32 position, + UINT32 length, + IDWriteFontCollection *basecollection, + const WCHAR *baseFamilyName, + DWRITE_FONT_WEIGHT baseWeight, + DWRITE_FONT_STYLE baseStyle, + DWRITE_FONT_STRETCH baseStretch, + UINT32 *mappedLength, + IDWriteFont **mappedFont, + FLOAT *scale); + + /*** IDWriteFontFallback1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFontFallback1_MapCharacters)( + IDWriteFontFallback1 *This, + IDWriteTextAnalysisSource *source, + UINT32 pos, + UINT32 length, + IDWriteFontCollection *base_collection, + const WCHAR *familyname, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + UINT32 *mapped_length, + FLOAT *scale, + IDWriteFontFace5 **fontface); + + END_INTERFACE +} IDWriteFontFallback1Vtbl; + +interface IDWriteFontFallback1 { + CONST_VTBL IDWriteFontFallback1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFallback1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFallback1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFallback1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFallback methods ***/ +/*** IDWriteFontFallback1 methods ***/ +#define IDWriteFontFallback1_MapCharacters(This,source,pos,length,base_collection,familyname,axis_values,num_values,mapped_length,scale,fontface) (This)->lpVtbl->IDWriteFontFallback1_MapCharacters(This,source,pos,length,base_collection,familyname,axis_values,num_values,mapped_length,scale,fontface) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFallback1_QueryInterface(IDWriteFontFallback1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFallback1_AddRef(IDWriteFontFallback1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFallback1_Release(IDWriteFontFallback1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFallback methods ***/ +/*** IDWriteFontFallback1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFallback1_MapCharacters(IDWriteFontFallback1* This,IDWriteTextAnalysisSource *source,UINT32 pos,UINT32 length,IDWriteFontCollection *base_collection,const WCHAR *familyname,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,UINT32 *mapped_length,FLOAT *scale,IDWriteFontFace5 **fontface) { + return This->lpVtbl->IDWriteFontFallback1_MapCharacters(This,source,pos,length,base_collection,familyname,axis_values,num_values,mapped_length,scale,fontface); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFallback1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteGdiInterop1 interface + */ +#ifndef __IDWriteGdiInterop1_INTERFACE_DEFINED__ +#define __IDWriteGdiInterop1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteGdiInterop1, 0x4556be70, 0x3abd, 0x4f70, 0x90,0xbe, 0x42,0x17,0x80,0xa6,0xf5,0x15); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("4556be70-3abd-4f70-90be-421780a6f515") +IDWriteGdiInterop1 : public IDWriteGdiInterop +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontFromLOGFONT( + const LOGFONTW *logfont, + IDWriteFontCollection *collection, + IDWriteFont **font) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontSignature( + IDWriteFontFace *fontface, + FONTSIGNATURE *fontsig) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontSignature( + IDWriteFont *font, + FONTSIGNATURE *fontsig) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMatchingFontsByLOGFONT( + const LOGFONTW *logfont, + IDWriteFontSet *fontset, + IDWriteFontSet **subset) = 0; + + using IDWriteGdiInterop::CreateFontFromLOGFONT; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteGdiInterop1, 0x4556be70, 0x3abd, 0x4f70, 0x90,0xbe, 0x42,0x17,0x80,0xa6,0xf5,0x15) +#endif +#else +typedef struct IDWriteGdiInterop1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteGdiInterop1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteGdiInterop1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteGdiInterop1 *This); + + /*** IDWriteGdiInterop methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)( + IDWriteGdiInterop1 *This, + const LOGFONTW *logfont, + IDWriteFont **font); + + HRESULT (STDMETHODCALLTYPE *ConvertFontToLOGFONT)( + IDWriteGdiInterop1 *This, + IDWriteFont *font, + LOGFONTW *logfont, + WINBOOL *is_systemfont); + + HRESULT (STDMETHODCALLTYPE *ConvertFontFaceToLOGFONT)( + IDWriteGdiInterop1 *This, + IDWriteFontFace *font, + LOGFONTW *logfont); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceFromHdc)( + IDWriteGdiInterop1 *This, + HDC hdc, + IDWriteFontFace **fontface); + + HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)( + IDWriteGdiInterop1 *This, + HDC hdc, + UINT32 width, + UINT32 height, + IDWriteBitmapRenderTarget **target); + + /*** IDWriteGdiInterop1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteGdiInterop1_CreateFontFromLOGFONT)( + IDWriteGdiInterop1 *This, + const LOGFONTW *logfont, + IDWriteFontCollection *collection, + IDWriteFont **font); + + HRESULT (STDMETHODCALLTYPE *GetFontSignature_)( + IDWriteGdiInterop1 *This, + IDWriteFontFace *fontface, + FONTSIGNATURE *fontsig); + + HRESULT (STDMETHODCALLTYPE *GetFontSignature)( + IDWriteGdiInterop1 *This, + IDWriteFont *font, + FONTSIGNATURE *fontsig); + + HRESULT (STDMETHODCALLTYPE *GetMatchingFontsByLOGFONT)( + IDWriteGdiInterop1 *This, + const LOGFONTW *logfont, + IDWriteFontSet *fontset, + IDWriteFontSet **subset); + + END_INTERFACE +} IDWriteGdiInterop1Vtbl; + +interface IDWriteGdiInterop1 { + CONST_VTBL IDWriteGdiInterop1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteGdiInterop1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteGdiInterop1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteGdiInterop1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteGdiInterop methods ***/ +#define IDWriteGdiInterop1_ConvertFontToLOGFONT(This,font,logfont,is_systemfont) (This)->lpVtbl->ConvertFontToLOGFONT(This,font,logfont,is_systemfont) +#define IDWriteGdiInterop1_ConvertFontFaceToLOGFONT(This,font,logfont) (This)->lpVtbl->ConvertFontFaceToLOGFONT(This,font,logfont) +#define IDWriteGdiInterop1_CreateFontFaceFromHdc(This,hdc,fontface) (This)->lpVtbl->CreateFontFaceFromHdc(This,hdc,fontface) +#define IDWriteGdiInterop1_CreateBitmapRenderTarget(This,hdc,width,height,target) (This)->lpVtbl->CreateBitmapRenderTarget(This,hdc,width,height,target) +/*** IDWriteGdiInterop1 methods ***/ +#define IDWriteGdiInterop1_CreateFontFromLOGFONT(This,logfont,collection,font) (This)->lpVtbl->IDWriteGdiInterop1_CreateFontFromLOGFONT(This,logfont,collection,font) +#define IDWriteGdiInterop1_GetFontSignature_(This,fontface,fontsig) (This)->lpVtbl->GetFontSignature_(This,fontface,fontsig) +#define IDWriteGdiInterop1_GetFontSignature(This,font,fontsig) (This)->lpVtbl->GetFontSignature(This,font,fontsig) +#define IDWriteGdiInterop1_GetMatchingFontsByLOGFONT(This,logfont,fontset,subset) (This)->lpVtbl->GetMatchingFontsByLOGFONT(This,logfont,fontset,subset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteGdiInterop1_QueryInterface(IDWriteGdiInterop1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteGdiInterop1_AddRef(IDWriteGdiInterop1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteGdiInterop1_Release(IDWriteGdiInterop1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteGdiInterop methods ***/ +static FORCEINLINE HRESULT IDWriteGdiInterop1_ConvertFontToLOGFONT(IDWriteGdiInterop1* This,IDWriteFont *font,LOGFONTW *logfont,WINBOOL *is_systemfont) { + return This->lpVtbl->ConvertFontToLOGFONT(This,font,logfont,is_systemfont); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_ConvertFontFaceToLOGFONT(IDWriteGdiInterop1* This,IDWriteFontFace *font,LOGFONTW *logfont) { + return This->lpVtbl->ConvertFontFaceToLOGFONT(This,font,logfont); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_CreateFontFaceFromHdc(IDWriteGdiInterop1* This,HDC hdc,IDWriteFontFace **fontface) { + return This->lpVtbl->CreateFontFaceFromHdc(This,hdc,fontface); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_CreateBitmapRenderTarget(IDWriteGdiInterop1* This,HDC hdc,UINT32 width,UINT32 height,IDWriteBitmapRenderTarget **target) { + return This->lpVtbl->CreateBitmapRenderTarget(This,hdc,width,height,target); +} +/*** IDWriteGdiInterop1 methods ***/ +static FORCEINLINE HRESULT IDWriteGdiInterop1_CreateFontFromLOGFONT(IDWriteGdiInterop1* This,const LOGFONTW *logfont,IDWriteFontCollection *collection,IDWriteFont **font) { + return This->lpVtbl->IDWriteGdiInterop1_CreateFontFromLOGFONT(This,logfont,collection,font); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_GetFontSignature_(IDWriteGdiInterop1* This,IDWriteFontFace *fontface,FONTSIGNATURE *fontsig) { + return This->lpVtbl->GetFontSignature_(This,fontface,fontsig); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_GetFontSignature(IDWriteGdiInterop1* This,IDWriteFont *font,FONTSIGNATURE *fontsig) { + return This->lpVtbl->GetFontSignature(This,font,fontsig); +} +static FORCEINLINE HRESULT IDWriteGdiInterop1_GetMatchingFontsByLOGFONT(IDWriteGdiInterop1* This,const LOGFONTW *logfont,IDWriteFontSet *fontset,IDWriteFontSet **subset) { + return This->lpVtbl->GetMatchingFontsByLOGFONT(This,logfont,fontset,subset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteGdiInterop1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSetBuilder interface + */ +#ifndef __IDWriteFontSetBuilder_INTERFACE_DEFINED__ +#define __IDWriteFontSetBuilder_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSetBuilder, 0x2f642afe, 0x9c68, 0x4f40, 0xb8,0xbe, 0x45,0x74,0x01,0xaf,0xcb,0x3d); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("2f642afe-9c68-4f40-b8be-457401afcb3d") +IDWriteFontSetBuilder : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE AddFontFaceReference( + IDWriteFontFaceReference *ref, + const DWRITE_FONT_PROPERTY *props, + UINT32 prop_count) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddFontFaceReference( + IDWriteFontFaceReference *ref) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddFontSet( + IDWriteFontSet *fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontSet( + IDWriteFontSet **fontset) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSetBuilder, 0x2f642afe, 0x9c68, 0x4f40, 0xb8,0xbe, 0x45,0x74,0x01,0xaf,0xcb,0x3d) +#endif +#else +typedef struct IDWriteFontSetBuilderVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSetBuilder *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSetBuilder *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSetBuilder *This); + + /*** IDWriteFontSetBuilder methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference_)( + IDWriteFontSetBuilder *This, + IDWriteFontFaceReference *ref, + const DWRITE_FONT_PROPERTY *props, + UINT32 prop_count); + + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference)( + IDWriteFontSetBuilder *This, + IDWriteFontFaceReference *ref); + + HRESULT (STDMETHODCALLTYPE *AddFontSet)( + IDWriteFontSetBuilder *This, + IDWriteFontSet *fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSet)( + IDWriteFontSetBuilder *This, + IDWriteFontSet **fontset); + + END_INTERFACE +} IDWriteFontSetBuilderVtbl; + +interface IDWriteFontSetBuilder { + CONST_VTBL IDWriteFontSetBuilderVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSetBuilder_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSetBuilder_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSetBuilder_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSetBuilder methods ***/ +#define IDWriteFontSetBuilder_AddFontFaceReference_(This,ref,props,prop_count) (This)->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count) +#define IDWriteFontSetBuilder_AddFontFaceReference(This,ref) (This)->lpVtbl->AddFontFaceReference(This,ref) +#define IDWriteFontSetBuilder_AddFontSet(This,fontset) (This)->lpVtbl->AddFontSet(This,fontset) +#define IDWriteFontSetBuilder_CreateFontSet(This,fontset) (This)->lpVtbl->CreateFontSet(This,fontset) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder_QueryInterface(IDWriteFontSetBuilder* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder_AddRef(IDWriteFontSetBuilder* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder_Release(IDWriteFontSetBuilder* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSetBuilder methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder_AddFontFaceReference_(IDWriteFontSetBuilder* This,IDWriteFontFaceReference *ref,const DWRITE_FONT_PROPERTY *props,UINT32 prop_count) { + return This->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder_AddFontFaceReference(IDWriteFontSetBuilder* This,IDWriteFontFaceReference *ref) { + return This->lpVtbl->AddFontFaceReference(This,ref); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder_AddFontSet(IDWriteFontSetBuilder* This,IDWriteFontSet *fontset) { + return This->lpVtbl->AddFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder_CreateFontSet(IDWriteFontSetBuilder* This,IDWriteFontSet **fontset) { + return This->lpVtbl->CreateFontSet(This,fontset); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSetBuilder_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSetBuilder1 interface + */ +#ifndef __IDWriteFontSetBuilder1_INTERFACE_DEFINED__ +#define __IDWriteFontSetBuilder1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSetBuilder1, 0x3ff7715f, 0x3cdc, 0x4dc6, 0x9b,0x72, 0xec,0x56,0x21,0xdc,0xca,0xfd); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("3ff7715f-3cdc-4dc6-9b72-ec5621dccafd") +IDWriteFontSetBuilder1 : public IDWriteFontSetBuilder +{ + virtual HRESULT STDMETHODCALLTYPE AddFontFile( + IDWriteFontFile *file) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSetBuilder1, 0x3ff7715f, 0x3cdc, 0x4dc6, 0x9b,0x72, 0xec,0x56,0x21,0xdc,0xca,0xfd) +#endif +#else +typedef struct IDWriteFontSetBuilder1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSetBuilder1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSetBuilder1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSetBuilder1 *This); + + /*** IDWriteFontSetBuilder methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference_)( + IDWriteFontSetBuilder1 *This, + IDWriteFontFaceReference *ref, + const DWRITE_FONT_PROPERTY *props, + UINT32 prop_count); + + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference)( + IDWriteFontSetBuilder1 *This, + IDWriteFontFaceReference *ref); + + HRESULT (STDMETHODCALLTYPE *AddFontSet)( + IDWriteFontSetBuilder1 *This, + IDWriteFontSet *fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSet)( + IDWriteFontSetBuilder1 *This, + IDWriteFontSet **fontset); + + /*** IDWriteFontSetBuilder1 methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFontFile)( + IDWriteFontSetBuilder1 *This, + IDWriteFontFile *file); + + END_INTERFACE +} IDWriteFontSetBuilder1Vtbl; + +interface IDWriteFontSetBuilder1 { + CONST_VTBL IDWriteFontSetBuilder1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSetBuilder1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSetBuilder1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSetBuilder1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSetBuilder methods ***/ +#define IDWriteFontSetBuilder1_AddFontFaceReference_(This,ref,props,prop_count) (This)->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count) +#define IDWriteFontSetBuilder1_AddFontFaceReference(This,ref) (This)->lpVtbl->AddFontFaceReference(This,ref) +#define IDWriteFontSetBuilder1_AddFontSet(This,fontset) (This)->lpVtbl->AddFontSet(This,fontset) +#define IDWriteFontSetBuilder1_CreateFontSet(This,fontset) (This)->lpVtbl->CreateFontSet(This,fontset) +/*** IDWriteFontSetBuilder1 methods ***/ +#define IDWriteFontSetBuilder1_AddFontFile(This,file) (This)->lpVtbl->AddFontFile(This,file) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_QueryInterface(IDWriteFontSetBuilder1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder1_AddRef(IDWriteFontSetBuilder1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder1_Release(IDWriteFontSetBuilder1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSetBuilder methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_AddFontFaceReference_(IDWriteFontSetBuilder1* This,IDWriteFontFaceReference *ref,const DWRITE_FONT_PROPERTY *props,UINT32 prop_count) { + return This->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_AddFontFaceReference(IDWriteFontSetBuilder1* This,IDWriteFontFaceReference *ref) { + return This->lpVtbl->AddFontFaceReference(This,ref); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_AddFontSet(IDWriteFontSetBuilder1* This,IDWriteFontSet *fontset) { + return This->lpVtbl->AddFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_CreateFontSet(IDWriteFontSetBuilder1* This,IDWriteFontSet **fontset) { + return This->lpVtbl->CreateFontSet(This,fontset); +} +/*** IDWriteFontSetBuilder1 methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder1_AddFontFile(IDWriteFontSetBuilder1* This,IDWriteFontFile *file) { + return This->lpVtbl->AddFontFile(This,file); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSetBuilder1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontSetBuilder2 interface + */ +#ifndef __IDWriteFontSetBuilder2_INTERFACE_DEFINED__ +#define __IDWriteFontSetBuilder2_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontSetBuilder2, 0xee5ba612, 0xb131, 0x463c, 0x8f,0x4f, 0x31,0x89,0xb9,0x40,0x1e,0x45); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("ee5ba612-b131-463c-8f4f-3189b9401e45") +IDWriteFontSetBuilder2 : public IDWriteFontSetBuilder1 +{ + virtual HRESULT STDMETHODCALLTYPE AddFont( + IDWriteFontFile *fontfile, + UINT32 face_index, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddFontFile( + const WCHAR *filepath) = 0; + + using IDWriteFontSetBuilder1::AddFontFile; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontSetBuilder2, 0xee5ba612, 0xb131, 0x463c, 0x8f,0x4f, 0x31,0x89,0xb9,0x40,0x1e,0x45) +#endif +#else +typedef struct IDWriteFontSetBuilder2Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontSetBuilder2 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontSetBuilder2 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontSetBuilder2 *This); + + /*** IDWriteFontSetBuilder methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference_)( + IDWriteFontSetBuilder2 *This, + IDWriteFontFaceReference *ref, + const DWRITE_FONT_PROPERTY *props, + UINT32 prop_count); + + HRESULT (STDMETHODCALLTYPE *AddFontFaceReference)( + IDWriteFontSetBuilder2 *This, + IDWriteFontFaceReference *ref); + + HRESULT (STDMETHODCALLTYPE *AddFontSet)( + IDWriteFontSetBuilder2 *This, + IDWriteFontSet *fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSet)( + IDWriteFontSetBuilder2 *This, + IDWriteFontSet **fontset); + + /*** IDWriteFontSetBuilder1 methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFontFile)( + IDWriteFontSetBuilder2 *This, + IDWriteFontFile *file); + + /*** IDWriteFontSetBuilder2 methods ***/ + HRESULT (STDMETHODCALLTYPE *AddFont)( + IDWriteFontSetBuilder2 *This, + IDWriteFontFile *fontfile, + UINT32 face_index, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_values, + const DWRITE_FONT_AXIS_RANGE *axis_ranges, + UINT32 num_ranges, + const DWRITE_FONT_PROPERTY *props, + UINT32 num_properties); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontSetBuilder2_AddFontFile)( + IDWriteFontSetBuilder2 *This, + const WCHAR *filepath); + + END_INTERFACE +} IDWriteFontSetBuilder2Vtbl; + +interface IDWriteFontSetBuilder2 { + CONST_VTBL IDWriteFontSetBuilder2Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontSetBuilder2_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontSetBuilder2_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontSetBuilder2_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontSetBuilder methods ***/ +#define IDWriteFontSetBuilder2_AddFontFaceReference_(This,ref,props,prop_count) (This)->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count) +#define IDWriteFontSetBuilder2_AddFontFaceReference(This,ref) (This)->lpVtbl->AddFontFaceReference(This,ref) +#define IDWriteFontSetBuilder2_AddFontSet(This,fontset) (This)->lpVtbl->AddFontSet(This,fontset) +#define IDWriteFontSetBuilder2_CreateFontSet(This,fontset) (This)->lpVtbl->CreateFontSet(This,fontset) +/*** IDWriteFontSetBuilder1 methods ***/ +/*** IDWriteFontSetBuilder2 methods ***/ +#define IDWriteFontSetBuilder2_AddFont(This,fontfile,face_index,simulations,axis_values,num_values,axis_ranges,num_ranges,props,num_properties) (This)->lpVtbl->AddFont(This,fontfile,face_index,simulations,axis_values,num_values,axis_ranges,num_ranges,props,num_properties) +#define IDWriteFontSetBuilder2_AddFontFile(This,filepath) (This)->lpVtbl->IDWriteFontSetBuilder2_AddFontFile(This,filepath) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_QueryInterface(IDWriteFontSetBuilder2* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder2_AddRef(IDWriteFontSetBuilder2* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontSetBuilder2_Release(IDWriteFontSetBuilder2* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontSetBuilder methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_AddFontFaceReference_(IDWriteFontSetBuilder2* This,IDWriteFontFaceReference *ref,const DWRITE_FONT_PROPERTY *props,UINT32 prop_count) { + return This->lpVtbl->AddFontFaceReference_(This,ref,props,prop_count); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_AddFontFaceReference(IDWriteFontSetBuilder2* This,IDWriteFontFaceReference *ref) { + return This->lpVtbl->AddFontFaceReference(This,ref); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_AddFontSet(IDWriteFontSetBuilder2* This,IDWriteFontSet *fontset) { + return This->lpVtbl->AddFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_CreateFontSet(IDWriteFontSetBuilder2* This,IDWriteFontSet **fontset) { + return This->lpVtbl->CreateFontSet(This,fontset); +} +/*** IDWriteFontSetBuilder1 methods ***/ +/*** IDWriteFontSetBuilder2 methods ***/ +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_AddFont(IDWriteFontSetBuilder2* This,IDWriteFontFile *fontfile,UINT32 face_index,DWRITE_FONT_SIMULATIONS simulations,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_values,const DWRITE_FONT_AXIS_RANGE *axis_ranges,UINT32 num_ranges,const DWRITE_FONT_PROPERTY *props,UINT32 num_properties) { + return This->lpVtbl->AddFont(This,fontfile,face_index,simulations,axis_values,num_values,axis_ranges,num_ranges,props,num_properties); +} +static FORCEINLINE HRESULT IDWriteFontSetBuilder2_AddFontFile(IDWriteFontSetBuilder2* This,const WCHAR *filepath) { + return This->lpVtbl->IDWriteFontSetBuilder2_AddFontFile(This,filepath); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontSetBuilder2_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFactory3 interface + */ +#ifndef __IDWriteFactory3_INTERFACE_DEFINED__ +#define __IDWriteFactory3_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("9a1b41c3-d3bb-466a-87fc-fe67556a3b65") +IDWriteFactory3 : public IDWriteFactory2 +{ + virtual HRESULT STDMETHODCALLTYPE CreateGlyphRunAnalysis( + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis) = 0; + + using IDWriteFactory::CreateGlyphRunAnalysis; + using IDWriteFactory2::CreateGlyphRunAnalysis; + + virtual HRESULT STDMETHODCALLTYPE CreateCustomRenderingParams( + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params) = 0; + + using IDWriteFactory::CreateCustomRenderingParams; + using IDWriteFactory1::CreateCustomRenderingParams; + using IDWriteFactory2::CreateCustomRenderingParams; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFaceReference( + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFaceReference( + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSystemFontSet( + IDWriteFontSet **fontset) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontSetBuilder( + IDWriteFontSetBuilder **builder) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontCollectionFromFontSet( + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSystemFontCollection( + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates) = 0; + + using IDWriteFactory::GetSystemFontCollection; + + virtual HRESULT STDMETHODCALLTYPE GetFontDownloadQueue( + IDWriteFontDownloadQueue **queue) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65) +#endif +#else +typedef struct IDWriteFactory3Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFactory3 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFactory3 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFactory3 *This); + + /*** IDWriteFactory methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontCollection)( + IDWriteFactory3 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontCollection)( + IDWriteFactory3 *This, + IDWriteFontCollectionLoader *loader, + const void *key, + UINT32 key_size, + IDWriteFontCollection **collection); + + HRESULT (STDMETHODCALLTYPE *RegisterFontCollectionLoader)( + IDWriteFactory3 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontCollectionLoader)( + IDWriteFactory3 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReference)( + IDWriteFactory3 *This, + const WCHAR *path, + const FILETIME *writetime, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontFileReference)( + IDWriteFactory3 *This, + const void *reference_key, + UINT32 key_size, + IDWriteFontFileLoader *loader, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFactory3 *This, + DWRITE_FONT_FACE_TYPE facetype, + UINT32 files_number, + IDWriteFontFile *const *font_files, + UINT32 index, + DWRITE_FONT_SIMULATIONS sim_flags, + IDWriteFontFace **font_face); + + HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)( + IDWriteFactory3 *This, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateMonitorRenderingParams)( + IDWriteFactory3 *This, + HMONITOR monitor, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)( + IDWriteFactory3 *This, + FLOAT gamma, + FLOAT enhancedContrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *RegisterFontFileLoader)( + IDWriteFactory3 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontFileLoader)( + IDWriteFactory3 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateTextFormat)( + IDWriteFactory3 *This, + const WCHAR *family_name, + IDWriteFontCollection *collection, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, + FLOAT size, + const WCHAR *locale, + IDWriteTextFormat **format); + + HRESULT (STDMETHODCALLTYPE *CreateTypography)( + IDWriteFactory3 *This, + IDWriteTypography **typography); + + HRESULT (STDMETHODCALLTYPE *GetGdiInterop)( + IDWriteFactory3 *This, + IDWriteGdiInterop **gdi_interop); + + HRESULT (STDMETHODCALLTYPE *CreateTextLayout)( + IDWriteFactory3 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT max_width, + FLOAT max_height, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateGdiCompatibleTextLayout)( + IDWriteFactory3 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT layout_width, + FLOAT layout_height, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateEllipsisTrimmingSign)( + IDWriteFactory3 *This, + IDWriteTextFormat *format, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *CreateTextAnalyzer)( + IDWriteFactory3 *This, + IDWriteTextAnalyzer **analyzer); + + HRESULT (STDMETHODCALLTYPE *CreateNumberSubstitution)( + IDWriteFactory3 *This, + DWRITE_NUMBER_SUBSTITUTION_METHOD method, + const WCHAR *locale, + WINBOOL ignore_user_override, + IDWriteNumberSubstitution **substitution); + + HRESULT (STDMETHODCALLTYPE *CreateGlyphRunAnalysis)( + IDWriteFactory3 *This, + const DWRITE_GLYPH_RUN *glyph_run, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + FLOAT baseline_x, + FLOAT baseline_y, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetEudcFontCollection)( + IDWriteFactory3 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory1_CreateCustomRenderingParams)( + IDWriteFactory3 *This, + FLOAT gamma, + FLOAT enhcontrast, + FLOAT enhcontrast_grayscale, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams1 **params); + + /*** IDWriteFactory2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontFallback)( + IDWriteFactory3 *This, + IDWriteFontFallback **fallback); + + HRESULT (STDMETHODCALLTYPE *CreateFontFallbackBuilder)( + IDWriteFactory3 *This, + IDWriteFontFallbackBuilder **fallbackbuilder); + + HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)( + IDWriteFactory3 *This, + FLOAT originX, + FLOAT originY, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, + DWRITE_MEASURING_MODE mode, + const DWRITE_MATRIX *transform, + UINT32 palette_index, + IDWriteColorGlyphRunEnumerator **colorlayers); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateCustomRenderingParams)( + IDWriteFactory3 *This, + FLOAT gamma, + FLOAT contrast, + FLOAT grayscalecontrast, + FLOAT cleartypeLevel, + DWRITE_PIXEL_GEOMETRY pixelGeometry, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_GRID_FIT_MODE gridFitMode, + IDWriteRenderingParams2 **params); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateGlyphRunAnalysis)( + IDWriteFactory3 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GRID_FIT_MODE gridFitMode, + DWRITE_TEXT_ANTIALIAS_MODE antialiasMode, + FLOAT originX, + FLOAT originY, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateGlyphRunAnalysis)( + IDWriteFactory3 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateCustomRenderingParams)( + IDWriteFactory3 *This, + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference_)( + IDWriteFactory3 *This, + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFactory3 *This, + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *GetSystemFontSet)( + IDWriteFactory3 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSetBuilder)( + IDWriteFactory3 *This, + IDWriteFontSetBuilder **builder); + + HRESULT (STDMETHODCALLTYPE *CreateFontCollectionFromFontSet)( + IDWriteFactory3 *This, + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_GetSystemFontCollection)( + IDWriteFactory3 *This, + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *GetFontDownloadQueue)( + IDWriteFactory3 *This, + IDWriteFontDownloadQueue **queue); + + END_INTERFACE +} IDWriteFactory3Vtbl; + +interface IDWriteFactory3 { + CONST_VTBL IDWriteFactory3Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFactory3_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFactory3_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFactory3_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFactory methods ***/ +#define IDWriteFactory3_CreateCustomFontCollection(This,loader,key,key_size,collection) (This)->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection) +#define IDWriteFactory3_RegisterFontCollectionLoader(This,loader) (This)->lpVtbl->RegisterFontCollectionLoader(This,loader) +#define IDWriteFactory3_UnregisterFontCollectionLoader(This,loader) (This)->lpVtbl->UnregisterFontCollectionLoader(This,loader) +#define IDWriteFactory3_CreateFontFileReference(This,path,writetime,font_file) (This)->lpVtbl->CreateFontFileReference(This,path,writetime,font_file) +#define IDWriteFactory3_CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) (This)->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) +#define IDWriteFactory3_CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) (This)->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) +#define IDWriteFactory3_CreateRenderingParams(This,params) (This)->lpVtbl->CreateRenderingParams(This,params) +#define IDWriteFactory3_CreateMonitorRenderingParams(This,monitor,params) (This)->lpVtbl->CreateMonitorRenderingParams(This,monitor,params) +#define IDWriteFactory3_RegisterFontFileLoader(This,loader) (This)->lpVtbl->RegisterFontFileLoader(This,loader) +#define IDWriteFactory3_UnregisterFontFileLoader(This,loader) (This)->lpVtbl->UnregisterFontFileLoader(This,loader) +#define IDWriteFactory3_CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) (This)->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) +#define IDWriteFactory3_CreateTypography(This,typography) (This)->lpVtbl->CreateTypography(This,typography) +#define IDWriteFactory3_GetGdiInterop(This,gdi_interop) (This)->lpVtbl->GetGdiInterop(This,gdi_interop) +#define IDWriteFactory3_CreateTextLayout(This,string,len,format,max_width,max_height,layout) (This)->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout) +#define IDWriteFactory3_CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) (This)->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) +#define IDWriteFactory3_CreateEllipsisTrimmingSign(This,format,trimming_sign) (This)->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign) +#define IDWriteFactory3_CreateTextAnalyzer(This,analyzer) (This)->lpVtbl->CreateTextAnalyzer(This,analyzer) +#define IDWriteFactory3_CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) (This)->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) +/*** IDWriteFactory1 methods ***/ +#define IDWriteFactory3_GetEudcFontCollection(This,collection,check_for_updates) (This)->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates) +/*** IDWriteFactory2 methods ***/ +#define IDWriteFactory3_GetSystemFontFallback(This,fallback) (This)->lpVtbl->GetSystemFontFallback(This,fallback) +#define IDWriteFactory3_CreateFontFallbackBuilder(This,fallbackbuilder) (This)->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder) +#define IDWriteFactory3_TranslateColorGlyphRun(This,originX,originY,run,rundescr,mode,transform,palette_index,colorlayers) (This)->lpVtbl->TranslateColorGlyphRun(This,originX,originY,run,rundescr,mode,transform,palette_index,colorlayers) +/*** IDWriteFactory3 methods ***/ +#define IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) (This)->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) +#define IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) (This)->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) +#define IDWriteFactory3_CreateFontFaceReference_(This,file,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference) +#define IDWriteFactory3_CreateFontFaceReference(This,path,writetime,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference) +#define IDWriteFactory3_GetSystemFontSet(This,fontset) (This)->lpVtbl->GetSystemFontSet(This,fontset) +#define IDWriteFactory3_CreateFontSetBuilder(This,builder) (This)->lpVtbl->CreateFontSetBuilder(This,builder) +#define IDWriteFactory3_CreateFontCollectionFromFontSet(This,fontset,collection) (This)->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection) +#define IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) (This)->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) +#define IDWriteFactory3_GetFontDownloadQueue(This,queue) (This)->lpVtbl->GetFontDownloadQueue(This,queue) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFactory3_QueryInterface(IDWriteFactory3* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFactory3_AddRef(IDWriteFactory3* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFactory3_Release(IDWriteFactory3* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFactory methods ***/ +static FORCEINLINE HRESULT IDWriteFactory3_CreateCustomFontCollection(IDWriteFactory3* This,IDWriteFontCollectionLoader *loader,const void *key,UINT32 key_size,IDWriteFontCollection **collection) { + return This->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection); +} +static FORCEINLINE HRESULT IDWriteFactory3_RegisterFontCollectionLoader(IDWriteFactory3* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->RegisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory3_UnregisterFontCollectionLoader(IDWriteFactory3* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->UnregisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontFileReference(IDWriteFactory3* This,const WCHAR *path,const FILETIME *writetime,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateFontFileReference(This,path,writetime,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateCustomFontFileReference(IDWriteFactory3* This,const void *reference_key,UINT32 key_size,IDWriteFontFileLoader *loader,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontFace(IDWriteFactory3* This,DWRITE_FONT_FACE_TYPE facetype,UINT32 files_number,IDWriteFontFile *const *font_files,UINT32 index,DWRITE_FONT_SIMULATIONS sim_flags,IDWriteFontFace **font_face) { + return This->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateRenderingParams(IDWriteFactory3* This,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateRenderingParams(This,params); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateMonitorRenderingParams(IDWriteFactory3* This,HMONITOR monitor,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateMonitorRenderingParams(This,monitor,params); +} +static FORCEINLINE HRESULT IDWriteFactory3_RegisterFontFileLoader(IDWriteFactory3* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->RegisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory3_UnregisterFontFileLoader(IDWriteFactory3* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->UnregisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateTextFormat(IDWriteFactory3* This,const WCHAR *family_name,IDWriteFontCollection *collection,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STYLE style,DWRITE_FONT_STRETCH stretch,FLOAT size,const WCHAR *locale,IDWriteTextFormat **format) { + return This->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateTypography(IDWriteFactory3* This,IDWriteTypography **typography) { + return This->lpVtbl->CreateTypography(This,typography); +} +static FORCEINLINE HRESULT IDWriteFactory3_GetGdiInterop(IDWriteFactory3* This,IDWriteGdiInterop **gdi_interop) { + return This->lpVtbl->GetGdiInterop(This,gdi_interop); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateTextLayout(IDWriteFactory3* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT max_width,FLOAT max_height,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateGdiCompatibleTextLayout(IDWriteFactory3* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT layout_width,FLOAT layout_height,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateEllipsisTrimmingSign(IDWriteFactory3* This,IDWriteTextFormat *format,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateTextAnalyzer(IDWriteFactory3* This,IDWriteTextAnalyzer **analyzer) { + return This->lpVtbl->CreateTextAnalyzer(This,analyzer); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateNumberSubstitution(IDWriteFactory3* This,DWRITE_NUMBER_SUBSTITUTION_METHOD method,const WCHAR *locale,WINBOOL ignore_user_override,IDWriteNumberSubstitution **substitution) { + return This->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution); +} +/*** IDWriteFactory1 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory3_GetEudcFontCollection(IDWriteFactory3* This,IDWriteFontCollection **collection,WINBOOL check_for_updates) { + return This->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates); +} +/*** IDWriteFactory2 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory3_GetSystemFontFallback(IDWriteFactory3* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetSystemFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontFallbackBuilder(IDWriteFactory3* This,IDWriteFontFallbackBuilder **fallbackbuilder) { + return This->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder); +} +static FORCEINLINE HRESULT IDWriteFactory3_TranslateColorGlyphRun(IDWriteFactory3* This,FLOAT originX,FLOAT originY,const DWRITE_GLYPH_RUN *run,const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr,DWRITE_MEASURING_MODE mode,const DWRITE_MATRIX *transform,UINT32 palette_index,IDWriteColorGlyphRunEnumerator **colorlayers) { + return This->lpVtbl->TranslateColorGlyphRun(This,originX,originY,run,rundescr,mode,transform,palette_index,colorlayers); +} +/*** IDWriteFactory3 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory3_CreateGlyphRunAnalysis(IDWriteFactory3* This,const DWRITE_GLYPH_RUN *run,const DWRITE_MATRIX *transform,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_MEASURING_MODE measuring_mode,DWRITE_GRID_FIT_MODE gridfit_mode,DWRITE_TEXT_ANTIALIAS_MODE antialias_mode,FLOAT origin_x,FLOAT origin_y,IDWriteGlyphRunAnalysis **analysis) { + return This->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateCustomRenderingParams(IDWriteFactory3* This,FLOAT gamma,FLOAT enhanced_contrast,FLOAT grayscale_enhanced_contrast,FLOAT cleartype_level,DWRITE_PIXEL_GEOMETRY pixel_geometry,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_GRID_FIT_MODE gridfit_mode,IDWriteRenderingParams3 **params) { + return This->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontFaceReference_(IDWriteFactory3* This,IDWriteFontFile *file,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontFaceReference(IDWriteFactory3* This,const WCHAR *path,const FILETIME *writetime,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory3_GetSystemFontSet(IDWriteFactory3* This,IDWriteFontSet **fontset) { + return This->lpVtbl->GetSystemFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontSetBuilder(IDWriteFactory3* This,IDWriteFontSetBuilder **builder) { + return This->lpVtbl->CreateFontSetBuilder(This,builder); +} +static FORCEINLINE HRESULT IDWriteFactory3_CreateFontCollectionFromFontSet(IDWriteFactory3* This,IDWriteFontSet *fontset,IDWriteFontCollection1 **collection) { + return This->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection); +} +static FORCEINLINE HRESULT IDWriteFactory3_GetSystemFontCollection(IDWriteFactory3* This,WINBOOL include_downloadable,IDWriteFontCollection1 **collection,WINBOOL check_for_updates) { + return This->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates); +} +static FORCEINLINE HRESULT IDWriteFactory3_GetFontDownloadQueue(IDWriteFactory3* This,IDWriteFontDownloadQueue **queue) { + return This->lpVtbl->GetFontDownloadQueue(This,queue); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFactory3_INTERFACE_DEFINED__ */ + +typedef struct DWRITE_GLYPH_IMAGE_DATA { + const void *imageData; + UINT32 imageDataSize; + UINT32 uniqueDataId; + UINT32 pixelsPerEm; + D2D1_SIZE_U pixelSize; + D2D1_POINT_2L horizontalLeftOrigin; + D2D1_POINT_2L horizontalRightOrigin; + D2D1_POINT_2L verticalTopOrigin; + D2D1_POINT_2L verticalBottomOrigin; +} DWRITE_GLYPH_IMAGE_DATA; +/***************************************************************************** + * IDWriteFontFace4 interface + */ +#ifndef __IDWriteFontFace4_INTERFACE_DEFINED__ +#define __IDWriteFontFace4_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFace4, 0x27f2a904, 0x4eb8, 0x441d, 0x96,0x78, 0x05,0x63,0xf5,0x3e,0x3e,0x2f); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("27f2a904-4eb8-441d-9678-0563f53e3e2f") +IDWriteFontFace4 : public IDWriteFontFace3 +{ + virtual HRESULT STDMETHODCALLTYPE GetGlyphImageFormats( + UINT16 glyph, + UINT32 ppem_first, + UINT32 ppem_last, + DWRITE_GLYPH_IMAGE_FORMATS *formats) = 0; + + virtual DWRITE_GLYPH_IMAGE_FORMATS STDMETHODCALLTYPE GetGlyphImageFormats( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGlyphImageData( + UINT16 glyph, + UINT32 ppem, + DWRITE_GLYPH_IMAGE_FORMATS format, + DWRITE_GLYPH_IMAGE_DATA *data, + void **context) = 0; + + virtual void STDMETHODCALLTYPE ReleaseGlyphImageData( + void *context) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFace4, 0x27f2a904, 0x4eb8, 0x441d, 0x96,0x78, 0x05,0x63,0xf5,0x3e,0x3e,0x2f) +#endif +#else +typedef struct IDWriteFontFace4Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFace4 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFace4 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFace4 *This); + + /*** IDWriteFontFace methods ***/ + DWRITE_FONT_FACE_TYPE (STDMETHODCALLTYPE *GetType)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetFiles)( + IDWriteFontFace4 *This, + UINT32 *number_of_files, + IDWriteFontFile **fontfiles); + + UINT32 (STDMETHODCALLTYPE *GetIndex)( + IDWriteFontFace4 *This); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFontFace4 *This); + + WINBOOL (STDMETHODCALLTYPE *IsSymbolFont)( + IDWriteFontFace4 *This); + + void (STDMETHODCALLTYPE *GetMetrics)( + IDWriteFontFace4 *This, + DWRITE_FONT_METRICS *metrics); + + UINT16 (STDMETHODCALLTYPE *GetGlyphCount)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphMetrics)( + IDWriteFontFace4 *This, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)( + IDWriteFontFace4 *This, + const UINT32 *codepoints, + UINT32 count, + UINT16 *glyph_indices); + + HRESULT (STDMETHODCALLTYPE *TryGetFontTable)( + IDWriteFontFace4 *This, + UINT32 table_tag, + const void **table_data, + UINT32 *table_size, + void **context, + WINBOOL *exists); + + void (STDMETHODCALLTYPE *ReleaseFontTable)( + IDWriteFontFace4 *This, + void *table_context); + + HRESULT (STDMETHODCALLTYPE *GetGlyphRunOutline)( + IDWriteFontFace4 *This, + FLOAT emSize, + const UINT16 *glyph_indices, + const FLOAT *glyph_advances, + const DWRITE_GLYPH_OFFSET *glyph_offsets, + UINT32 glyph_count, + WINBOOL is_sideways, + WINBOOL is_rtl, + IDWriteGeometrySink *geometrysink); + + HRESULT (STDMETHODCALLTYPE *GetRecommendedRenderingMode)( + IDWriteFontFace4 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + DWRITE_MEASURING_MODE mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *rendering_mode); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleMetrics)( + IDWriteFontFace4 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)( + IDWriteFontFace4 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + /*** IDWriteFontFace1 methods ***/ + void (STDMETHODCALLTYPE *IDWriteFontFace1_GetMetrics)( + IDWriteFontFace4 *This, + DWRITE_FONT_METRICS1 *metrics); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetGdiCompatibleMetrics)( + IDWriteFontFace4 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS1 *metrics); + + void (STDMETHODCALLTYPE *GetCaretMetrics)( + IDWriteFontFace4 *This, + DWRITE_CARET_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetUnicodeRanges)( + IDWriteFontFace4 *This, + UINT32 max_count, + DWRITE_UNICODE_RANGE *ranges, + UINT32 *count); + + WINBOOL (STDMETHODCALLTYPE *IsMonospacedFont)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphAdvances)( + IDWriteFontFace4 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphAdvances)( + IDWriteFontFace4 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + WINBOOL is_sideways, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances); + + HRESULT (STDMETHODCALLTYPE *GetKerningPairAdjustments)( + IDWriteFontFace4 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *adjustments); + + WINBOOL (STDMETHODCALLTYPE *HasKerningPairs)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetRecommendedRenderingMode)( + IDWriteFontFace4 *This, + FLOAT font_emsize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_RENDERING_MODE *rendering_mode); + + HRESULT (STDMETHODCALLTYPE *GetVerticalGlyphVariants)( + IDWriteFontFace4 *This, + UINT32 glyph_count, + const UINT16 *nominal_indices, + UINT16 *vertical_indices); + + WINBOOL (STDMETHODCALLTYPE *HasVerticalGlyphVariants)( + IDWriteFontFace4 *This); + + /*** IDWriteFontFace2 methods ***/ + WINBOOL (STDMETHODCALLTYPE *IsColorFont)( + IDWriteFontFace4 *This); + + UINT32 (STDMETHODCALLTYPE *GetColorPaletteCount)( + IDWriteFontFace4 *This); + + UINT32 (STDMETHODCALLTYPE *GetPaletteEntryCount)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetPaletteEntries)( + IDWriteFontFace4 *This, + UINT32 palette_index, + UINT32 first_entry_index, + UINT32 entry_count, + DWRITE_COLOR_F *entries); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace2_GetRecommendedRenderingMode)( + IDWriteFontFace4 *This, + FLOAT fontEmSize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuringmode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *renderingmode, + DWRITE_GRID_FIT_MODE *gridfitmode); + + /*** IDWriteFontFace3 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontFace4 *This, + IDWriteFontFaceReference **reference); + + void (STDMETHODCALLTYPE *GetPanose)( + IDWriteFontFace4 *This, + DWRITE_PANOSE *panose); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetWeight)( + IDWriteFontFace4 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetStretch)( + IDWriteFontFace4 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetStyle)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetFamilyNames)( + IDWriteFontFace4 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetFaceNames)( + IDWriteFontFace4 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetInformationalStrings)( + IDWriteFontFace4 *This, + DWRITE_INFORMATIONAL_STRING_ID stringid, + IDWriteLocalizedStrings **strings, + WINBOOL *exists); + + WINBOOL (STDMETHODCALLTYPE *HasCharacter)( + IDWriteFontFace4 *This, + UINT32 character); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace3_GetRecommendedRenderingMode)( + IDWriteFontFace4 *This, + FLOAT emsize, + FLOAT dpi_x, + FLOAT dpi_y, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE1 *rendering_mode, + DWRITE_GRID_FIT_MODE *gridfit_mode); + + WINBOOL (STDMETHODCALLTYPE *IsCharacterLocal)( + IDWriteFontFace4 *This, + UINT32 character); + + WINBOOL (STDMETHODCALLTYPE *IsGlyphLocal)( + IDWriteFontFace4 *This, + UINT16 glyph); + + HRESULT (STDMETHODCALLTYPE *AreCharactersLocal)( + IDWriteFontFace4 *This, + const WCHAR *characters, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + HRESULT (STDMETHODCALLTYPE *AreGlyphsLocal)( + IDWriteFontFace4 *This, + const UINT16 *glyphs, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + /*** IDWriteFontFace4 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetGlyphImageFormats_)( + IDWriteFontFace4 *This, + UINT16 glyph, + UINT32 ppem_first, + UINT32 ppem_last, + DWRITE_GLYPH_IMAGE_FORMATS *formats); + + DWRITE_GLYPH_IMAGE_FORMATS (STDMETHODCALLTYPE *GetGlyphImageFormats)( + IDWriteFontFace4 *This); + + HRESULT (STDMETHODCALLTYPE *GetGlyphImageData)( + IDWriteFontFace4 *This, + UINT16 glyph, + UINT32 ppem, + DWRITE_GLYPH_IMAGE_FORMATS format, + DWRITE_GLYPH_IMAGE_DATA *data, + void **context); + + void (STDMETHODCALLTYPE *ReleaseGlyphImageData)( + IDWriteFontFace4 *This, + void *context); + + END_INTERFACE +} IDWriteFontFace4Vtbl; + +interface IDWriteFontFace4 { + CONST_VTBL IDWriteFontFace4Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFace4_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFace4_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFace4_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFace methods ***/ +#define IDWriteFontFace4_GetType(This) (This)->lpVtbl->GetType(This) +#define IDWriteFontFace4_GetFiles(This,number_of_files,fontfiles) (This)->lpVtbl->GetFiles(This,number_of_files,fontfiles) +#define IDWriteFontFace4_GetIndex(This) (This)->lpVtbl->GetIndex(This) +#define IDWriteFontFace4_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +#define IDWriteFontFace4_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This) +#define IDWriteFontFace4_GetGlyphCount(This) (This)->lpVtbl->GetGlyphCount(This) +#define IDWriteFontFace4_GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) +#define IDWriteFontFace4_GetGlyphIndices(This,codepoints,count,glyph_indices) (This)->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices) +#define IDWriteFontFace4_TryGetFontTable(This,table_tag,table_data,table_size,context,exists) (This)->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists) +#define IDWriteFontFace4_ReleaseFontTable(This,table_context) (This)->lpVtbl->ReleaseFontTable(This,table_context) +#define IDWriteFontFace4_GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) (This)->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) +#define IDWriteFontFace4_GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) +/*** IDWriteFontFace1 methods ***/ +#define IDWriteFontFace4_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics) +#define IDWriteFontFace4_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) (This)->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) +#define IDWriteFontFace4_GetCaretMetrics(This,metrics) (This)->lpVtbl->GetCaretMetrics(This,metrics) +#define IDWriteFontFace4_GetUnicodeRanges(This,max_count,ranges,count) (This)->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count) +#define IDWriteFontFace4_IsMonospacedFont(This) (This)->lpVtbl->IsMonospacedFont(This) +#define IDWriteFontFace4_GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) (This)->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) +#define IDWriteFontFace4_GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) (This)->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) +#define IDWriteFontFace4_GetKerningPairAdjustments(This,glyph_count,indices,adjustments) (This)->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments) +#define IDWriteFontFace4_HasKerningPairs(This) (This)->lpVtbl->HasKerningPairs(This) +#define IDWriteFontFace4_GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) (This)->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) +#define IDWriteFontFace4_HasVerticalGlyphVariants(This) (This)->lpVtbl->HasVerticalGlyphVariants(This) +/*** IDWriteFontFace2 methods ***/ +#define IDWriteFontFace4_IsColorFont(This) (This)->lpVtbl->IsColorFont(This) +#define IDWriteFontFace4_GetColorPaletteCount(This) (This)->lpVtbl->GetColorPaletteCount(This) +#define IDWriteFontFace4_GetPaletteEntryCount(This) (This)->lpVtbl->GetPaletteEntryCount(This) +#define IDWriteFontFace4_GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) (This)->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) +/*** IDWriteFontFace3 methods ***/ +#define IDWriteFontFace4_GetFontFaceReference(This,reference) (This)->lpVtbl->GetFontFaceReference(This,reference) +#define IDWriteFontFace4_GetPanose(This,panose) (This)->lpVtbl->GetPanose(This,panose) +#define IDWriteFontFace4_GetWeight(This) (This)->lpVtbl->GetWeight(This) +#define IDWriteFontFace4_GetStretch(This) (This)->lpVtbl->GetStretch(This) +#define IDWriteFontFace4_GetStyle(This) (This)->lpVtbl->GetStyle(This) +#define IDWriteFontFace4_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) +#define IDWriteFontFace4_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names) +#define IDWriteFontFace4_GetInformationalStrings(This,stringid,strings,exists) (This)->lpVtbl->GetInformationalStrings(This,stringid,strings,exists) +#define IDWriteFontFace4_HasCharacter(This,character) (This)->lpVtbl->HasCharacter(This,character) +#define IDWriteFontFace4_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) (This)->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) +#define IDWriteFontFace4_IsCharacterLocal(This,character) (This)->lpVtbl->IsCharacterLocal(This,character) +#define IDWriteFontFace4_IsGlyphLocal(This,glyph) (This)->lpVtbl->IsGlyphLocal(This,glyph) +#define IDWriteFontFace4_AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) (This)->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) +#define IDWriteFontFace4_AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) (This)->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) +/*** IDWriteFontFace4 methods ***/ +#define IDWriteFontFace4_GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats) (This)->lpVtbl->GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats) +#define IDWriteFontFace4_GetGlyphImageFormats(This) (This)->lpVtbl->GetGlyphImageFormats(This) +#define IDWriteFontFace4_GetGlyphImageData(This,glyph,ppem,format,data,context) (This)->lpVtbl->GetGlyphImageData(This,glyph,ppem,format,data,context) +#define IDWriteFontFace4_ReleaseGlyphImageData(This,context) (This)->lpVtbl->ReleaseGlyphImageData(This,context) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace4_QueryInterface(IDWriteFontFace4* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFace4_AddRef(IDWriteFontFace4* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFace4_Release(IDWriteFontFace4* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFace methods ***/ +static FORCEINLINE DWRITE_FONT_FACE_TYPE IDWriteFontFace4_GetType(IDWriteFontFace4* This) { + return This->lpVtbl->GetType(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetFiles(IDWriteFontFace4* This,UINT32 *number_of_files,IDWriteFontFile **fontfiles) { + return This->lpVtbl->GetFiles(This,number_of_files,fontfiles); +} +static FORCEINLINE UINT32 IDWriteFontFace4_GetIndex(IDWriteFontFace4* This) { + return This->lpVtbl->GetIndex(This); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFontFace4_GetSimulations(IDWriteFontFace4* This) { + return This->lpVtbl->GetSimulations(This); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_IsSymbolFont(IDWriteFontFace4* This) { + return This->lpVtbl->IsSymbolFont(This); +} +static FORCEINLINE UINT16 IDWriteFontFace4_GetGlyphCount(IDWriteFontFace4* This) { + return This->lpVtbl->GetGlyphCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetDesignGlyphMetrics(IDWriteFontFace4* This,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGlyphIndices(IDWriteFontFace4* This,const UINT32 *codepoints,UINT32 count,UINT16 *glyph_indices) { + return This->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices); +} +static FORCEINLINE HRESULT IDWriteFontFace4_TryGetFontTable(IDWriteFontFace4* This,UINT32 table_tag,const void **table_data,UINT32 *table_size,void **context,WINBOOL *exists) { + return This->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists); +} +static FORCEINLINE void IDWriteFontFace4_ReleaseFontTable(IDWriteFontFace4* This,void *table_context) { + This->lpVtbl->ReleaseFontTable(This,table_context); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGlyphRunOutline(IDWriteFontFace4* This,FLOAT emSize,const UINT16 *glyph_indices,const FLOAT *glyph_advances,const DWRITE_GLYPH_OFFSET *glyph_offsets,UINT32 glyph_count,WINBOOL is_sideways,WINBOOL is_rtl,IDWriteGeometrySink *geometrysink) { + return This->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGdiCompatibleGlyphMetrics(IDWriteFontFace4* This,FLOAT emSize,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways); +} +/*** IDWriteFontFace1 methods ***/ +static FORCEINLINE void IDWriteFontFace4_GetMetrics(IDWriteFontFace4* This,DWRITE_FONT_METRICS1 *metrics) { + This->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGdiCompatibleMetrics(IDWriteFontFace4* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,DWRITE_FONT_METRICS1 *metrics) { + return This->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics); +} +static FORCEINLINE void IDWriteFontFace4_GetCaretMetrics(IDWriteFontFace4* This,DWRITE_CARET_METRICS *metrics) { + This->lpVtbl->GetCaretMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetUnicodeRanges(IDWriteFontFace4* This,UINT32 max_count,DWRITE_UNICODE_RANGE *ranges,UINT32 *count) { + return This->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_IsMonospacedFont(IDWriteFontFace4* This) { + return This->lpVtbl->IsMonospacedFont(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetDesignGlyphAdvances(IDWriteFontFace4* This,UINT32 glyph_count,const UINT16 *indices,INT32 *advances,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGdiCompatibleGlyphAdvances(IDWriteFontFace4* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,WINBOOL is_sideways,UINT32 glyph_count,const UINT16 *indices,INT32 *advances) { + return This->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetKerningPairAdjustments(IDWriteFontFace4* This,UINT32 glyph_count,const UINT16 *indices,INT32 *adjustments) { + return This->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_HasKerningPairs(IDWriteFontFace4* This) { + return This->lpVtbl->HasKerningPairs(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetVerticalGlyphVariants(IDWriteFontFace4* This,UINT32 glyph_count,const UINT16 *nominal_indices,UINT16 *vertical_indices) { + return This->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_HasVerticalGlyphVariants(IDWriteFontFace4* This) { + return This->lpVtbl->HasVerticalGlyphVariants(This); +} +/*** IDWriteFontFace2 methods ***/ +static FORCEINLINE WINBOOL IDWriteFontFace4_IsColorFont(IDWriteFontFace4* This) { + return This->lpVtbl->IsColorFont(This); +} +static FORCEINLINE UINT32 IDWriteFontFace4_GetColorPaletteCount(IDWriteFontFace4* This) { + return This->lpVtbl->GetColorPaletteCount(This); +} +static FORCEINLINE UINT32 IDWriteFontFace4_GetPaletteEntryCount(IDWriteFontFace4* This) { + return This->lpVtbl->GetPaletteEntryCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetPaletteEntries(IDWriteFontFace4* This,UINT32 palette_index,UINT32 first_entry_index,UINT32 entry_count,DWRITE_COLOR_F *entries) { + return This->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries); +} +/*** IDWriteFontFace3 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace4_GetFontFaceReference(IDWriteFontFace4* This,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,reference); +} +static FORCEINLINE void IDWriteFontFace4_GetPanose(IDWriteFontFace4* This,DWRITE_PANOSE *panose) { + This->lpVtbl->GetPanose(This,panose); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteFontFace4_GetWeight(IDWriteFontFace4* This) { + return This->lpVtbl->GetWeight(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteFontFace4_GetStretch(IDWriteFontFace4* This) { + return This->lpVtbl->GetStretch(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteFontFace4_GetStyle(IDWriteFontFace4* This) { + return This->lpVtbl->GetStyle(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetFamilyNames(IDWriteFontFace4* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFamilyNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetFaceNames(IDWriteFontFace4* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFaceNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetInformationalStrings(IDWriteFontFace4* This,DWRITE_INFORMATIONAL_STRING_ID stringid,IDWriteLocalizedStrings **strings,WINBOOL *exists) { + return This->lpVtbl->GetInformationalStrings(This,stringid,strings,exists); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_HasCharacter(IDWriteFontFace4* This,UINT32 character) { + return This->lpVtbl->HasCharacter(This,character); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetRecommendedRenderingMode(IDWriteFontFace4* This,FLOAT emsize,FLOAT dpi_x,FLOAT dpi_y,const DWRITE_MATRIX *transform,WINBOOL is_sideways,DWRITE_OUTLINE_THRESHOLD threshold,DWRITE_MEASURING_MODE measuring_mode,IDWriteRenderingParams *params,DWRITE_RENDERING_MODE1 *rendering_mode,DWRITE_GRID_FIT_MODE *gridfit_mode) { + return This->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_IsCharacterLocal(IDWriteFontFace4* This,UINT32 character) { + return This->lpVtbl->IsCharacterLocal(This,character); +} +static FORCEINLINE WINBOOL IDWriteFontFace4_IsGlyphLocal(IDWriteFontFace4* This,UINT16 glyph) { + return This->lpVtbl->IsGlyphLocal(This,glyph); +} +static FORCEINLINE HRESULT IDWriteFontFace4_AreCharactersLocal(IDWriteFontFace4* This,const WCHAR *characters,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local); +} +static FORCEINLINE HRESULT IDWriteFontFace4_AreGlyphsLocal(IDWriteFontFace4* This,const UINT16 *glyphs,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local); +} +/*** IDWriteFontFace4 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace4_GetGlyphImageFormats_(IDWriteFontFace4* This,UINT16 glyph,UINT32 ppem_first,UINT32 ppem_last,DWRITE_GLYPH_IMAGE_FORMATS *formats) { + return This->lpVtbl->GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats); +} +static FORCEINLINE DWRITE_GLYPH_IMAGE_FORMATS IDWriteFontFace4_GetGlyphImageFormats(IDWriteFontFace4* This) { + return This->lpVtbl->GetGlyphImageFormats(This); +} +static FORCEINLINE HRESULT IDWriteFontFace4_GetGlyphImageData(IDWriteFontFace4* This,UINT16 glyph,UINT32 ppem,DWRITE_GLYPH_IMAGE_FORMATS format,DWRITE_GLYPH_IMAGE_DATA *data,void **context) { + return This->lpVtbl->GetGlyphImageData(This,glyph,ppem,format,data,context); +} +static FORCEINLINE void IDWriteFontFace4_ReleaseGlyphImageData(IDWriteFontFace4* This,void *context) { + This->lpVtbl->ReleaseGlyphImageData(This,context); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFace4_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFontFace5 interface + */ +#ifndef __IDWriteFontFace5_INTERFACE_DEFINED__ +#define __IDWriteFontFace5_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFontFace5, 0x98eff3a5, 0xb667, 0x479a, 0xb1,0x45, 0xe2,0xfa,0x5b,0x9f,0xdc,0x29); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("98eff3a5-b667-479a-b145-e2fa5b9fdc29") +IDWriteFontFace5 : public IDWriteFontFace4 +{ + virtual UINT32 STDMETHODCALLTYPE GetFontAxisValueCount( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontAxisValues( + DWRITE_FONT_AXIS_VALUE *values, + UINT32 value_count) = 0; + + virtual WINBOOL STDMETHODCALLTYPE HasVariations( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFontResource( + IDWriteFontResource **resource) = 0; + + virtual WINBOOL STDMETHODCALLTYPE Equals( + IDWriteFontFace *fontface) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFontFace5, 0x98eff3a5, 0xb667, 0x479a, 0xb1,0x45, 0xe2,0xfa,0x5b,0x9f,0xdc,0x29) +#endif +#else +typedef struct IDWriteFontFace5Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFontFace5 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFontFace5 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFontFace5 *This); + + /*** IDWriteFontFace methods ***/ + DWRITE_FONT_FACE_TYPE (STDMETHODCALLTYPE *GetType)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetFiles)( + IDWriteFontFace5 *This, + UINT32 *number_of_files, + IDWriteFontFile **fontfiles); + + UINT32 (STDMETHODCALLTYPE *GetIndex)( + IDWriteFontFace5 *This); + + DWRITE_FONT_SIMULATIONS (STDMETHODCALLTYPE *GetSimulations)( + IDWriteFontFace5 *This); + + WINBOOL (STDMETHODCALLTYPE *IsSymbolFont)( + IDWriteFontFace5 *This); + + void (STDMETHODCALLTYPE *GetMetrics)( + IDWriteFontFace5 *This, + DWRITE_FONT_METRICS *metrics); + + UINT16 (STDMETHODCALLTYPE *GetGlyphCount)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphMetrics)( + IDWriteFontFace5 *This, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)( + IDWriteFontFace5 *This, + const UINT32 *codepoints, + UINT32 count, + UINT16 *glyph_indices); + + HRESULT (STDMETHODCALLTYPE *TryGetFontTable)( + IDWriteFontFace5 *This, + UINT32 table_tag, + const void **table_data, + UINT32 *table_size, + void **context, + WINBOOL *exists); + + void (STDMETHODCALLTYPE *ReleaseFontTable)( + IDWriteFontFace5 *This, + void *table_context); + + HRESULT (STDMETHODCALLTYPE *GetGlyphRunOutline)( + IDWriteFontFace5 *This, + FLOAT emSize, + const UINT16 *glyph_indices, + const FLOAT *glyph_advances, + const DWRITE_GLYPH_OFFSET *glyph_offsets, + UINT32 glyph_count, + WINBOOL is_sideways, + WINBOOL is_rtl, + IDWriteGeometrySink *geometrysink); + + HRESULT (STDMETHODCALLTYPE *GetRecommendedRenderingMode)( + IDWriteFontFace5 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + DWRITE_MEASURING_MODE mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *rendering_mode); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleMetrics)( + IDWriteFontFace5 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)( + IDWriteFontFace5 *This, + FLOAT emSize, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + const UINT16 *glyph_indices, + UINT32 glyph_count, + DWRITE_GLYPH_METRICS *metrics, + WINBOOL is_sideways); + + /*** IDWriteFontFace1 methods ***/ + void (STDMETHODCALLTYPE *IDWriteFontFace1_GetMetrics)( + IDWriteFontFace5 *This, + DWRITE_FONT_METRICS1 *metrics); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetGdiCompatibleMetrics)( + IDWriteFontFace5 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_FONT_METRICS1 *metrics); + + void (STDMETHODCALLTYPE *GetCaretMetrics)( + IDWriteFontFace5 *This, + DWRITE_CARET_METRICS *metrics); + + HRESULT (STDMETHODCALLTYPE *GetUnicodeRanges)( + IDWriteFontFace5 *This, + UINT32 max_count, + DWRITE_UNICODE_RANGE *ranges, + UINT32 *count); + + WINBOOL (STDMETHODCALLTYPE *IsMonospacedFont)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetDesignGlyphAdvances)( + IDWriteFontFace5 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances, + WINBOOL is_sideways); + + HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphAdvances)( + IDWriteFontFace5 *This, + FLOAT em_size, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + WINBOOL is_sideways, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *advances); + + HRESULT (STDMETHODCALLTYPE *GetKerningPairAdjustments)( + IDWriteFontFace5 *This, + UINT32 glyph_count, + const UINT16 *indices, + INT32 *adjustments); + + WINBOOL (STDMETHODCALLTYPE *HasKerningPairs)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace1_GetRecommendedRenderingMode)( + IDWriteFontFace5 *This, + FLOAT font_emsize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_RENDERING_MODE *rendering_mode); + + HRESULT (STDMETHODCALLTYPE *GetVerticalGlyphVariants)( + IDWriteFontFace5 *This, + UINT32 glyph_count, + const UINT16 *nominal_indices, + UINT16 *vertical_indices); + + WINBOOL (STDMETHODCALLTYPE *HasVerticalGlyphVariants)( + IDWriteFontFace5 *This); + + /*** IDWriteFontFace2 methods ***/ + WINBOOL (STDMETHODCALLTYPE *IsColorFont)( + IDWriteFontFace5 *This); + + UINT32 (STDMETHODCALLTYPE *GetColorPaletteCount)( + IDWriteFontFace5 *This); + + UINT32 (STDMETHODCALLTYPE *GetPaletteEntryCount)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetPaletteEntries)( + IDWriteFontFace5 *This, + UINT32 palette_index, + UINT32 first_entry_index, + UINT32 entry_count, + DWRITE_COLOR_F *entries); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace2_GetRecommendedRenderingMode)( + IDWriteFontFace5 *This, + FLOAT fontEmSize, + FLOAT dpiX, + FLOAT dpiY, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuringmode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE *renderingmode, + DWRITE_GRID_FIT_MODE *gridfitmode); + + /*** IDWriteFontFace3 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetFontFaceReference)( + IDWriteFontFace5 *This, + IDWriteFontFaceReference **reference); + + void (STDMETHODCALLTYPE *GetPanose)( + IDWriteFontFace5 *This, + DWRITE_PANOSE *panose); + + DWRITE_FONT_WEIGHT (STDMETHODCALLTYPE *GetWeight)( + IDWriteFontFace5 *This); + + DWRITE_FONT_STRETCH (STDMETHODCALLTYPE *GetStretch)( + IDWriteFontFace5 *This); + + DWRITE_FONT_STYLE (STDMETHODCALLTYPE *GetStyle)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetFamilyNames)( + IDWriteFontFace5 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetFaceNames)( + IDWriteFontFace5 *This, + IDWriteLocalizedStrings **names); + + HRESULT (STDMETHODCALLTYPE *GetInformationalStrings)( + IDWriteFontFace5 *This, + DWRITE_INFORMATIONAL_STRING_ID stringid, + IDWriteLocalizedStrings **strings, + WINBOOL *exists); + + WINBOOL (STDMETHODCALLTYPE *HasCharacter)( + IDWriteFontFace5 *This, + UINT32 character); + + HRESULT (STDMETHODCALLTYPE *IDWriteFontFace3_GetRecommendedRenderingMode)( + IDWriteFontFace5 *This, + FLOAT emsize, + FLOAT dpi_x, + FLOAT dpi_y, + const DWRITE_MATRIX *transform, + WINBOOL is_sideways, + DWRITE_OUTLINE_THRESHOLD threshold, + DWRITE_MEASURING_MODE measuring_mode, + IDWriteRenderingParams *params, + DWRITE_RENDERING_MODE1 *rendering_mode, + DWRITE_GRID_FIT_MODE *gridfit_mode); + + WINBOOL (STDMETHODCALLTYPE *IsCharacterLocal)( + IDWriteFontFace5 *This, + UINT32 character); + + WINBOOL (STDMETHODCALLTYPE *IsGlyphLocal)( + IDWriteFontFace5 *This, + UINT16 glyph); + + HRESULT (STDMETHODCALLTYPE *AreCharactersLocal)( + IDWriteFontFace5 *This, + const WCHAR *characters, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + HRESULT (STDMETHODCALLTYPE *AreGlyphsLocal)( + IDWriteFontFace5 *This, + const UINT16 *glyphs, + UINT32 count, + WINBOOL enqueue_if_not, + WINBOOL *are_local); + + /*** IDWriteFontFace4 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetGlyphImageFormats_)( + IDWriteFontFace5 *This, + UINT16 glyph, + UINT32 ppem_first, + UINT32 ppem_last, + DWRITE_GLYPH_IMAGE_FORMATS *formats); + + DWRITE_GLYPH_IMAGE_FORMATS (STDMETHODCALLTYPE *GetGlyphImageFormats)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetGlyphImageData)( + IDWriteFontFace5 *This, + UINT16 glyph, + UINT32 ppem, + DWRITE_GLYPH_IMAGE_FORMATS format, + DWRITE_GLYPH_IMAGE_DATA *data, + void **context); + + void (STDMETHODCALLTYPE *ReleaseGlyphImageData)( + IDWriteFontFace5 *This, + void *context); + + /*** IDWriteFontFace5 methods ***/ + UINT32 (STDMETHODCALLTYPE *GetFontAxisValueCount)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontAxisValues)( + IDWriteFontFace5 *This, + DWRITE_FONT_AXIS_VALUE *values, + UINT32 value_count); + + WINBOOL (STDMETHODCALLTYPE *HasVariations)( + IDWriteFontFace5 *This); + + HRESULT (STDMETHODCALLTYPE *GetFontResource)( + IDWriteFontFace5 *This, + IDWriteFontResource **resource); + + WINBOOL (STDMETHODCALLTYPE *Equals)( + IDWriteFontFace5 *This, + IDWriteFontFace *fontface); + + END_INTERFACE +} IDWriteFontFace5Vtbl; + +interface IDWriteFontFace5 { + CONST_VTBL IDWriteFontFace5Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFontFace5_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFontFace5_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFontFace5_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFace methods ***/ +#define IDWriteFontFace5_GetType(This) (This)->lpVtbl->GetType(This) +#define IDWriteFontFace5_GetFiles(This,number_of_files,fontfiles) (This)->lpVtbl->GetFiles(This,number_of_files,fontfiles) +#define IDWriteFontFace5_GetIndex(This) (This)->lpVtbl->GetIndex(This) +#define IDWriteFontFace5_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) +#define IDWriteFontFace5_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This) +#define IDWriteFontFace5_GetGlyphCount(This) (This)->lpVtbl->GetGlyphCount(This) +#define IDWriteFontFace5_GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways) +#define IDWriteFontFace5_GetGlyphIndices(This,codepoints,count,glyph_indices) (This)->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices) +#define IDWriteFontFace5_TryGetFontTable(This,table_tag,table_data,table_size,context,exists) (This)->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists) +#define IDWriteFontFace5_ReleaseFontTable(This,table_context) (This)->lpVtbl->ReleaseFontTable(This,table_context) +#define IDWriteFontFace5_GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) (This)->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink) +#define IDWriteFontFace5_GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) (This)->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways) +/*** IDWriteFontFace1 methods ***/ +#define IDWriteFontFace5_GetMetrics(This,metrics) (This)->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics) +#define IDWriteFontFace5_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) (This)->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics) +#define IDWriteFontFace5_GetCaretMetrics(This,metrics) (This)->lpVtbl->GetCaretMetrics(This,metrics) +#define IDWriteFontFace5_GetUnicodeRanges(This,max_count,ranges,count) (This)->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count) +#define IDWriteFontFace5_IsMonospacedFont(This) (This)->lpVtbl->IsMonospacedFont(This) +#define IDWriteFontFace5_GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) (This)->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways) +#define IDWriteFontFace5_GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) (This)->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances) +#define IDWriteFontFace5_GetKerningPairAdjustments(This,glyph_count,indices,adjustments) (This)->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments) +#define IDWriteFontFace5_HasKerningPairs(This) (This)->lpVtbl->HasKerningPairs(This) +#define IDWriteFontFace5_GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) (This)->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices) +#define IDWriteFontFace5_HasVerticalGlyphVariants(This) (This)->lpVtbl->HasVerticalGlyphVariants(This) +/*** IDWriteFontFace2 methods ***/ +#define IDWriteFontFace5_IsColorFont(This) (This)->lpVtbl->IsColorFont(This) +#define IDWriteFontFace5_GetColorPaletteCount(This) (This)->lpVtbl->GetColorPaletteCount(This) +#define IDWriteFontFace5_GetPaletteEntryCount(This) (This)->lpVtbl->GetPaletteEntryCount(This) +#define IDWriteFontFace5_GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) (This)->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries) +/*** IDWriteFontFace3 methods ***/ +#define IDWriteFontFace5_GetFontFaceReference(This,reference) (This)->lpVtbl->GetFontFaceReference(This,reference) +#define IDWriteFontFace5_GetPanose(This,panose) (This)->lpVtbl->GetPanose(This,panose) +#define IDWriteFontFace5_GetWeight(This) (This)->lpVtbl->GetWeight(This) +#define IDWriteFontFace5_GetStretch(This) (This)->lpVtbl->GetStretch(This) +#define IDWriteFontFace5_GetStyle(This) (This)->lpVtbl->GetStyle(This) +#define IDWriteFontFace5_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) +#define IDWriteFontFace5_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names) +#define IDWriteFontFace5_GetInformationalStrings(This,stringid,strings,exists) (This)->lpVtbl->GetInformationalStrings(This,stringid,strings,exists) +#define IDWriteFontFace5_HasCharacter(This,character) (This)->lpVtbl->HasCharacter(This,character) +#define IDWriteFontFace5_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) (This)->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode) +#define IDWriteFontFace5_IsCharacterLocal(This,character) (This)->lpVtbl->IsCharacterLocal(This,character) +#define IDWriteFontFace5_IsGlyphLocal(This,glyph) (This)->lpVtbl->IsGlyphLocal(This,glyph) +#define IDWriteFontFace5_AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) (This)->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local) +#define IDWriteFontFace5_AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) (This)->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local) +/*** IDWriteFontFace4 methods ***/ +#define IDWriteFontFace5_GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats) (This)->lpVtbl->GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats) +#define IDWriteFontFace5_GetGlyphImageFormats(This) (This)->lpVtbl->GetGlyphImageFormats(This) +#define IDWriteFontFace5_GetGlyphImageData(This,glyph,ppem,format,data,context) (This)->lpVtbl->GetGlyphImageData(This,glyph,ppem,format,data,context) +#define IDWriteFontFace5_ReleaseGlyphImageData(This,context) (This)->lpVtbl->ReleaseGlyphImageData(This,context) +/*** IDWriteFontFace5 methods ***/ +#define IDWriteFontFace5_GetFontAxisValueCount(This) (This)->lpVtbl->GetFontAxisValueCount(This) +#define IDWriteFontFace5_GetFontAxisValues(This,values,value_count) (This)->lpVtbl->GetFontAxisValues(This,values,value_count) +#define IDWriteFontFace5_HasVariations(This) (This)->lpVtbl->HasVariations(This) +#define IDWriteFontFace5_GetFontResource(This,resource) (This)->lpVtbl->GetFontResource(This,resource) +#define IDWriteFontFace5_Equals(This,fontface) (This)->lpVtbl->Equals(This,fontface) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace5_QueryInterface(IDWriteFontFace5* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFontFace5_AddRef(IDWriteFontFace5* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFontFace5_Release(IDWriteFontFace5* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFace methods ***/ +static FORCEINLINE DWRITE_FONT_FACE_TYPE IDWriteFontFace5_GetType(IDWriteFontFace5* This) { + return This->lpVtbl->GetType(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetFiles(IDWriteFontFace5* This,UINT32 *number_of_files,IDWriteFontFile **fontfiles) { + return This->lpVtbl->GetFiles(This,number_of_files,fontfiles); +} +static FORCEINLINE UINT32 IDWriteFontFace5_GetIndex(IDWriteFontFace5* This) { + return This->lpVtbl->GetIndex(This); +} +static FORCEINLINE DWRITE_FONT_SIMULATIONS IDWriteFontFace5_GetSimulations(IDWriteFontFace5* This) { + return This->lpVtbl->GetSimulations(This); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_IsSymbolFont(IDWriteFontFace5* This) { + return This->lpVtbl->IsSymbolFont(This); +} +static FORCEINLINE UINT16 IDWriteFontFace5_GetGlyphCount(IDWriteFontFace5* This) { + return This->lpVtbl->GetGlyphCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetDesignGlyphMetrics(IDWriteFontFace5* This,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphMetrics(This,glyph_indices,glyph_count,metrics,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGlyphIndices(IDWriteFontFace5* This,const UINT32 *codepoints,UINT32 count,UINT16 *glyph_indices) { + return This->lpVtbl->GetGlyphIndices(This,codepoints,count,glyph_indices); +} +static FORCEINLINE HRESULT IDWriteFontFace5_TryGetFontTable(IDWriteFontFace5* This,UINT32 table_tag,const void **table_data,UINT32 *table_size,void **context,WINBOOL *exists) { + return This->lpVtbl->TryGetFontTable(This,table_tag,table_data,table_size,context,exists); +} +static FORCEINLINE void IDWriteFontFace5_ReleaseFontTable(IDWriteFontFace5* This,void *table_context) { + This->lpVtbl->ReleaseFontTable(This,table_context); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGlyphRunOutline(IDWriteFontFace5* This,FLOAT emSize,const UINT16 *glyph_indices,const FLOAT *glyph_advances,const DWRITE_GLYPH_OFFSET *glyph_offsets,UINT32 glyph_count,WINBOOL is_sideways,WINBOOL is_rtl,IDWriteGeometrySink *geometrysink) { + return This->lpVtbl->GetGlyphRunOutline(This,emSize,glyph_indices,glyph_advances,glyph_offsets,glyph_count,is_sideways,is_rtl,geometrysink); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGdiCompatibleGlyphMetrics(IDWriteFontFace5* This,FLOAT emSize,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,const UINT16 *glyph_indices,UINT32 glyph_count,DWRITE_GLYPH_METRICS *metrics,WINBOOL is_sideways) { + return This->lpVtbl->GetGdiCompatibleGlyphMetrics(This,emSize,pixels_per_dip,transform,use_gdi_natural,glyph_indices,glyph_count,metrics,is_sideways); +} +/*** IDWriteFontFace1 methods ***/ +static FORCEINLINE void IDWriteFontFace5_GetMetrics(IDWriteFontFace5* This,DWRITE_FONT_METRICS1 *metrics) { + This->lpVtbl->IDWriteFontFace1_GetMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGdiCompatibleMetrics(IDWriteFontFace5* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,DWRITE_FONT_METRICS1 *metrics) { + return This->lpVtbl->IDWriteFontFace1_GetGdiCompatibleMetrics(This,em_size,pixels_per_dip,transform,metrics); +} +static FORCEINLINE void IDWriteFontFace5_GetCaretMetrics(IDWriteFontFace5* This,DWRITE_CARET_METRICS *metrics) { + This->lpVtbl->GetCaretMetrics(This,metrics); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetUnicodeRanges(IDWriteFontFace5* This,UINT32 max_count,DWRITE_UNICODE_RANGE *ranges,UINT32 *count) { + return This->lpVtbl->GetUnicodeRanges(This,max_count,ranges,count); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_IsMonospacedFont(IDWriteFontFace5* This) { + return This->lpVtbl->IsMonospacedFont(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetDesignGlyphAdvances(IDWriteFontFace5* This,UINT32 glyph_count,const UINT16 *indices,INT32 *advances,WINBOOL is_sideways) { + return This->lpVtbl->GetDesignGlyphAdvances(This,glyph_count,indices,advances,is_sideways); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGdiCompatibleGlyphAdvances(IDWriteFontFace5* This,FLOAT em_size,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,WINBOOL is_sideways,UINT32 glyph_count,const UINT16 *indices,INT32 *advances) { + return This->lpVtbl->GetGdiCompatibleGlyphAdvances(This,em_size,pixels_per_dip,transform,use_gdi_natural,is_sideways,glyph_count,indices,advances); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetKerningPairAdjustments(IDWriteFontFace5* This,UINT32 glyph_count,const UINT16 *indices,INT32 *adjustments) { + return This->lpVtbl->GetKerningPairAdjustments(This,glyph_count,indices,adjustments); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_HasKerningPairs(IDWriteFontFace5* This) { + return This->lpVtbl->HasKerningPairs(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetVerticalGlyphVariants(IDWriteFontFace5* This,UINT32 glyph_count,const UINT16 *nominal_indices,UINT16 *vertical_indices) { + return This->lpVtbl->GetVerticalGlyphVariants(This,glyph_count,nominal_indices,vertical_indices); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_HasVerticalGlyphVariants(IDWriteFontFace5* This) { + return This->lpVtbl->HasVerticalGlyphVariants(This); +} +/*** IDWriteFontFace2 methods ***/ +static FORCEINLINE WINBOOL IDWriteFontFace5_IsColorFont(IDWriteFontFace5* This) { + return This->lpVtbl->IsColorFont(This); +} +static FORCEINLINE UINT32 IDWriteFontFace5_GetColorPaletteCount(IDWriteFontFace5* This) { + return This->lpVtbl->GetColorPaletteCount(This); +} +static FORCEINLINE UINT32 IDWriteFontFace5_GetPaletteEntryCount(IDWriteFontFace5* This) { + return This->lpVtbl->GetPaletteEntryCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetPaletteEntries(IDWriteFontFace5* This,UINT32 palette_index,UINT32 first_entry_index,UINT32 entry_count,DWRITE_COLOR_F *entries) { + return This->lpVtbl->GetPaletteEntries(This,palette_index,first_entry_index,entry_count,entries); +} +/*** IDWriteFontFace3 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace5_GetFontFaceReference(IDWriteFontFace5* This,IDWriteFontFaceReference **reference) { + return This->lpVtbl->GetFontFaceReference(This,reference); +} +static FORCEINLINE void IDWriteFontFace5_GetPanose(IDWriteFontFace5* This,DWRITE_PANOSE *panose) { + This->lpVtbl->GetPanose(This,panose); +} +static FORCEINLINE DWRITE_FONT_WEIGHT IDWriteFontFace5_GetWeight(IDWriteFontFace5* This) { + return This->lpVtbl->GetWeight(This); +} +static FORCEINLINE DWRITE_FONT_STRETCH IDWriteFontFace5_GetStretch(IDWriteFontFace5* This) { + return This->lpVtbl->GetStretch(This); +} +static FORCEINLINE DWRITE_FONT_STYLE IDWriteFontFace5_GetStyle(IDWriteFontFace5* This) { + return This->lpVtbl->GetStyle(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetFamilyNames(IDWriteFontFace5* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFamilyNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetFaceNames(IDWriteFontFace5* This,IDWriteLocalizedStrings **names) { + return This->lpVtbl->GetFaceNames(This,names); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetInformationalStrings(IDWriteFontFace5* This,DWRITE_INFORMATIONAL_STRING_ID stringid,IDWriteLocalizedStrings **strings,WINBOOL *exists) { + return This->lpVtbl->GetInformationalStrings(This,stringid,strings,exists); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_HasCharacter(IDWriteFontFace5* This,UINT32 character) { + return This->lpVtbl->HasCharacter(This,character); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetRecommendedRenderingMode(IDWriteFontFace5* This,FLOAT emsize,FLOAT dpi_x,FLOAT dpi_y,const DWRITE_MATRIX *transform,WINBOOL is_sideways,DWRITE_OUTLINE_THRESHOLD threshold,DWRITE_MEASURING_MODE measuring_mode,IDWriteRenderingParams *params,DWRITE_RENDERING_MODE1 *rendering_mode,DWRITE_GRID_FIT_MODE *gridfit_mode) { + return This->lpVtbl->IDWriteFontFace3_GetRecommendedRenderingMode(This,emsize,dpi_x,dpi_y,transform,is_sideways,threshold,measuring_mode,params,rendering_mode,gridfit_mode); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_IsCharacterLocal(IDWriteFontFace5* This,UINT32 character) { + return This->lpVtbl->IsCharacterLocal(This,character); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_IsGlyphLocal(IDWriteFontFace5* This,UINT16 glyph) { + return This->lpVtbl->IsGlyphLocal(This,glyph); +} +static FORCEINLINE HRESULT IDWriteFontFace5_AreCharactersLocal(IDWriteFontFace5* This,const WCHAR *characters,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreCharactersLocal(This,characters,count,enqueue_if_not,are_local); +} +static FORCEINLINE HRESULT IDWriteFontFace5_AreGlyphsLocal(IDWriteFontFace5* This,const UINT16 *glyphs,UINT32 count,WINBOOL enqueue_if_not,WINBOOL *are_local) { + return This->lpVtbl->AreGlyphsLocal(This,glyphs,count,enqueue_if_not,are_local); +} +/*** IDWriteFontFace4 methods ***/ +static FORCEINLINE HRESULT IDWriteFontFace5_GetGlyphImageFormats_(IDWriteFontFace5* This,UINT16 glyph,UINT32 ppem_first,UINT32 ppem_last,DWRITE_GLYPH_IMAGE_FORMATS *formats) { + return This->lpVtbl->GetGlyphImageFormats_(This,glyph,ppem_first,ppem_last,formats); +} +static FORCEINLINE DWRITE_GLYPH_IMAGE_FORMATS IDWriteFontFace5_GetGlyphImageFormats(IDWriteFontFace5* This) { + return This->lpVtbl->GetGlyphImageFormats(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetGlyphImageData(IDWriteFontFace5* This,UINT16 glyph,UINT32 ppem,DWRITE_GLYPH_IMAGE_FORMATS format,DWRITE_GLYPH_IMAGE_DATA *data,void **context) { + return This->lpVtbl->GetGlyphImageData(This,glyph,ppem,format,data,context); +} +static FORCEINLINE void IDWriteFontFace5_ReleaseGlyphImageData(IDWriteFontFace5* This,void *context) { + This->lpVtbl->ReleaseGlyphImageData(This,context); +} +/*** IDWriteFontFace5 methods ***/ +static FORCEINLINE UINT32 IDWriteFontFace5_GetFontAxisValueCount(IDWriteFontFace5* This) { + return This->lpVtbl->GetFontAxisValueCount(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetFontAxisValues(IDWriteFontFace5* This,DWRITE_FONT_AXIS_VALUE *values,UINT32 value_count) { + return This->lpVtbl->GetFontAxisValues(This,values,value_count); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_HasVariations(IDWriteFontFace5* This) { + return This->lpVtbl->HasVariations(This); +} +static FORCEINLINE HRESULT IDWriteFontFace5_GetFontResource(IDWriteFontFace5* This,IDWriteFontResource **resource) { + return This->lpVtbl->GetFontResource(This,resource); +} +static FORCEINLINE WINBOOL IDWriteFontFace5_Equals(IDWriteFontFace5* This,IDWriteFontFace *fontface) { + return This->lpVtbl->Equals(This,fontface); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFontFace5_INTERFACE_DEFINED__ */ + +typedef struct DWRITE_COLOR_GLYPH_RUN1 { + DWRITE_GLYPH_RUN glyphRun; + DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription; + FLOAT baselineOriginX; + FLOAT baselineOriginY; + DWRITE_COLOR_F runColor; + UINT16 paletteIndex; + DWRITE_GLYPH_IMAGE_FORMATS glyphImageFormat; + DWRITE_MEASURING_MODE measuringMode; +} DWRITE_COLOR_GLYPH_RUN1; +/***************************************************************************** + * IDWriteColorGlyphRunEnumerator1 interface + */ +#ifndef __IDWriteColorGlyphRunEnumerator1_INTERFACE_DEFINED__ +#define __IDWriteColorGlyphRunEnumerator1_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("7c5f86da-c7a1-4f05-b8e1-55a179fe5a35") +IDWriteColorGlyphRunEnumerator1 : public IDWriteColorGlyphRunEnumerator +{ + virtual HRESULT STDMETHODCALLTYPE GetCurrentRun( + const DWRITE_COLOR_GLYPH_RUN1 **run) = 0; + + using IDWriteColorGlyphRunEnumerator::GetCurrentRun; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35) +#endif +#else +typedef struct IDWriteColorGlyphRunEnumerator1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteColorGlyphRunEnumerator1 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteColorGlyphRunEnumerator1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteColorGlyphRunEnumerator1 *This); + + /*** IDWriteColorGlyphRunEnumerator methods ***/ + HRESULT (STDMETHODCALLTYPE *MoveNext)( + IDWriteColorGlyphRunEnumerator1 *This, + WINBOOL *hasRun); + + HRESULT (STDMETHODCALLTYPE *GetCurrentRun)( + IDWriteColorGlyphRunEnumerator1 *This, + const DWRITE_COLOR_GLYPH_RUN **run); + + /*** IDWriteColorGlyphRunEnumerator1 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteColorGlyphRunEnumerator1_GetCurrentRun)( + IDWriteColorGlyphRunEnumerator1 *This, + const DWRITE_COLOR_GLYPH_RUN1 **run); + + END_INTERFACE +} IDWriteColorGlyphRunEnumerator1Vtbl; + +interface IDWriteColorGlyphRunEnumerator1 { + CONST_VTBL IDWriteColorGlyphRunEnumerator1Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteColorGlyphRunEnumerator1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteColorGlyphRunEnumerator1_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteColorGlyphRunEnumerator1_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteColorGlyphRunEnumerator methods ***/ +#define IDWriteColorGlyphRunEnumerator1_MoveNext(This,hasRun) (This)->lpVtbl->MoveNext(This,hasRun) +/*** IDWriteColorGlyphRunEnumerator1 methods ***/ +#define IDWriteColorGlyphRunEnumerator1_GetCurrentRun(This,run) (This)->lpVtbl->IDWriteColorGlyphRunEnumerator1_GetCurrentRun(This,run) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteColorGlyphRunEnumerator1_QueryInterface(IDWriteColorGlyphRunEnumerator1* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteColorGlyphRunEnumerator1_AddRef(IDWriteColorGlyphRunEnumerator1* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteColorGlyphRunEnumerator1_Release(IDWriteColorGlyphRunEnumerator1* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteColorGlyphRunEnumerator methods ***/ +static FORCEINLINE HRESULT IDWriteColorGlyphRunEnumerator1_MoveNext(IDWriteColorGlyphRunEnumerator1* This,WINBOOL *hasRun) { + return This->lpVtbl->MoveNext(This,hasRun); +} +/*** IDWriteColorGlyphRunEnumerator1 methods ***/ +static FORCEINLINE HRESULT IDWriteColorGlyphRunEnumerator1_GetCurrentRun(IDWriteColorGlyphRunEnumerator1* This,const DWRITE_COLOR_GLYPH_RUN1 **run) { + return This->lpVtbl->IDWriteColorGlyphRunEnumerator1_GetCurrentRun(This,run); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteColorGlyphRunEnumerator1_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFactory4 interface + */ +#ifndef __IDWriteFactory4_INTERFACE_DEFINED__ +#define __IDWriteFactory4_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("4b0b5bd3-0797-4549-8ac5-fe915cc53856") +IDWriteFactory4 : public IDWriteFactory3 +{ + virtual HRESULT STDMETHODCALLTYPE TranslateColorGlyphRun( + D2D1_POINT_2F baseline_origin, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc, + DWRITE_GLYPH_IMAGE_FORMATS desired_formats, + DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_MATRIX *transform, + UINT32 palette, + IDWriteColorGlyphRunEnumerator1 **layers) = 0; + + using IDWriteFactory2::TranslateColorGlyphRun; + + virtual HRESULT STDMETHODCALLTYPE ComputeGlyphOrigins( + const DWRITE_GLYPH_RUN *run, + D2D1_POINT_2F baseline_origin, + D2D1_POINT_2F *origins) = 0; + + virtual HRESULT STDMETHODCALLTYPE ComputeGlyphOrigins( + const DWRITE_GLYPH_RUN *run, + DWRITE_MEASURING_MODE measuring_mode, + D2D1_POINT_2F baseline_origin, + const DWRITE_MATRIX *transform, + D2D1_POINT_2F *origins) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56) +#endif +#else +typedef struct IDWriteFactory4Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFactory4 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFactory4 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFactory4 *This); + + /*** IDWriteFactory methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontCollection)( + IDWriteFactory4 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontCollection)( + IDWriteFactory4 *This, + IDWriteFontCollectionLoader *loader, + const void *key, + UINT32 key_size, + IDWriteFontCollection **collection); + + HRESULT (STDMETHODCALLTYPE *RegisterFontCollectionLoader)( + IDWriteFactory4 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontCollectionLoader)( + IDWriteFactory4 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReference)( + IDWriteFactory4 *This, + const WCHAR *path, + const FILETIME *writetime, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontFileReference)( + IDWriteFactory4 *This, + const void *reference_key, + UINT32 key_size, + IDWriteFontFileLoader *loader, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFactory4 *This, + DWRITE_FONT_FACE_TYPE facetype, + UINT32 files_number, + IDWriteFontFile *const *font_files, + UINT32 index, + DWRITE_FONT_SIMULATIONS sim_flags, + IDWriteFontFace **font_face); + + HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)( + IDWriteFactory4 *This, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateMonitorRenderingParams)( + IDWriteFactory4 *This, + HMONITOR monitor, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)( + IDWriteFactory4 *This, + FLOAT gamma, + FLOAT enhancedContrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *RegisterFontFileLoader)( + IDWriteFactory4 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontFileLoader)( + IDWriteFactory4 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateTextFormat)( + IDWriteFactory4 *This, + const WCHAR *family_name, + IDWriteFontCollection *collection, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, + FLOAT size, + const WCHAR *locale, + IDWriteTextFormat **format); + + HRESULT (STDMETHODCALLTYPE *CreateTypography)( + IDWriteFactory4 *This, + IDWriteTypography **typography); + + HRESULT (STDMETHODCALLTYPE *GetGdiInterop)( + IDWriteFactory4 *This, + IDWriteGdiInterop **gdi_interop); + + HRESULT (STDMETHODCALLTYPE *CreateTextLayout)( + IDWriteFactory4 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT max_width, + FLOAT max_height, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateGdiCompatibleTextLayout)( + IDWriteFactory4 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT layout_width, + FLOAT layout_height, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateEllipsisTrimmingSign)( + IDWriteFactory4 *This, + IDWriteTextFormat *format, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *CreateTextAnalyzer)( + IDWriteFactory4 *This, + IDWriteTextAnalyzer **analyzer); + + HRESULT (STDMETHODCALLTYPE *CreateNumberSubstitution)( + IDWriteFactory4 *This, + DWRITE_NUMBER_SUBSTITUTION_METHOD method, + const WCHAR *locale, + WINBOOL ignore_user_override, + IDWriteNumberSubstitution **substitution); + + HRESULT (STDMETHODCALLTYPE *CreateGlyphRunAnalysis)( + IDWriteFactory4 *This, + const DWRITE_GLYPH_RUN *glyph_run, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + FLOAT baseline_x, + FLOAT baseline_y, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetEudcFontCollection)( + IDWriteFactory4 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory1_CreateCustomRenderingParams)( + IDWriteFactory4 *This, + FLOAT gamma, + FLOAT enhcontrast, + FLOAT enhcontrast_grayscale, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams1 **params); + + /*** IDWriteFactory2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontFallback)( + IDWriteFactory4 *This, + IDWriteFontFallback **fallback); + + HRESULT (STDMETHODCALLTYPE *CreateFontFallbackBuilder)( + IDWriteFactory4 *This, + IDWriteFontFallbackBuilder **fallbackbuilder); + + HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)( + IDWriteFactory4 *This, + FLOAT originX, + FLOAT originY, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, + DWRITE_MEASURING_MODE mode, + const DWRITE_MATRIX *transform, + UINT32 palette_index, + IDWriteColorGlyphRunEnumerator **colorlayers); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateCustomRenderingParams)( + IDWriteFactory4 *This, + FLOAT gamma, + FLOAT contrast, + FLOAT grayscalecontrast, + FLOAT cleartypeLevel, + DWRITE_PIXEL_GEOMETRY pixelGeometry, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_GRID_FIT_MODE gridFitMode, + IDWriteRenderingParams2 **params); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateGlyphRunAnalysis)( + IDWriteFactory4 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GRID_FIT_MODE gridFitMode, + DWRITE_TEXT_ANTIALIAS_MODE antialiasMode, + FLOAT originX, + FLOAT originY, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateGlyphRunAnalysis)( + IDWriteFactory4 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateCustomRenderingParams)( + IDWriteFactory4 *This, + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference_)( + IDWriteFactory4 *This, + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFactory4 *This, + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *GetSystemFontSet)( + IDWriteFactory4 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSetBuilder)( + IDWriteFactory4 *This, + IDWriteFontSetBuilder **builder); + + HRESULT (STDMETHODCALLTYPE *CreateFontCollectionFromFontSet)( + IDWriteFactory4 *This, + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_GetSystemFontCollection)( + IDWriteFactory4 *This, + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *GetFontDownloadQueue)( + IDWriteFactory4 *This, + IDWriteFontDownloadQueue **queue); + + /*** IDWriteFactory4 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory4_TranslateColorGlyphRun)( + IDWriteFactory4 *This, + D2D1_POINT_2F baseline_origin, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc, + DWRITE_GLYPH_IMAGE_FORMATS desired_formats, + DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_MATRIX *transform, + UINT32 palette, + IDWriteColorGlyphRunEnumerator1 **layers); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins_)( + IDWriteFactory4 *This, + const DWRITE_GLYPH_RUN *run, + D2D1_POINT_2F baseline_origin, + D2D1_POINT_2F *origins); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins)( + IDWriteFactory4 *This, + const DWRITE_GLYPH_RUN *run, + DWRITE_MEASURING_MODE measuring_mode, + D2D1_POINT_2F baseline_origin, + const DWRITE_MATRIX *transform, + D2D1_POINT_2F *origins); + + END_INTERFACE +} IDWriteFactory4Vtbl; + +interface IDWriteFactory4 { + CONST_VTBL IDWriteFactory4Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFactory4_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFactory4_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFactory4_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFactory methods ***/ +#define IDWriteFactory4_CreateCustomFontCollection(This,loader,key,key_size,collection) (This)->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection) +#define IDWriteFactory4_RegisterFontCollectionLoader(This,loader) (This)->lpVtbl->RegisterFontCollectionLoader(This,loader) +#define IDWriteFactory4_UnregisterFontCollectionLoader(This,loader) (This)->lpVtbl->UnregisterFontCollectionLoader(This,loader) +#define IDWriteFactory4_CreateFontFileReference(This,path,writetime,font_file) (This)->lpVtbl->CreateFontFileReference(This,path,writetime,font_file) +#define IDWriteFactory4_CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) (This)->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) +#define IDWriteFactory4_CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) (This)->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) +#define IDWriteFactory4_CreateRenderingParams(This,params) (This)->lpVtbl->CreateRenderingParams(This,params) +#define IDWriteFactory4_CreateMonitorRenderingParams(This,monitor,params) (This)->lpVtbl->CreateMonitorRenderingParams(This,monitor,params) +#define IDWriteFactory4_RegisterFontFileLoader(This,loader) (This)->lpVtbl->RegisterFontFileLoader(This,loader) +#define IDWriteFactory4_UnregisterFontFileLoader(This,loader) (This)->lpVtbl->UnregisterFontFileLoader(This,loader) +#define IDWriteFactory4_CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) (This)->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) +#define IDWriteFactory4_CreateTypography(This,typography) (This)->lpVtbl->CreateTypography(This,typography) +#define IDWriteFactory4_GetGdiInterop(This,gdi_interop) (This)->lpVtbl->GetGdiInterop(This,gdi_interop) +#define IDWriteFactory4_CreateTextLayout(This,string,len,format,max_width,max_height,layout) (This)->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout) +#define IDWriteFactory4_CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) (This)->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) +#define IDWriteFactory4_CreateEllipsisTrimmingSign(This,format,trimming_sign) (This)->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign) +#define IDWriteFactory4_CreateTextAnalyzer(This,analyzer) (This)->lpVtbl->CreateTextAnalyzer(This,analyzer) +#define IDWriteFactory4_CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) (This)->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) +/*** IDWriteFactory1 methods ***/ +#define IDWriteFactory4_GetEudcFontCollection(This,collection,check_for_updates) (This)->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates) +/*** IDWriteFactory2 methods ***/ +#define IDWriteFactory4_GetSystemFontFallback(This,fallback) (This)->lpVtbl->GetSystemFontFallback(This,fallback) +#define IDWriteFactory4_CreateFontFallbackBuilder(This,fallbackbuilder) (This)->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder) +/*** IDWriteFactory3 methods ***/ +#define IDWriteFactory4_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) (This)->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) +#define IDWriteFactory4_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) (This)->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) +#define IDWriteFactory4_CreateFontFaceReference_(This,file,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference) +#define IDWriteFactory4_CreateFontFaceReference(This,path,writetime,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference) +#define IDWriteFactory4_GetSystemFontSet(This,fontset) (This)->lpVtbl->GetSystemFontSet(This,fontset) +#define IDWriteFactory4_CreateFontSetBuilder(This,builder) (This)->lpVtbl->CreateFontSetBuilder(This,builder) +#define IDWriteFactory4_CreateFontCollectionFromFontSet(This,fontset,collection) (This)->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection) +#define IDWriteFactory4_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) (This)->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) +#define IDWriteFactory4_GetFontDownloadQueue(This,queue) (This)->lpVtbl->GetFontDownloadQueue(This,queue) +/*** IDWriteFactory4 methods ***/ +#define IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) (This)->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) +#define IDWriteFactory4_ComputeGlyphOrigins_(This,run,baseline_origin,origins) (This)->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins) +#define IDWriteFactory4_ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) (This)->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_QueryInterface(IDWriteFactory4* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFactory4_AddRef(IDWriteFactory4* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFactory4_Release(IDWriteFactory4* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFactory methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_CreateCustomFontCollection(IDWriteFactory4* This,IDWriteFontCollectionLoader *loader,const void *key,UINT32 key_size,IDWriteFontCollection **collection) { + return This->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection); +} +static FORCEINLINE HRESULT IDWriteFactory4_RegisterFontCollectionLoader(IDWriteFactory4* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->RegisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory4_UnregisterFontCollectionLoader(IDWriteFactory4* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->UnregisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontFileReference(IDWriteFactory4* This,const WCHAR *path,const FILETIME *writetime,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateFontFileReference(This,path,writetime,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateCustomFontFileReference(IDWriteFactory4* This,const void *reference_key,UINT32 key_size,IDWriteFontFileLoader *loader,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontFace(IDWriteFactory4* This,DWRITE_FONT_FACE_TYPE facetype,UINT32 files_number,IDWriteFontFile *const *font_files,UINT32 index,DWRITE_FONT_SIMULATIONS sim_flags,IDWriteFontFace **font_face) { + return This->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateRenderingParams(IDWriteFactory4* This,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateRenderingParams(This,params); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateMonitorRenderingParams(IDWriteFactory4* This,HMONITOR monitor,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateMonitorRenderingParams(This,monitor,params); +} +static FORCEINLINE HRESULT IDWriteFactory4_RegisterFontFileLoader(IDWriteFactory4* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->RegisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory4_UnregisterFontFileLoader(IDWriteFactory4* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->UnregisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateTextFormat(IDWriteFactory4* This,const WCHAR *family_name,IDWriteFontCollection *collection,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STYLE style,DWRITE_FONT_STRETCH stretch,FLOAT size,const WCHAR *locale,IDWriteTextFormat **format) { + return This->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateTypography(IDWriteFactory4* This,IDWriteTypography **typography) { + return This->lpVtbl->CreateTypography(This,typography); +} +static FORCEINLINE HRESULT IDWriteFactory4_GetGdiInterop(IDWriteFactory4* This,IDWriteGdiInterop **gdi_interop) { + return This->lpVtbl->GetGdiInterop(This,gdi_interop); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateTextLayout(IDWriteFactory4* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT max_width,FLOAT max_height,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateGdiCompatibleTextLayout(IDWriteFactory4* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT layout_width,FLOAT layout_height,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateEllipsisTrimmingSign(IDWriteFactory4* This,IDWriteTextFormat *format,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateTextAnalyzer(IDWriteFactory4* This,IDWriteTextAnalyzer **analyzer) { + return This->lpVtbl->CreateTextAnalyzer(This,analyzer); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateNumberSubstitution(IDWriteFactory4* This,DWRITE_NUMBER_SUBSTITUTION_METHOD method,const WCHAR *locale,WINBOOL ignore_user_override,IDWriteNumberSubstitution **substitution) { + return This->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution); +} +/*** IDWriteFactory1 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_GetEudcFontCollection(IDWriteFactory4* This,IDWriteFontCollection **collection,WINBOOL check_for_updates) { + return This->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates); +} +/*** IDWriteFactory2 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_GetSystemFontFallback(IDWriteFactory4* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetSystemFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontFallbackBuilder(IDWriteFactory4* This,IDWriteFontFallbackBuilder **fallbackbuilder) { + return This->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder); +} +/*** IDWriteFactory3 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_CreateGlyphRunAnalysis(IDWriteFactory4* This,const DWRITE_GLYPH_RUN *run,const DWRITE_MATRIX *transform,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_MEASURING_MODE measuring_mode,DWRITE_GRID_FIT_MODE gridfit_mode,DWRITE_TEXT_ANTIALIAS_MODE antialias_mode,FLOAT origin_x,FLOAT origin_y,IDWriteGlyphRunAnalysis **analysis) { + return This->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateCustomRenderingParams(IDWriteFactory4* This,FLOAT gamma,FLOAT enhanced_contrast,FLOAT grayscale_enhanced_contrast,FLOAT cleartype_level,DWRITE_PIXEL_GEOMETRY pixel_geometry,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_GRID_FIT_MODE gridfit_mode,IDWriteRenderingParams3 **params) { + return This->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontFaceReference_(IDWriteFactory4* This,IDWriteFontFile *file,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontFaceReference(IDWriteFactory4* This,const WCHAR *path,const FILETIME *writetime,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory4_GetSystemFontSet(IDWriteFactory4* This,IDWriteFontSet **fontset) { + return This->lpVtbl->GetSystemFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontSetBuilder(IDWriteFactory4* This,IDWriteFontSetBuilder **builder) { + return This->lpVtbl->CreateFontSetBuilder(This,builder); +} +static FORCEINLINE HRESULT IDWriteFactory4_CreateFontCollectionFromFontSet(IDWriteFactory4* This,IDWriteFontSet *fontset,IDWriteFontCollection1 **collection) { + return This->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection); +} +static FORCEINLINE HRESULT IDWriteFactory4_GetSystemFontCollection(IDWriteFactory4* This,WINBOOL include_downloadable,IDWriteFontCollection1 **collection,WINBOOL check_for_updates) { + return This->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates); +} +static FORCEINLINE HRESULT IDWriteFactory4_GetFontDownloadQueue(IDWriteFactory4* This,IDWriteFontDownloadQueue **queue) { + return This->lpVtbl->GetFontDownloadQueue(This,queue); +} +/*** IDWriteFactory4 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory4_TranslateColorGlyphRun(IDWriteFactory4* This,D2D1_POINT_2F baseline_origin,const DWRITE_GLYPH_RUN *run,const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc,DWRITE_GLYPH_IMAGE_FORMATS desired_formats,DWRITE_MEASURING_MODE measuring_mode,const DWRITE_MATRIX *transform,UINT32 palette,IDWriteColorGlyphRunEnumerator1 **layers) { + return This->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers); +} +static FORCEINLINE HRESULT IDWriteFactory4_ComputeGlyphOrigins_(IDWriteFactory4* This,const DWRITE_GLYPH_RUN *run,D2D1_POINT_2F baseline_origin,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins); +} +static FORCEINLINE HRESULT IDWriteFactory4_ComputeGlyphOrigins(IDWriteFactory4* This,const DWRITE_GLYPH_RUN *run,DWRITE_MEASURING_MODE measuring_mode,D2D1_POINT_2F baseline_origin,const DWRITE_MATRIX *transform,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFactory4_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteAsyncResult interface + */ +#ifndef __IDWriteAsyncResult_INTERFACE_DEFINED__ +#define __IDWriteAsyncResult_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteAsyncResult, 0xce25f8fd, 0x863b, 0x4d13, 0x96,0x51, 0xc1,0xf8,0x8d,0xc7,0x3f,0xe2); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("ce25f8fd-863b-4d13-9651-c1f88dc73fe2") +IDWriteAsyncResult : public IUnknown +{ + virtual HANDLE STDMETHODCALLTYPE GetWaitHandle( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetResult( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteAsyncResult, 0xce25f8fd, 0x863b, 0x4d13, 0x96,0x51, 0xc1,0xf8,0x8d,0xc7,0x3f,0xe2) +#endif +#else +typedef struct IDWriteAsyncResultVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteAsyncResult *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteAsyncResult *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteAsyncResult *This); + + /*** IDWriteAsyncResult methods ***/ + HANDLE (STDMETHODCALLTYPE *GetWaitHandle)( + IDWriteAsyncResult *This); + + HRESULT (STDMETHODCALLTYPE *GetResult)( + IDWriteAsyncResult *This); + + END_INTERFACE +} IDWriteAsyncResultVtbl; + +interface IDWriteAsyncResult { + CONST_VTBL IDWriteAsyncResultVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteAsyncResult_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteAsyncResult_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteAsyncResult_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteAsyncResult methods ***/ +#define IDWriteAsyncResult_GetWaitHandle(This) (This)->lpVtbl->GetWaitHandle(This) +#define IDWriteAsyncResult_GetResult(This) (This)->lpVtbl->GetResult(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteAsyncResult_QueryInterface(IDWriteAsyncResult* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteAsyncResult_AddRef(IDWriteAsyncResult* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteAsyncResult_Release(IDWriteAsyncResult* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteAsyncResult methods ***/ +static FORCEINLINE HANDLE IDWriteAsyncResult_GetWaitHandle(IDWriteAsyncResult* This) { + return This->lpVtbl->GetWaitHandle(This); +} +static FORCEINLINE HRESULT IDWriteAsyncResult_GetResult(IDWriteAsyncResult* This) { + return This->lpVtbl->GetResult(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteAsyncResult_INTERFACE_DEFINED__ */ + +typedef struct DWRITE_FILE_FRAGMENT { + UINT64 fileOffset; + UINT64 fragmentSize; +} DWRITE_FILE_FRAGMENT; +/***************************************************************************** + * IDWriteRemoteFontFileStream interface + */ +#ifndef __IDWriteRemoteFontFileStream_INTERFACE_DEFINED__ +#define __IDWriteRemoteFontFileStream_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteRemoteFontFileStream, 0x4db3757a, 0x2c72, 0x4ed9, 0xb2,0xb6, 0x1a,0xba,0xbe,0x1a,0xff,0x9c); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("4db3757a-2c72-4ed9-b2b6-1ababe1aff9c") +IDWriteRemoteFontFileStream : public IDWriteFontFileStream +{ + virtual HRESULT STDMETHODCALLTYPE GetLocalFileSize( + UINT64 *size) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFileFragmentLocality( + UINT64 offset, + UINT64 size, + WINBOOL *is_local, + UINT64 *partial_size) = 0; + + virtual DWRITE_LOCALITY STDMETHODCALLTYPE GetLocality( + ) = 0; + + virtual HRESULT STDMETHODCALLTYPE BeginDownload( + const GUID *operation_id, + const DWRITE_FILE_FRAGMENT *fragments, + UINT32 fragment_count, + IDWriteAsyncResult **async_result) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteRemoteFontFileStream, 0x4db3757a, 0x2c72, 0x4ed9, 0xb2,0xb6, 0x1a,0xba,0xbe,0x1a,0xff,0x9c) +#endif +#else +typedef struct IDWriteRemoteFontFileStreamVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteRemoteFontFileStream *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteRemoteFontFileStream *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteRemoteFontFileStream *This); + + /*** IDWriteFontFileStream methods ***/ + HRESULT (STDMETHODCALLTYPE *ReadFileFragment)( + IDWriteRemoteFontFileStream *This, + const void **fragment_start, + UINT64 offset, + UINT64 fragment_size, + void **fragment_context); + + void (STDMETHODCALLTYPE *ReleaseFileFragment)( + IDWriteRemoteFontFileStream *This, + void *fragment_context); + + HRESULT (STDMETHODCALLTYPE *GetFileSize)( + IDWriteRemoteFontFileStream *This, + UINT64 *size); + + HRESULT (STDMETHODCALLTYPE *GetLastWriteTime)( + IDWriteRemoteFontFileStream *This, + UINT64 *last_writetime); + + /*** IDWriteRemoteFontFileStream methods ***/ + HRESULT (STDMETHODCALLTYPE *GetLocalFileSize)( + IDWriteRemoteFontFileStream *This, + UINT64 *size); + + HRESULT (STDMETHODCALLTYPE *GetFileFragmentLocality)( + IDWriteRemoteFontFileStream *This, + UINT64 offset, + UINT64 size, + WINBOOL *is_local, + UINT64 *partial_size); + + DWRITE_LOCALITY (STDMETHODCALLTYPE *GetLocality)( + IDWriteRemoteFontFileStream *This); + + HRESULT (STDMETHODCALLTYPE *BeginDownload)( + IDWriteRemoteFontFileStream *This, + const GUID *operation_id, + const DWRITE_FILE_FRAGMENT *fragments, + UINT32 fragment_count, + IDWriteAsyncResult **async_result); + + END_INTERFACE +} IDWriteRemoteFontFileStreamVtbl; + +interface IDWriteRemoteFontFileStream { + CONST_VTBL IDWriteRemoteFontFileStreamVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteRemoteFontFileStream_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteRemoteFontFileStream_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteRemoteFontFileStream_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFileStream methods ***/ +#define IDWriteRemoteFontFileStream_ReadFileFragment(This,fragment_start,offset,fragment_size,fragment_context) (This)->lpVtbl->ReadFileFragment(This,fragment_start,offset,fragment_size,fragment_context) +#define IDWriteRemoteFontFileStream_ReleaseFileFragment(This,fragment_context) (This)->lpVtbl->ReleaseFileFragment(This,fragment_context) +#define IDWriteRemoteFontFileStream_GetFileSize(This,size) (This)->lpVtbl->GetFileSize(This,size) +#define IDWriteRemoteFontFileStream_GetLastWriteTime(This,last_writetime) (This)->lpVtbl->GetLastWriteTime(This,last_writetime) +/*** IDWriteRemoteFontFileStream methods ***/ +#define IDWriteRemoteFontFileStream_GetLocalFileSize(This,size) (This)->lpVtbl->GetLocalFileSize(This,size) +#define IDWriteRemoteFontFileStream_GetFileFragmentLocality(This,offset,size,is_local,partial_size) (This)->lpVtbl->GetFileFragmentLocality(This,offset,size,is_local,partial_size) +#define IDWriteRemoteFontFileStream_GetLocality(This) (This)->lpVtbl->GetLocality(This) +#define IDWriteRemoteFontFileStream_BeginDownload(This,operation_id,fragments,fragment_count,async_result) (This)->lpVtbl->BeginDownload(This,operation_id,fragments,fragment_count,async_result) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_QueryInterface(IDWriteRemoteFontFileStream* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteRemoteFontFileStream_AddRef(IDWriteRemoteFontFileStream* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteRemoteFontFileStream_Release(IDWriteRemoteFontFileStream* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFileStream methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_ReadFileFragment(IDWriteRemoteFontFileStream* This,const void **fragment_start,UINT64 offset,UINT64 fragment_size,void **fragment_context) { + return This->lpVtbl->ReadFileFragment(This,fragment_start,offset,fragment_size,fragment_context); +} +static FORCEINLINE void IDWriteRemoteFontFileStream_ReleaseFileFragment(IDWriteRemoteFontFileStream* This,void *fragment_context) { + This->lpVtbl->ReleaseFileFragment(This,fragment_context); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_GetFileSize(IDWriteRemoteFontFileStream* This,UINT64 *size) { + return This->lpVtbl->GetFileSize(This,size); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_GetLastWriteTime(IDWriteRemoteFontFileStream* This,UINT64 *last_writetime) { + return This->lpVtbl->GetLastWriteTime(This,last_writetime); +} +/*** IDWriteRemoteFontFileStream methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_GetLocalFileSize(IDWriteRemoteFontFileStream* This,UINT64 *size) { + return This->lpVtbl->GetLocalFileSize(This,size); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_GetFileFragmentLocality(IDWriteRemoteFontFileStream* This,UINT64 offset,UINT64 size,WINBOOL *is_local,UINT64 *partial_size) { + return This->lpVtbl->GetFileFragmentLocality(This,offset,size,is_local,partial_size); +} +static FORCEINLINE DWRITE_LOCALITY IDWriteRemoteFontFileStream_GetLocality(IDWriteRemoteFontFileStream* This) { + return This->lpVtbl->GetLocality(This); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileStream_BeginDownload(IDWriteRemoteFontFileStream* This,const GUID *operation_id,const DWRITE_FILE_FRAGMENT *fragments,UINT32 fragment_count,IDWriteAsyncResult **async_result) { + return This->lpVtbl->BeginDownload(This,operation_id,fragments,fragment_count,async_result); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteRemoteFontFileStream_INTERFACE_DEFINED__ */ + +typedef enum DWRITE_CONTAINER_TYPE { + DWRITE_CONTAINER_TYPE_UNKNOWN = 0, + DWRITE_CONTAINER_TYPE_WOFF = 1, + DWRITE_CONTAINER_TYPE_WOFF2 = 2 +} DWRITE_CONTAINER_TYPE; +/***************************************************************************** + * IDWriteRemoteFontFileLoader interface + */ +#ifndef __IDWriteRemoteFontFileLoader_INTERFACE_DEFINED__ +#define __IDWriteRemoteFontFileLoader_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteRemoteFontFileLoader, 0x68648c83, 0x6ede, 0x46c0, 0xab,0x46, 0x20,0x08,0x3a,0x88,0x7f,0xde); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("68648c83-6ede-46c0-ab46-20083a887fde") +IDWriteRemoteFontFileLoader : public IDWriteFontFileLoader +{ + virtual HRESULT STDMETHODCALLTYPE CreateRemoteStreamFromKey( + const void *key, + UINT32 key_size, + IDWriteRemoteFontFileStream **stream) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLocalityFromKey( + const void *key, + UINT32 key_size, + DWRITE_LOCALITY *locality) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateFontFileReferenceFromUrl( + IDWriteFactory *factory, + const WCHAR *base_url, + const WCHAR *file_url, + IDWriteFontFile **fontfile) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteRemoteFontFileLoader, 0x68648c83, 0x6ede, 0x46c0, 0xab,0x46, 0x20,0x08,0x3a,0x88,0x7f,0xde) +#endif +#else +typedef struct IDWriteRemoteFontFileLoaderVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteRemoteFontFileLoader *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteRemoteFontFileLoader *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteRemoteFontFileLoader *This); + + /*** IDWriteFontFileLoader methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateStreamFromKey)( + IDWriteRemoteFontFileLoader *This, + const void *key, + UINT32 key_size, + IDWriteFontFileStream **stream); + + /*** IDWriteRemoteFontFileLoader methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateRemoteStreamFromKey)( + IDWriteRemoteFontFileLoader *This, + const void *key, + UINT32 key_size, + IDWriteRemoteFontFileStream **stream); + + HRESULT (STDMETHODCALLTYPE *GetLocalityFromKey)( + IDWriteRemoteFontFileLoader *This, + const void *key, + UINT32 key_size, + DWRITE_LOCALITY *locality); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReferenceFromUrl)( + IDWriteRemoteFontFileLoader *This, + IDWriteFactory *factory, + const WCHAR *base_url, + const WCHAR *file_url, + IDWriteFontFile **fontfile); + + END_INTERFACE +} IDWriteRemoteFontFileLoaderVtbl; + +interface IDWriteRemoteFontFileLoader { + CONST_VTBL IDWriteRemoteFontFileLoaderVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteRemoteFontFileLoader_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteRemoteFontFileLoader_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteRemoteFontFileLoader_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFileLoader methods ***/ +#define IDWriteRemoteFontFileLoader_CreateStreamFromKey(This,key,key_size,stream) (This)->lpVtbl->CreateStreamFromKey(This,key,key_size,stream) +/*** IDWriteRemoteFontFileLoader methods ***/ +#define IDWriteRemoteFontFileLoader_CreateRemoteStreamFromKey(This,key,key_size,stream) (This)->lpVtbl->CreateRemoteStreamFromKey(This,key,key_size,stream) +#define IDWriteRemoteFontFileLoader_GetLocalityFromKey(This,key,key_size,locality) (This)->lpVtbl->GetLocalityFromKey(This,key,key_size,locality) +#define IDWriteRemoteFontFileLoader_CreateFontFileReferenceFromUrl(This,factory,base_url,file_url,fontfile) (This)->lpVtbl->CreateFontFileReferenceFromUrl(This,factory,base_url,file_url,fontfile) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileLoader_QueryInterface(IDWriteRemoteFontFileLoader* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteRemoteFontFileLoader_AddRef(IDWriteRemoteFontFileLoader* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteRemoteFontFileLoader_Release(IDWriteRemoteFontFileLoader* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFileLoader methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileLoader_CreateStreamFromKey(IDWriteRemoteFontFileLoader* This,const void *key,UINT32 key_size,IDWriteFontFileStream **stream) { + return This->lpVtbl->CreateStreamFromKey(This,key,key_size,stream); +} +/*** IDWriteRemoteFontFileLoader methods ***/ +static FORCEINLINE HRESULT IDWriteRemoteFontFileLoader_CreateRemoteStreamFromKey(IDWriteRemoteFontFileLoader* This,const void *key,UINT32 key_size,IDWriteRemoteFontFileStream **stream) { + return This->lpVtbl->CreateRemoteStreamFromKey(This,key,key_size,stream); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileLoader_GetLocalityFromKey(IDWriteRemoteFontFileLoader* This,const void *key,UINT32 key_size,DWRITE_LOCALITY *locality) { + return This->lpVtbl->GetLocalityFromKey(This,key,key_size,locality); +} +static FORCEINLINE HRESULT IDWriteRemoteFontFileLoader_CreateFontFileReferenceFromUrl(IDWriteRemoteFontFileLoader* This,IDWriteFactory *factory,const WCHAR *base_url,const WCHAR *file_url,IDWriteFontFile **fontfile) { + return This->lpVtbl->CreateFontFileReferenceFromUrl(This,factory,base_url,file_url,fontfile); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteRemoteFontFileLoader_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteInMemoryFontFileLoader interface + */ +#ifndef __IDWriteInMemoryFontFileLoader_INTERFACE_DEFINED__ +#define __IDWriteInMemoryFontFileLoader_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteInMemoryFontFileLoader, 0xdc102f47, 0xa12d, 0x4b1c, 0x82,0x2d, 0x9e,0x11,0x7e,0x33,0x04,0x3f); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("dc102f47-a12d-4b1c-822d-9e117e33043f") +IDWriteInMemoryFontFileLoader : public IDWriteFontFileLoader +{ + virtual HRESULT STDMETHODCALLTYPE CreateInMemoryFontFileReference( + IDWriteFactory *factory, + const void *data, + UINT32 data_size, + IUnknown *owner, + IDWriteFontFile **fontfile) = 0; + + virtual UINT32 STDMETHODCALLTYPE GetFileCount( + ) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteInMemoryFontFileLoader, 0xdc102f47, 0xa12d, 0x4b1c, 0x82,0x2d, 0x9e,0x11,0x7e,0x33,0x04,0x3f) +#endif +#else +typedef struct IDWriteInMemoryFontFileLoaderVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteInMemoryFontFileLoader *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteInMemoryFontFileLoader *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteInMemoryFontFileLoader *This); + + /*** IDWriteFontFileLoader methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateStreamFromKey)( + IDWriteInMemoryFontFileLoader *This, + const void *key, + UINT32 key_size, + IDWriteFontFileStream **stream); + + /*** IDWriteInMemoryFontFileLoader methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateInMemoryFontFileReference)( + IDWriteInMemoryFontFileLoader *This, + IDWriteFactory *factory, + const void *data, + UINT32 data_size, + IUnknown *owner, + IDWriteFontFile **fontfile); + + UINT32 (STDMETHODCALLTYPE *GetFileCount)( + IDWriteInMemoryFontFileLoader *This); + + END_INTERFACE +} IDWriteInMemoryFontFileLoaderVtbl; + +interface IDWriteInMemoryFontFileLoader { + CONST_VTBL IDWriteInMemoryFontFileLoaderVtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteInMemoryFontFileLoader_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteInMemoryFontFileLoader_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteInMemoryFontFileLoader_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFontFileLoader methods ***/ +#define IDWriteInMemoryFontFileLoader_CreateStreamFromKey(This,key,key_size,stream) (This)->lpVtbl->CreateStreamFromKey(This,key,key_size,stream) +/*** IDWriteInMemoryFontFileLoader methods ***/ +#define IDWriteInMemoryFontFileLoader_CreateInMemoryFontFileReference(This,factory,data,data_size,owner,fontfile) (This)->lpVtbl->CreateInMemoryFontFileReference(This,factory,data,data_size,owner,fontfile) +#define IDWriteInMemoryFontFileLoader_GetFileCount(This) (This)->lpVtbl->GetFileCount(This) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteInMemoryFontFileLoader_QueryInterface(IDWriteInMemoryFontFileLoader* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteInMemoryFontFileLoader_AddRef(IDWriteInMemoryFontFileLoader* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteInMemoryFontFileLoader_Release(IDWriteInMemoryFontFileLoader* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFontFileLoader methods ***/ +static FORCEINLINE HRESULT IDWriteInMemoryFontFileLoader_CreateStreamFromKey(IDWriteInMemoryFontFileLoader* This,const void *key,UINT32 key_size,IDWriteFontFileStream **stream) { + return This->lpVtbl->CreateStreamFromKey(This,key,key_size,stream); +} +/*** IDWriteInMemoryFontFileLoader methods ***/ +static FORCEINLINE HRESULT IDWriteInMemoryFontFileLoader_CreateInMemoryFontFileReference(IDWriteInMemoryFontFileLoader* This,IDWriteFactory *factory,const void *data,UINT32 data_size,IUnknown *owner,IDWriteFontFile **fontfile) { + return This->lpVtbl->CreateInMemoryFontFileReference(This,factory,data,data_size,owner,fontfile); +} +static FORCEINLINE UINT32 IDWriteInMemoryFontFileLoader_GetFileCount(IDWriteInMemoryFontFileLoader* This) { + return This->lpVtbl->GetFileCount(This); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteInMemoryFontFileLoader_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFactory5 interface + */ +#ifndef __IDWriteFactory5_INTERFACE_DEFINED__ +#define __IDWriteFactory5_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFactory5, 0x958db99a, 0xbe2a, 0x4f09, 0xaf,0x7d, 0x65,0x18,0x98,0x03,0xd1,0xd3); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("958db99a-be2a-4f09-af7d-65189803d1d3") +IDWriteFactory5 : public IDWriteFactory4 +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontSetBuilder( + IDWriteFontSetBuilder1 **fontset_builder) = 0; + + using IDWriteFactory3::CreateFontSetBuilder; + + virtual HRESULT STDMETHODCALLTYPE CreateInMemoryFontFileLoader( + IDWriteInMemoryFontFileLoader **loader) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateHttpFontFileLoader( + const WCHAR *referrer_url, + const WCHAR *extra_headers, + IDWriteRemoteFontFileLoader **loader) = 0; + + virtual DWRITE_CONTAINER_TYPE STDMETHODCALLTYPE AnalyzeContainerType( + const void *data, + UINT32 data_size) = 0; + + virtual HRESULT STDMETHODCALLTYPE UnpackFontFile( + DWRITE_CONTAINER_TYPE container_type, + const void *data, + UINT32 data_size, + IDWriteFontFileStream **stream) = 0; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFactory5, 0x958db99a, 0xbe2a, 0x4f09, 0xaf,0x7d, 0x65,0x18,0x98,0x03,0xd1,0xd3) +#endif +#else +typedef struct IDWriteFactory5Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFactory5 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFactory5 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFactory5 *This); + + /*** IDWriteFactory methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontCollection)( + IDWriteFactory5 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontCollection)( + IDWriteFactory5 *This, + IDWriteFontCollectionLoader *loader, + const void *key, + UINT32 key_size, + IDWriteFontCollection **collection); + + HRESULT (STDMETHODCALLTYPE *RegisterFontCollectionLoader)( + IDWriteFactory5 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontCollectionLoader)( + IDWriteFactory5 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReference)( + IDWriteFactory5 *This, + const WCHAR *path, + const FILETIME *writetime, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontFileReference)( + IDWriteFactory5 *This, + const void *reference_key, + UINT32 key_size, + IDWriteFontFileLoader *loader, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFactory5 *This, + DWRITE_FONT_FACE_TYPE facetype, + UINT32 files_number, + IDWriteFontFile *const *font_files, + UINT32 index, + DWRITE_FONT_SIMULATIONS sim_flags, + IDWriteFontFace **font_face); + + HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)( + IDWriteFactory5 *This, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateMonitorRenderingParams)( + IDWriteFactory5 *This, + HMONITOR monitor, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)( + IDWriteFactory5 *This, + FLOAT gamma, + FLOAT enhancedContrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *RegisterFontFileLoader)( + IDWriteFactory5 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontFileLoader)( + IDWriteFactory5 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateTextFormat)( + IDWriteFactory5 *This, + const WCHAR *family_name, + IDWriteFontCollection *collection, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, + FLOAT size, + const WCHAR *locale, + IDWriteTextFormat **format); + + HRESULT (STDMETHODCALLTYPE *CreateTypography)( + IDWriteFactory5 *This, + IDWriteTypography **typography); + + HRESULT (STDMETHODCALLTYPE *GetGdiInterop)( + IDWriteFactory5 *This, + IDWriteGdiInterop **gdi_interop); + + HRESULT (STDMETHODCALLTYPE *CreateTextLayout)( + IDWriteFactory5 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT max_width, + FLOAT max_height, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateGdiCompatibleTextLayout)( + IDWriteFactory5 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT layout_width, + FLOAT layout_height, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateEllipsisTrimmingSign)( + IDWriteFactory5 *This, + IDWriteTextFormat *format, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *CreateTextAnalyzer)( + IDWriteFactory5 *This, + IDWriteTextAnalyzer **analyzer); + + HRESULT (STDMETHODCALLTYPE *CreateNumberSubstitution)( + IDWriteFactory5 *This, + DWRITE_NUMBER_SUBSTITUTION_METHOD method, + const WCHAR *locale, + WINBOOL ignore_user_override, + IDWriteNumberSubstitution **substitution); + + HRESULT (STDMETHODCALLTYPE *CreateGlyphRunAnalysis)( + IDWriteFactory5 *This, + const DWRITE_GLYPH_RUN *glyph_run, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + FLOAT baseline_x, + FLOAT baseline_y, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetEudcFontCollection)( + IDWriteFactory5 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory1_CreateCustomRenderingParams)( + IDWriteFactory5 *This, + FLOAT gamma, + FLOAT enhcontrast, + FLOAT enhcontrast_grayscale, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams1 **params); + + /*** IDWriteFactory2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontFallback)( + IDWriteFactory5 *This, + IDWriteFontFallback **fallback); + + HRESULT (STDMETHODCALLTYPE *CreateFontFallbackBuilder)( + IDWriteFactory5 *This, + IDWriteFontFallbackBuilder **fallbackbuilder); + + HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)( + IDWriteFactory5 *This, + FLOAT originX, + FLOAT originY, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, + DWRITE_MEASURING_MODE mode, + const DWRITE_MATRIX *transform, + UINT32 palette_index, + IDWriteColorGlyphRunEnumerator **colorlayers); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateCustomRenderingParams)( + IDWriteFactory5 *This, + FLOAT gamma, + FLOAT contrast, + FLOAT grayscalecontrast, + FLOAT cleartypeLevel, + DWRITE_PIXEL_GEOMETRY pixelGeometry, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_GRID_FIT_MODE gridFitMode, + IDWriteRenderingParams2 **params); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateGlyphRunAnalysis)( + IDWriteFactory5 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GRID_FIT_MODE gridFitMode, + DWRITE_TEXT_ANTIALIAS_MODE antialiasMode, + FLOAT originX, + FLOAT originY, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateGlyphRunAnalysis)( + IDWriteFactory5 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateCustomRenderingParams)( + IDWriteFactory5 *This, + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference_)( + IDWriteFactory5 *This, + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFactory5 *This, + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *GetSystemFontSet)( + IDWriteFactory5 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSetBuilder)( + IDWriteFactory5 *This, + IDWriteFontSetBuilder **builder); + + HRESULT (STDMETHODCALLTYPE *CreateFontCollectionFromFontSet)( + IDWriteFactory5 *This, + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_GetSystemFontCollection)( + IDWriteFactory5 *This, + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *GetFontDownloadQueue)( + IDWriteFactory5 *This, + IDWriteFontDownloadQueue **queue); + + /*** IDWriteFactory4 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory4_TranslateColorGlyphRun)( + IDWriteFactory5 *This, + D2D1_POINT_2F baseline_origin, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc, + DWRITE_GLYPH_IMAGE_FORMATS desired_formats, + DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_MATRIX *transform, + UINT32 palette, + IDWriteColorGlyphRunEnumerator1 **layers); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins_)( + IDWriteFactory5 *This, + const DWRITE_GLYPH_RUN *run, + D2D1_POINT_2F baseline_origin, + D2D1_POINT_2F *origins); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins)( + IDWriteFactory5 *This, + const DWRITE_GLYPH_RUN *run, + DWRITE_MEASURING_MODE measuring_mode, + D2D1_POINT_2F baseline_origin, + const DWRITE_MATRIX *transform, + D2D1_POINT_2F *origins); + + /*** IDWriteFactory5 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory5_CreateFontSetBuilder)( + IDWriteFactory5 *This, + IDWriteFontSetBuilder1 **fontset_builder); + + HRESULT (STDMETHODCALLTYPE *CreateInMemoryFontFileLoader)( + IDWriteFactory5 *This, + IDWriteInMemoryFontFileLoader **loader); + + HRESULT (STDMETHODCALLTYPE *CreateHttpFontFileLoader)( + IDWriteFactory5 *This, + const WCHAR *referrer_url, + const WCHAR *extra_headers, + IDWriteRemoteFontFileLoader **loader); + + DWRITE_CONTAINER_TYPE (STDMETHODCALLTYPE *AnalyzeContainerType)( + IDWriteFactory5 *This, + const void *data, + UINT32 data_size); + + HRESULT (STDMETHODCALLTYPE *UnpackFontFile)( + IDWriteFactory5 *This, + DWRITE_CONTAINER_TYPE container_type, + const void *data, + UINT32 data_size, + IDWriteFontFileStream **stream); + + END_INTERFACE +} IDWriteFactory5Vtbl; + +interface IDWriteFactory5 { + CONST_VTBL IDWriteFactory5Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFactory5_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFactory5_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFactory5_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFactory methods ***/ +#define IDWriteFactory5_CreateCustomFontCollection(This,loader,key,key_size,collection) (This)->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection) +#define IDWriteFactory5_RegisterFontCollectionLoader(This,loader) (This)->lpVtbl->RegisterFontCollectionLoader(This,loader) +#define IDWriteFactory5_UnregisterFontCollectionLoader(This,loader) (This)->lpVtbl->UnregisterFontCollectionLoader(This,loader) +#define IDWriteFactory5_CreateFontFileReference(This,path,writetime,font_file) (This)->lpVtbl->CreateFontFileReference(This,path,writetime,font_file) +#define IDWriteFactory5_CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) (This)->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) +#define IDWriteFactory5_CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) (This)->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) +#define IDWriteFactory5_CreateRenderingParams(This,params) (This)->lpVtbl->CreateRenderingParams(This,params) +#define IDWriteFactory5_CreateMonitorRenderingParams(This,monitor,params) (This)->lpVtbl->CreateMonitorRenderingParams(This,monitor,params) +#define IDWriteFactory5_RegisterFontFileLoader(This,loader) (This)->lpVtbl->RegisterFontFileLoader(This,loader) +#define IDWriteFactory5_UnregisterFontFileLoader(This,loader) (This)->lpVtbl->UnregisterFontFileLoader(This,loader) +#define IDWriteFactory5_CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) (This)->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format) +#define IDWriteFactory5_CreateTypography(This,typography) (This)->lpVtbl->CreateTypography(This,typography) +#define IDWriteFactory5_GetGdiInterop(This,gdi_interop) (This)->lpVtbl->GetGdiInterop(This,gdi_interop) +#define IDWriteFactory5_CreateTextLayout(This,string,len,format,max_width,max_height,layout) (This)->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout) +#define IDWriteFactory5_CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) (This)->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) +#define IDWriteFactory5_CreateEllipsisTrimmingSign(This,format,trimming_sign) (This)->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign) +#define IDWriteFactory5_CreateTextAnalyzer(This,analyzer) (This)->lpVtbl->CreateTextAnalyzer(This,analyzer) +#define IDWriteFactory5_CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) (This)->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) +/*** IDWriteFactory1 methods ***/ +#define IDWriteFactory5_GetEudcFontCollection(This,collection,check_for_updates) (This)->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates) +/*** IDWriteFactory2 methods ***/ +#define IDWriteFactory5_GetSystemFontFallback(This,fallback) (This)->lpVtbl->GetSystemFontFallback(This,fallback) +#define IDWriteFactory5_CreateFontFallbackBuilder(This,fallbackbuilder) (This)->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder) +/*** IDWriteFactory3 methods ***/ +#define IDWriteFactory5_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) (This)->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) +#define IDWriteFactory5_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) (This)->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) +#define IDWriteFactory5_CreateFontFaceReference_(This,file,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference) +#define IDWriteFactory5_CreateFontFaceReference(This,path,writetime,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference) +#define IDWriteFactory5_GetSystemFontSet(This,fontset) (This)->lpVtbl->GetSystemFontSet(This,fontset) +#define IDWriteFactory5_CreateFontCollectionFromFontSet(This,fontset,collection) (This)->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection) +#define IDWriteFactory5_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) (This)->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates) +#define IDWriteFactory5_GetFontDownloadQueue(This,queue) (This)->lpVtbl->GetFontDownloadQueue(This,queue) +/*** IDWriteFactory4 methods ***/ +#define IDWriteFactory5_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) (This)->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) +#define IDWriteFactory5_ComputeGlyphOrigins_(This,run,baseline_origin,origins) (This)->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins) +#define IDWriteFactory5_ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) (This)->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) +/*** IDWriteFactory5 methods ***/ +#define IDWriteFactory5_CreateFontSetBuilder(This,fontset_builder) (This)->lpVtbl->IDWriteFactory5_CreateFontSetBuilder(This,fontset_builder) +#define IDWriteFactory5_CreateInMemoryFontFileLoader(This,loader) (This)->lpVtbl->CreateInMemoryFontFileLoader(This,loader) +#define IDWriteFactory5_CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) (This)->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) +#define IDWriteFactory5_AnalyzeContainerType(This,data,data_size) (This)->lpVtbl->AnalyzeContainerType(This,data,data_size) +#define IDWriteFactory5_UnpackFontFile(This,container_type,data,data_size,stream) (This)->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_QueryInterface(IDWriteFactory5* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFactory5_AddRef(IDWriteFactory5* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFactory5_Release(IDWriteFactory5* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFactory methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_CreateCustomFontCollection(IDWriteFactory5* This,IDWriteFontCollectionLoader *loader,const void *key,UINT32 key_size,IDWriteFontCollection **collection) { + return This->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection); +} +static FORCEINLINE HRESULT IDWriteFactory5_RegisterFontCollectionLoader(IDWriteFactory5* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->RegisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory5_UnregisterFontCollectionLoader(IDWriteFactory5* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->UnregisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontFileReference(IDWriteFactory5* This,const WCHAR *path,const FILETIME *writetime,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateFontFileReference(This,path,writetime,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateCustomFontFileReference(IDWriteFactory5* This,const void *reference_key,UINT32 key_size,IDWriteFontFileLoader *loader,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontFace(IDWriteFactory5* This,DWRITE_FONT_FACE_TYPE facetype,UINT32 files_number,IDWriteFontFile *const *font_files,UINT32 index,DWRITE_FONT_SIMULATIONS sim_flags,IDWriteFontFace **font_face) { + return This->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateRenderingParams(IDWriteFactory5* This,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateRenderingParams(This,params); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateMonitorRenderingParams(IDWriteFactory5* This,HMONITOR monitor,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateMonitorRenderingParams(This,monitor,params); +} +static FORCEINLINE HRESULT IDWriteFactory5_RegisterFontFileLoader(IDWriteFactory5* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->RegisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory5_UnregisterFontFileLoader(IDWriteFactory5* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->UnregisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateTextFormat(IDWriteFactory5* This,const WCHAR *family_name,IDWriteFontCollection *collection,DWRITE_FONT_WEIGHT weight,DWRITE_FONT_STYLE style,DWRITE_FONT_STRETCH stretch,FLOAT size,const WCHAR *locale,IDWriteTextFormat **format) { + return This->lpVtbl->CreateTextFormat(This,family_name,collection,weight,style,stretch,size,locale,format); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateTypography(IDWriteFactory5* This,IDWriteTypography **typography) { + return This->lpVtbl->CreateTypography(This,typography); +} +static FORCEINLINE HRESULT IDWriteFactory5_GetGdiInterop(IDWriteFactory5* This,IDWriteGdiInterop **gdi_interop) { + return This->lpVtbl->GetGdiInterop(This,gdi_interop); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateTextLayout(IDWriteFactory5* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT max_width,FLOAT max_height,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateGdiCompatibleTextLayout(IDWriteFactory5* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT layout_width,FLOAT layout_height,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateEllipsisTrimmingSign(IDWriteFactory5* This,IDWriteTextFormat *format,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateTextAnalyzer(IDWriteFactory5* This,IDWriteTextAnalyzer **analyzer) { + return This->lpVtbl->CreateTextAnalyzer(This,analyzer); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateNumberSubstitution(IDWriteFactory5* This,DWRITE_NUMBER_SUBSTITUTION_METHOD method,const WCHAR *locale,WINBOOL ignore_user_override,IDWriteNumberSubstitution **substitution) { + return This->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution); +} +/*** IDWriteFactory1 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_GetEudcFontCollection(IDWriteFactory5* This,IDWriteFontCollection **collection,WINBOOL check_for_updates) { + return This->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates); +} +/*** IDWriteFactory2 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_GetSystemFontFallback(IDWriteFactory5* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetSystemFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontFallbackBuilder(IDWriteFactory5* This,IDWriteFontFallbackBuilder **fallbackbuilder) { + return This->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder); +} +/*** IDWriteFactory3 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_CreateGlyphRunAnalysis(IDWriteFactory5* This,const DWRITE_GLYPH_RUN *run,const DWRITE_MATRIX *transform,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_MEASURING_MODE measuring_mode,DWRITE_GRID_FIT_MODE gridfit_mode,DWRITE_TEXT_ANTIALIAS_MODE antialias_mode,FLOAT origin_x,FLOAT origin_y,IDWriteGlyphRunAnalysis **analysis) { + return This->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateCustomRenderingParams(IDWriteFactory5* This,FLOAT gamma,FLOAT enhanced_contrast,FLOAT grayscale_enhanced_contrast,FLOAT cleartype_level,DWRITE_PIXEL_GEOMETRY pixel_geometry,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_GRID_FIT_MODE gridfit_mode,IDWriteRenderingParams3 **params) { + return This->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontFaceReference_(IDWriteFactory5* This,IDWriteFontFile *file,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontFaceReference(IDWriteFactory5* This,const WCHAR *path,const FILETIME *writetime,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference(This,path,writetime,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory5_GetSystemFontSet(IDWriteFactory5* This,IDWriteFontSet **fontset) { + return This->lpVtbl->GetSystemFontSet(This,fontset); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontCollectionFromFontSet(IDWriteFactory5* This,IDWriteFontSet *fontset,IDWriteFontCollection1 **collection) { + return This->lpVtbl->CreateFontCollectionFromFontSet(This,fontset,collection); +} +static FORCEINLINE HRESULT IDWriteFactory5_GetSystemFontCollection(IDWriteFactory5* This,WINBOOL include_downloadable,IDWriteFontCollection1 **collection,WINBOOL check_for_updates) { + return This->lpVtbl->IDWriteFactory3_GetSystemFontCollection(This,include_downloadable,collection,check_for_updates); +} +static FORCEINLINE HRESULT IDWriteFactory5_GetFontDownloadQueue(IDWriteFactory5* This,IDWriteFontDownloadQueue **queue) { + return This->lpVtbl->GetFontDownloadQueue(This,queue); +} +/*** IDWriteFactory4 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_TranslateColorGlyphRun(IDWriteFactory5* This,D2D1_POINT_2F baseline_origin,const DWRITE_GLYPH_RUN *run,const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc,DWRITE_GLYPH_IMAGE_FORMATS desired_formats,DWRITE_MEASURING_MODE measuring_mode,const DWRITE_MATRIX *transform,UINT32 palette,IDWriteColorGlyphRunEnumerator1 **layers) { + return This->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers); +} +static FORCEINLINE HRESULT IDWriteFactory5_ComputeGlyphOrigins_(IDWriteFactory5* This,const DWRITE_GLYPH_RUN *run,D2D1_POINT_2F baseline_origin,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins); +} +static FORCEINLINE HRESULT IDWriteFactory5_ComputeGlyphOrigins(IDWriteFactory5* This,const DWRITE_GLYPH_RUN *run,DWRITE_MEASURING_MODE measuring_mode,D2D1_POINT_2F baseline_origin,const DWRITE_MATRIX *transform,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins); +} +/*** IDWriteFactory5 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory5_CreateFontSetBuilder(IDWriteFactory5* This,IDWriteFontSetBuilder1 **fontset_builder) { + return This->lpVtbl->IDWriteFactory5_CreateFontSetBuilder(This,fontset_builder); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateInMemoryFontFileLoader(IDWriteFactory5* This,IDWriteInMemoryFontFileLoader **loader) { + return This->lpVtbl->CreateInMemoryFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory5_CreateHttpFontFileLoader(IDWriteFactory5* This,const WCHAR *referrer_url,const WCHAR *extra_headers,IDWriteRemoteFontFileLoader **loader) { + return This->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader); +} +static FORCEINLINE DWRITE_CONTAINER_TYPE IDWriteFactory5_AnalyzeContainerType(IDWriteFactory5* This,const void *data,UINT32 data_size) { + return This->lpVtbl->AnalyzeContainerType(This,data,data_size); +} +static FORCEINLINE HRESULT IDWriteFactory5_UnpackFontFile(IDWriteFactory5* This,DWRITE_CONTAINER_TYPE container_type,const void *data,UINT32 data_size,IDWriteFontFileStream **stream) { + return This->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFactory5_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFactory6 interface + */ +#ifndef __IDWriteFactory6_INTERFACE_DEFINED__ +#define __IDWriteFactory6_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFactory6, 0xf3744d80, 0x21f7, 0x42eb, 0xb3,0x5d, 0x99,0x5b,0xc7,0x2f,0xc2,0x23); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("f3744d80-21f7-42eb-b35d-995bc72fc223") +IDWriteFactory6 : public IDWriteFactory5 +{ + virtual HRESULT STDMETHODCALLTYPE CreateFontFaceReference( + IDWriteFontFile *file, + UINT32 face_index, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + IDWriteFontFaceReference1 **face_ref) = 0; + + using IDWriteFactory5::CreateFontFaceReference; + + virtual HRESULT STDMETHODCALLTYPE CreateFontResource( + IDWriteFontFile *file, + UINT32 face_index, + IDWriteFontResource **resource) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSystemFontSet( + WINBOOL include_downloadable, + IDWriteFontSet1 **fontset) = 0; + + using IDWriteFactory3::GetSystemFontSet; + + virtual HRESULT STDMETHODCALLTYPE GetSystemFontCollection( + WINBOOL include_downloadable, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection) = 0; + + using IDWriteFactory3::GetSystemFontCollection; + + virtual HRESULT STDMETHODCALLTYPE CreateFontCollectionFromFontSet( + IDWriteFontSet *fontset, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection) = 0; + + using IDWriteFactory5::CreateFontCollectionFromFontSet; + + virtual HRESULT STDMETHODCALLTYPE CreateFontSetBuilder( + IDWriteFontSetBuilder2 **builder) = 0; + + using IDWriteFactory3::CreateFontSetBuilder; + using IDWriteFactory5::CreateFontSetBuilder; + + virtual HRESULT STDMETHODCALLTYPE CreateTextFormat( + const WCHAR *familyname, + IDWriteFontCollection *collection, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + FLOAT fontsize, + const WCHAR *localename, + IDWriteTextFormat3 **format) = 0; + + using IDWriteFactory::CreateTextFormat; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFactory6, 0xf3744d80, 0x21f7, 0x42eb, 0xb3,0x5d, 0x99,0x5b,0xc7,0x2f,0xc2,0x23) +#endif +#else +typedef struct IDWriteFactory6Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFactory6 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFactory6 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFactory6 *This); + + /*** IDWriteFactory methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontCollection)( + IDWriteFactory6 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontCollection)( + IDWriteFactory6 *This, + IDWriteFontCollectionLoader *loader, + const void *key, + UINT32 key_size, + IDWriteFontCollection **collection); + + HRESULT (STDMETHODCALLTYPE *RegisterFontCollectionLoader)( + IDWriteFactory6 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontCollectionLoader)( + IDWriteFactory6 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReference)( + IDWriteFactory6 *This, + const WCHAR *path, + const FILETIME *writetime, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontFileReference)( + IDWriteFactory6 *This, + const void *reference_key, + UINT32 key_size, + IDWriteFontFileLoader *loader, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFactory6 *This, + DWRITE_FONT_FACE_TYPE facetype, + UINT32 files_number, + IDWriteFontFile *const *font_files, + UINT32 index, + DWRITE_FONT_SIMULATIONS sim_flags, + IDWriteFontFace **font_face); + + HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)( + IDWriteFactory6 *This, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateMonitorRenderingParams)( + IDWriteFactory6 *This, + HMONITOR monitor, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)( + IDWriteFactory6 *This, + FLOAT gamma, + FLOAT enhancedContrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *RegisterFontFileLoader)( + IDWriteFactory6 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontFileLoader)( + IDWriteFactory6 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateTextFormat)( + IDWriteFactory6 *This, + const WCHAR *family_name, + IDWriteFontCollection *collection, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, + FLOAT size, + const WCHAR *locale, + IDWriteTextFormat **format); + + HRESULT (STDMETHODCALLTYPE *CreateTypography)( + IDWriteFactory6 *This, + IDWriteTypography **typography); + + HRESULT (STDMETHODCALLTYPE *GetGdiInterop)( + IDWriteFactory6 *This, + IDWriteGdiInterop **gdi_interop); + + HRESULT (STDMETHODCALLTYPE *CreateTextLayout)( + IDWriteFactory6 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT max_width, + FLOAT max_height, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateGdiCompatibleTextLayout)( + IDWriteFactory6 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT layout_width, + FLOAT layout_height, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateEllipsisTrimmingSign)( + IDWriteFactory6 *This, + IDWriteTextFormat *format, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *CreateTextAnalyzer)( + IDWriteFactory6 *This, + IDWriteTextAnalyzer **analyzer); + + HRESULT (STDMETHODCALLTYPE *CreateNumberSubstitution)( + IDWriteFactory6 *This, + DWRITE_NUMBER_SUBSTITUTION_METHOD method, + const WCHAR *locale, + WINBOOL ignore_user_override, + IDWriteNumberSubstitution **substitution); + + HRESULT (STDMETHODCALLTYPE *CreateGlyphRunAnalysis)( + IDWriteFactory6 *This, + const DWRITE_GLYPH_RUN *glyph_run, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + FLOAT baseline_x, + FLOAT baseline_y, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetEudcFontCollection)( + IDWriteFactory6 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory1_CreateCustomRenderingParams)( + IDWriteFactory6 *This, + FLOAT gamma, + FLOAT enhcontrast, + FLOAT enhcontrast_grayscale, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams1 **params); + + /*** IDWriteFactory2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontFallback)( + IDWriteFactory6 *This, + IDWriteFontFallback **fallback); + + HRESULT (STDMETHODCALLTYPE *CreateFontFallbackBuilder)( + IDWriteFactory6 *This, + IDWriteFontFallbackBuilder **fallbackbuilder); + + HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)( + IDWriteFactory6 *This, + FLOAT originX, + FLOAT originY, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, + DWRITE_MEASURING_MODE mode, + const DWRITE_MATRIX *transform, + UINT32 palette_index, + IDWriteColorGlyphRunEnumerator **colorlayers); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateCustomRenderingParams)( + IDWriteFactory6 *This, + FLOAT gamma, + FLOAT contrast, + FLOAT grayscalecontrast, + FLOAT cleartypeLevel, + DWRITE_PIXEL_GEOMETRY pixelGeometry, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_GRID_FIT_MODE gridFitMode, + IDWriteRenderingParams2 **params); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateGlyphRunAnalysis)( + IDWriteFactory6 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GRID_FIT_MODE gridFitMode, + DWRITE_TEXT_ANTIALIAS_MODE antialiasMode, + FLOAT originX, + FLOAT originY, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateGlyphRunAnalysis)( + IDWriteFactory6 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateCustomRenderingParams)( + IDWriteFactory6 *This, + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference_)( + IDWriteFactory6 *This, + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFactory6 *This, + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *GetSystemFontSet)( + IDWriteFactory6 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSetBuilder)( + IDWriteFactory6 *This, + IDWriteFontSetBuilder **builder); + + HRESULT (STDMETHODCALLTYPE *CreateFontCollectionFromFontSet)( + IDWriteFactory6 *This, + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_GetSystemFontCollection)( + IDWriteFactory6 *This, + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *GetFontDownloadQueue)( + IDWriteFactory6 *This, + IDWriteFontDownloadQueue **queue); + + /*** IDWriteFactory4 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory4_TranslateColorGlyphRun)( + IDWriteFactory6 *This, + D2D1_POINT_2F baseline_origin, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc, + DWRITE_GLYPH_IMAGE_FORMATS desired_formats, + DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_MATRIX *transform, + UINT32 palette, + IDWriteColorGlyphRunEnumerator1 **layers); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins_)( + IDWriteFactory6 *This, + const DWRITE_GLYPH_RUN *run, + D2D1_POINT_2F baseline_origin, + D2D1_POINT_2F *origins); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins)( + IDWriteFactory6 *This, + const DWRITE_GLYPH_RUN *run, + DWRITE_MEASURING_MODE measuring_mode, + D2D1_POINT_2F baseline_origin, + const DWRITE_MATRIX *transform, + D2D1_POINT_2F *origins); + + /*** IDWriteFactory5 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory5_CreateFontSetBuilder)( + IDWriteFactory6 *This, + IDWriteFontSetBuilder1 **fontset_builder); + + HRESULT (STDMETHODCALLTYPE *CreateInMemoryFontFileLoader)( + IDWriteFactory6 *This, + IDWriteInMemoryFontFileLoader **loader); + + HRESULT (STDMETHODCALLTYPE *CreateHttpFontFileLoader)( + IDWriteFactory6 *This, + const WCHAR *referrer_url, + const WCHAR *extra_headers, + IDWriteRemoteFontFileLoader **loader); + + DWRITE_CONTAINER_TYPE (STDMETHODCALLTYPE *AnalyzeContainerType)( + IDWriteFactory6 *This, + const void *data, + UINT32 data_size); + + HRESULT (STDMETHODCALLTYPE *UnpackFontFile)( + IDWriteFactory6 *This, + DWRITE_CONTAINER_TYPE container_type, + const void *data, + UINT32 data_size, + IDWriteFontFileStream **stream); + + /*** IDWriteFactory6 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontFaceReference)( + IDWriteFactory6 *This, + IDWriteFontFile *file, + UINT32 face_index, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + IDWriteFontFaceReference1 **face_ref); + + HRESULT (STDMETHODCALLTYPE *CreateFontResource)( + IDWriteFactory6 *This, + IDWriteFontFile *file, + UINT32 face_index, + IDWriteFontResource **resource); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_GetSystemFontSet)( + IDWriteFactory6 *This, + WINBOOL include_downloadable, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_GetSystemFontCollection)( + IDWriteFactory6 *This, + WINBOOL include_downloadable, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontCollectionFromFontSet)( + IDWriteFactory6 *This, + IDWriteFontSet *fontset, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontSetBuilder)( + IDWriteFactory6 *This, + IDWriteFontSetBuilder2 **builder); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateTextFormat)( + IDWriteFactory6 *This, + const WCHAR *familyname, + IDWriteFontCollection *collection, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + FLOAT fontsize, + const WCHAR *localename, + IDWriteTextFormat3 **format); + + END_INTERFACE +} IDWriteFactory6Vtbl; + +interface IDWriteFactory6 { + CONST_VTBL IDWriteFactory6Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFactory6_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFactory6_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFactory6_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFactory methods ***/ +#define IDWriteFactory6_CreateCustomFontCollection(This,loader,key,key_size,collection) (This)->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection) +#define IDWriteFactory6_RegisterFontCollectionLoader(This,loader) (This)->lpVtbl->RegisterFontCollectionLoader(This,loader) +#define IDWriteFactory6_UnregisterFontCollectionLoader(This,loader) (This)->lpVtbl->UnregisterFontCollectionLoader(This,loader) +#define IDWriteFactory6_CreateFontFileReference(This,path,writetime,font_file) (This)->lpVtbl->CreateFontFileReference(This,path,writetime,font_file) +#define IDWriteFactory6_CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) (This)->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) +#define IDWriteFactory6_CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) (This)->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) +#define IDWriteFactory6_CreateRenderingParams(This,params) (This)->lpVtbl->CreateRenderingParams(This,params) +#define IDWriteFactory6_CreateMonitorRenderingParams(This,monitor,params) (This)->lpVtbl->CreateMonitorRenderingParams(This,monitor,params) +#define IDWriteFactory6_RegisterFontFileLoader(This,loader) (This)->lpVtbl->RegisterFontFileLoader(This,loader) +#define IDWriteFactory6_UnregisterFontFileLoader(This,loader) (This)->lpVtbl->UnregisterFontFileLoader(This,loader) +#define IDWriteFactory6_CreateTypography(This,typography) (This)->lpVtbl->CreateTypography(This,typography) +#define IDWriteFactory6_GetGdiInterop(This,gdi_interop) (This)->lpVtbl->GetGdiInterop(This,gdi_interop) +#define IDWriteFactory6_CreateTextLayout(This,string,len,format,max_width,max_height,layout) (This)->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout) +#define IDWriteFactory6_CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) (This)->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) +#define IDWriteFactory6_CreateEllipsisTrimmingSign(This,format,trimming_sign) (This)->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign) +#define IDWriteFactory6_CreateTextAnalyzer(This,analyzer) (This)->lpVtbl->CreateTextAnalyzer(This,analyzer) +#define IDWriteFactory6_CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) (This)->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) +/*** IDWriteFactory1 methods ***/ +#define IDWriteFactory6_GetEudcFontCollection(This,collection,check_for_updates) (This)->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates) +/*** IDWriteFactory2 methods ***/ +#define IDWriteFactory6_GetSystemFontFallback(This,fallback) (This)->lpVtbl->GetSystemFontFallback(This,fallback) +#define IDWriteFactory6_CreateFontFallbackBuilder(This,fallbackbuilder) (This)->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder) +/*** IDWriteFactory3 methods ***/ +#define IDWriteFactory6_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) (This)->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) +#define IDWriteFactory6_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) (This)->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) +#define IDWriteFactory6_CreateFontFaceReference_(This,file,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference) +#define IDWriteFactory6_GetFontDownloadQueue(This,queue) (This)->lpVtbl->GetFontDownloadQueue(This,queue) +/*** IDWriteFactory4 methods ***/ +#define IDWriteFactory6_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) (This)->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) +#define IDWriteFactory6_ComputeGlyphOrigins_(This,run,baseline_origin,origins) (This)->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins) +#define IDWriteFactory6_ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) (This)->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) +/*** IDWriteFactory5 methods ***/ +#define IDWriteFactory6_CreateInMemoryFontFileLoader(This,loader) (This)->lpVtbl->CreateInMemoryFontFileLoader(This,loader) +#define IDWriteFactory6_CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) (This)->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) +#define IDWriteFactory6_AnalyzeContainerType(This,data,data_size) (This)->lpVtbl->AnalyzeContainerType(This,data,data_size) +#define IDWriteFactory6_UnpackFontFile(This,container_type,data,data_size,stream) (This)->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream) +/*** IDWriteFactory6 methods ***/ +#define IDWriteFactory6_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref) (This)->lpVtbl->IDWriteFactory6_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref) +#define IDWriteFactory6_CreateFontResource(This,file,face_index,resource) (This)->lpVtbl->CreateFontResource(This,file,face_index,resource) +#define IDWriteFactory6_GetSystemFontSet(This,include_downloadable,fontset) (This)->lpVtbl->IDWriteFactory6_GetSystemFontSet(This,include_downloadable,fontset) +#define IDWriteFactory6_GetSystemFontCollection(This,include_downloadable,family_model,collection) (This)->lpVtbl->IDWriteFactory6_GetSystemFontCollection(This,include_downloadable,family_model,collection) +#define IDWriteFactory6_CreateFontCollectionFromFontSet(This,fontset,family_model,collection) (This)->lpVtbl->IDWriteFactory6_CreateFontCollectionFromFontSet(This,fontset,family_model,collection) +#define IDWriteFactory6_CreateFontSetBuilder(This,builder) (This)->lpVtbl->IDWriteFactory6_CreateFontSetBuilder(This,builder) +#define IDWriteFactory6_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format) (This)->lpVtbl->IDWriteFactory6_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_QueryInterface(IDWriteFactory6* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFactory6_AddRef(IDWriteFactory6* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFactory6_Release(IDWriteFactory6* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFactory methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_CreateCustomFontCollection(IDWriteFactory6* This,IDWriteFontCollectionLoader *loader,const void *key,UINT32 key_size,IDWriteFontCollection **collection) { + return This->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection); +} +static FORCEINLINE HRESULT IDWriteFactory6_RegisterFontCollectionLoader(IDWriteFactory6* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->RegisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory6_UnregisterFontCollectionLoader(IDWriteFactory6* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->UnregisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontFileReference(IDWriteFactory6* This,const WCHAR *path,const FILETIME *writetime,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateFontFileReference(This,path,writetime,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateCustomFontFileReference(IDWriteFactory6* This,const void *reference_key,UINT32 key_size,IDWriteFontFileLoader *loader,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontFace(IDWriteFactory6* This,DWRITE_FONT_FACE_TYPE facetype,UINT32 files_number,IDWriteFontFile *const *font_files,UINT32 index,DWRITE_FONT_SIMULATIONS sim_flags,IDWriteFontFace **font_face) { + return This->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateRenderingParams(IDWriteFactory6* This,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateRenderingParams(This,params); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateMonitorRenderingParams(IDWriteFactory6* This,HMONITOR monitor,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateMonitorRenderingParams(This,monitor,params); +} +static FORCEINLINE HRESULT IDWriteFactory6_RegisterFontFileLoader(IDWriteFactory6* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->RegisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory6_UnregisterFontFileLoader(IDWriteFactory6* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->UnregisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateTypography(IDWriteFactory6* This,IDWriteTypography **typography) { + return This->lpVtbl->CreateTypography(This,typography); +} +static FORCEINLINE HRESULT IDWriteFactory6_GetGdiInterop(IDWriteFactory6* This,IDWriteGdiInterop **gdi_interop) { + return This->lpVtbl->GetGdiInterop(This,gdi_interop); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateTextLayout(IDWriteFactory6* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT max_width,FLOAT max_height,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateGdiCompatibleTextLayout(IDWriteFactory6* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT layout_width,FLOAT layout_height,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateEllipsisTrimmingSign(IDWriteFactory6* This,IDWriteTextFormat *format,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateTextAnalyzer(IDWriteFactory6* This,IDWriteTextAnalyzer **analyzer) { + return This->lpVtbl->CreateTextAnalyzer(This,analyzer); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateNumberSubstitution(IDWriteFactory6* This,DWRITE_NUMBER_SUBSTITUTION_METHOD method,const WCHAR *locale,WINBOOL ignore_user_override,IDWriteNumberSubstitution **substitution) { + return This->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution); +} +/*** IDWriteFactory1 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_GetEudcFontCollection(IDWriteFactory6* This,IDWriteFontCollection **collection,WINBOOL check_for_updates) { + return This->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates); +} +/*** IDWriteFactory2 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_GetSystemFontFallback(IDWriteFactory6* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetSystemFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontFallbackBuilder(IDWriteFactory6* This,IDWriteFontFallbackBuilder **fallbackbuilder) { + return This->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder); +} +/*** IDWriteFactory3 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_CreateGlyphRunAnalysis(IDWriteFactory6* This,const DWRITE_GLYPH_RUN *run,const DWRITE_MATRIX *transform,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_MEASURING_MODE measuring_mode,DWRITE_GRID_FIT_MODE gridfit_mode,DWRITE_TEXT_ANTIALIAS_MODE antialias_mode,FLOAT origin_x,FLOAT origin_y,IDWriteGlyphRunAnalysis **analysis) { + return This->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateCustomRenderingParams(IDWriteFactory6* This,FLOAT gamma,FLOAT enhanced_contrast,FLOAT grayscale_enhanced_contrast,FLOAT cleartype_level,DWRITE_PIXEL_GEOMETRY pixel_geometry,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_GRID_FIT_MODE gridfit_mode,IDWriteRenderingParams3 **params) { + return This->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontFaceReference_(IDWriteFactory6* This,IDWriteFontFile *file,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory6_GetFontDownloadQueue(IDWriteFactory6* This,IDWriteFontDownloadQueue **queue) { + return This->lpVtbl->GetFontDownloadQueue(This,queue); +} +/*** IDWriteFactory4 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_TranslateColorGlyphRun(IDWriteFactory6* This,D2D1_POINT_2F baseline_origin,const DWRITE_GLYPH_RUN *run,const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc,DWRITE_GLYPH_IMAGE_FORMATS desired_formats,DWRITE_MEASURING_MODE measuring_mode,const DWRITE_MATRIX *transform,UINT32 palette,IDWriteColorGlyphRunEnumerator1 **layers) { + return This->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers); +} +static FORCEINLINE HRESULT IDWriteFactory6_ComputeGlyphOrigins_(IDWriteFactory6* This,const DWRITE_GLYPH_RUN *run,D2D1_POINT_2F baseline_origin,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins); +} +static FORCEINLINE HRESULT IDWriteFactory6_ComputeGlyphOrigins(IDWriteFactory6* This,const DWRITE_GLYPH_RUN *run,DWRITE_MEASURING_MODE measuring_mode,D2D1_POINT_2F baseline_origin,const DWRITE_MATRIX *transform,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins); +} +/*** IDWriteFactory5 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_CreateInMemoryFontFileLoader(IDWriteFactory6* This,IDWriteInMemoryFontFileLoader **loader) { + return This->lpVtbl->CreateInMemoryFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateHttpFontFileLoader(IDWriteFactory6* This,const WCHAR *referrer_url,const WCHAR *extra_headers,IDWriteRemoteFontFileLoader **loader) { + return This->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader); +} +static FORCEINLINE DWRITE_CONTAINER_TYPE IDWriteFactory6_AnalyzeContainerType(IDWriteFactory6* This,const void *data,UINT32 data_size) { + return This->lpVtbl->AnalyzeContainerType(This,data,data_size); +} +static FORCEINLINE HRESULT IDWriteFactory6_UnpackFontFile(IDWriteFactory6* This,DWRITE_CONTAINER_TYPE container_type,const void *data,UINT32 data_size,IDWriteFontFileStream **stream) { + return This->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream); +} +/*** IDWriteFactory6 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontFaceReference(IDWriteFactory6* This,IDWriteFontFile *file,UINT32 face_index,DWRITE_FONT_SIMULATIONS simulations,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_axis,IDWriteFontFaceReference1 **face_ref) { + return This->lpVtbl->IDWriteFactory6_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontResource(IDWriteFactory6* This,IDWriteFontFile *file,UINT32 face_index,IDWriteFontResource **resource) { + return This->lpVtbl->CreateFontResource(This,file,face_index,resource); +} +static FORCEINLINE HRESULT IDWriteFactory6_GetSystemFontSet(IDWriteFactory6* This,WINBOOL include_downloadable,IDWriteFontSet1 **fontset) { + return This->lpVtbl->IDWriteFactory6_GetSystemFontSet(This,include_downloadable,fontset); +} +static FORCEINLINE HRESULT IDWriteFactory6_GetSystemFontCollection(IDWriteFactory6* This,WINBOOL include_downloadable,DWRITE_FONT_FAMILY_MODEL family_model,IDWriteFontCollection2 **collection) { + return This->lpVtbl->IDWriteFactory6_GetSystemFontCollection(This,include_downloadable,family_model,collection); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontCollectionFromFontSet(IDWriteFactory6* This,IDWriteFontSet *fontset,DWRITE_FONT_FAMILY_MODEL family_model,IDWriteFontCollection2 **collection) { + return This->lpVtbl->IDWriteFactory6_CreateFontCollectionFromFontSet(This,fontset,family_model,collection); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateFontSetBuilder(IDWriteFactory6* This,IDWriteFontSetBuilder2 **builder) { + return This->lpVtbl->IDWriteFactory6_CreateFontSetBuilder(This,builder); +} +static FORCEINLINE HRESULT IDWriteFactory6_CreateTextFormat(IDWriteFactory6* This,const WCHAR *familyname,IDWriteFontCollection *collection,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_axis,FLOAT fontsize,const WCHAR *localename,IDWriteTextFormat3 **format) { + return This->lpVtbl->IDWriteFactory6_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFactory6_INTERFACE_DEFINED__ */ + +/***************************************************************************** + * IDWriteFactory7 interface + */ +#ifndef __IDWriteFactory7_INTERFACE_DEFINED__ +#define __IDWriteFactory7_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_IDWriteFactory7, 0x35d0e0b3, 0x9076, 0x4d2e, 0xa0,0x16, 0xa9,0x1b,0x56,0x8a,0x06,0xb4); +#if defined(__cplusplus) && !defined(CINTERFACE) +MIDL_INTERFACE("35d0e0b3-9076-4d2e-a016-a91b568a06b4") +IDWriteFactory7 : public IDWriteFactory6 +{ + virtual HRESULT STDMETHODCALLTYPE GetSystemFontSet( + WINBOOL include_downloadable, + IDWriteFontSet2 **fontset) = 0; + + using IDWriteFactory6::GetSystemFontSet; + + virtual HRESULT STDMETHODCALLTYPE GetSystemFontCollection( + WINBOOL include_downloadable, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection3 **collection) = 0; + + using IDWriteFactory6::GetSystemFontCollection; + +}; +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDWriteFactory7, 0x35d0e0b3, 0x9076, 0x4d2e, 0xa0,0x16, 0xa9,0x1b,0x56,0x8a,0x06,0xb4) +#endif +#else +typedef struct IDWriteFactory7Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IDWriteFactory7 *This, + REFIID riid, + void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IDWriteFactory7 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IDWriteFactory7 *This); + + /*** IDWriteFactory methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontCollection)( + IDWriteFactory7 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontCollection)( + IDWriteFactory7 *This, + IDWriteFontCollectionLoader *loader, + const void *key, + UINT32 key_size, + IDWriteFontCollection **collection); + + HRESULT (STDMETHODCALLTYPE *RegisterFontCollectionLoader)( + IDWriteFactory7 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontCollectionLoader)( + IDWriteFactory7 *This, + IDWriteFontCollectionLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateFontFileReference)( + IDWriteFactory7 *This, + const WCHAR *path, + const FILETIME *writetime, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateCustomFontFileReference)( + IDWriteFactory7 *This, + const void *reference_key, + UINT32 key_size, + IDWriteFontFileLoader *loader, + IDWriteFontFile **font_file); + + HRESULT (STDMETHODCALLTYPE *CreateFontFace)( + IDWriteFactory7 *This, + DWRITE_FONT_FACE_TYPE facetype, + UINT32 files_number, + IDWriteFontFile *const *font_files, + UINT32 index, + DWRITE_FONT_SIMULATIONS sim_flags, + IDWriteFontFace **font_face); + + HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)( + IDWriteFactory7 *This, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateMonitorRenderingParams)( + IDWriteFactory7 *This, + HMONITOR monitor, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)( + IDWriteFactory7 *This, + FLOAT gamma, + FLOAT enhancedContrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams **params); + + HRESULT (STDMETHODCALLTYPE *RegisterFontFileLoader)( + IDWriteFactory7 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *UnregisterFontFileLoader)( + IDWriteFactory7 *This, + IDWriteFontFileLoader *loader); + + HRESULT (STDMETHODCALLTYPE *CreateTextFormat)( + IDWriteFactory7 *This, + const WCHAR *family_name, + IDWriteFontCollection *collection, + DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STYLE style, + DWRITE_FONT_STRETCH stretch, + FLOAT size, + const WCHAR *locale, + IDWriteTextFormat **format); + + HRESULT (STDMETHODCALLTYPE *CreateTypography)( + IDWriteFactory7 *This, + IDWriteTypography **typography); + + HRESULT (STDMETHODCALLTYPE *GetGdiInterop)( + IDWriteFactory7 *This, + IDWriteGdiInterop **gdi_interop); + + HRESULT (STDMETHODCALLTYPE *CreateTextLayout)( + IDWriteFactory7 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT max_width, + FLOAT max_height, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateGdiCompatibleTextLayout)( + IDWriteFactory7 *This, + const WCHAR *string, + UINT32 len, + IDWriteTextFormat *format, + FLOAT layout_width, + FLOAT layout_height, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + WINBOOL use_gdi_natural, + IDWriteTextLayout **layout); + + HRESULT (STDMETHODCALLTYPE *CreateEllipsisTrimmingSign)( + IDWriteFactory7 *This, + IDWriteTextFormat *format, + IDWriteInlineObject **trimming_sign); + + HRESULT (STDMETHODCALLTYPE *CreateTextAnalyzer)( + IDWriteFactory7 *This, + IDWriteTextAnalyzer **analyzer); + + HRESULT (STDMETHODCALLTYPE *CreateNumberSubstitution)( + IDWriteFactory7 *This, + DWRITE_NUMBER_SUBSTITUTION_METHOD method, + const WCHAR *locale, + WINBOOL ignore_user_override, + IDWriteNumberSubstitution **substitution); + + HRESULT (STDMETHODCALLTYPE *CreateGlyphRunAnalysis)( + IDWriteFactory7 *This, + const DWRITE_GLYPH_RUN *glyph_run, + FLOAT pixels_per_dip, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + FLOAT baseline_x, + FLOAT baseline_y, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory1 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetEudcFontCollection)( + IDWriteFactory7 *This, + IDWriteFontCollection **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory1_CreateCustomRenderingParams)( + IDWriteFactory7 *This, + FLOAT gamma, + FLOAT enhcontrast, + FLOAT enhcontrast_grayscale, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY geometry, + DWRITE_RENDERING_MODE mode, + IDWriteRenderingParams1 **params); + + /*** IDWriteFactory2 methods ***/ + HRESULT (STDMETHODCALLTYPE *GetSystemFontFallback)( + IDWriteFactory7 *This, + IDWriteFontFallback **fallback); + + HRESULT (STDMETHODCALLTYPE *CreateFontFallbackBuilder)( + IDWriteFactory7 *This, + IDWriteFontFallbackBuilder **fallbackbuilder); + + HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)( + IDWriteFactory7 *This, + FLOAT originX, + FLOAT originY, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr, + DWRITE_MEASURING_MODE mode, + const DWRITE_MATRIX *transform, + UINT32 palette_index, + IDWriteColorGlyphRunEnumerator **colorlayers); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateCustomRenderingParams)( + IDWriteFactory7 *This, + FLOAT gamma, + FLOAT contrast, + FLOAT grayscalecontrast, + FLOAT cleartypeLevel, + DWRITE_PIXEL_GEOMETRY pixelGeometry, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_GRID_FIT_MODE gridFitMode, + IDWriteRenderingParams2 **params); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory2_CreateGlyphRunAnalysis)( + IDWriteFactory7 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE renderingMode, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GRID_FIT_MODE gridFitMode, + DWRITE_TEXT_ANTIALIAS_MODE antialiasMode, + FLOAT originX, + FLOAT originY, + IDWriteGlyphRunAnalysis **analysis); + + /*** IDWriteFactory3 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateGlyphRunAnalysis)( + IDWriteFactory7 *This, + const DWRITE_GLYPH_RUN *run, + const DWRITE_MATRIX *transform, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_MEASURING_MODE measuring_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + DWRITE_TEXT_ANTIALIAS_MODE antialias_mode, + FLOAT origin_x, + FLOAT origin_y, + IDWriteGlyphRunAnalysis **analysis); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_CreateCustomRenderingParams)( + IDWriteFactory7 *This, + FLOAT gamma, + FLOAT enhanced_contrast, + FLOAT grayscale_enhanced_contrast, + FLOAT cleartype_level, + DWRITE_PIXEL_GEOMETRY pixel_geometry, + DWRITE_RENDERING_MODE1 rendering_mode, + DWRITE_GRID_FIT_MODE gridfit_mode, + IDWriteRenderingParams3 **params); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference_)( + IDWriteFactory7 *This, + IDWriteFontFile *file, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *CreateFontFaceReference)( + IDWriteFactory7 *This, + const WCHAR *path, + const FILETIME *writetime, + UINT32 index, + DWRITE_FONT_SIMULATIONS simulations, + IDWriteFontFaceReference **reference); + + HRESULT (STDMETHODCALLTYPE *GetSystemFontSet)( + IDWriteFactory7 *This, + IDWriteFontSet **fontset); + + HRESULT (STDMETHODCALLTYPE *CreateFontSetBuilder)( + IDWriteFactory7 *This, + IDWriteFontSetBuilder **builder); + + HRESULT (STDMETHODCALLTYPE *CreateFontCollectionFromFontSet)( + IDWriteFactory7 *This, + IDWriteFontSet *fontset, + IDWriteFontCollection1 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory3_GetSystemFontCollection)( + IDWriteFactory7 *This, + WINBOOL include_downloadable, + IDWriteFontCollection1 **collection, + WINBOOL check_for_updates); + + HRESULT (STDMETHODCALLTYPE *GetFontDownloadQueue)( + IDWriteFactory7 *This, + IDWriteFontDownloadQueue **queue); + + /*** IDWriteFactory4 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory4_TranslateColorGlyphRun)( + IDWriteFactory7 *This, + D2D1_POINT_2F baseline_origin, + const DWRITE_GLYPH_RUN *run, + const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc, + DWRITE_GLYPH_IMAGE_FORMATS desired_formats, + DWRITE_MEASURING_MODE measuring_mode, + const DWRITE_MATRIX *transform, + UINT32 palette, + IDWriteColorGlyphRunEnumerator1 **layers); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins_)( + IDWriteFactory7 *This, + const DWRITE_GLYPH_RUN *run, + D2D1_POINT_2F baseline_origin, + D2D1_POINT_2F *origins); + + HRESULT (STDMETHODCALLTYPE *ComputeGlyphOrigins)( + IDWriteFactory7 *This, + const DWRITE_GLYPH_RUN *run, + DWRITE_MEASURING_MODE measuring_mode, + D2D1_POINT_2F baseline_origin, + const DWRITE_MATRIX *transform, + D2D1_POINT_2F *origins); + + /*** IDWriteFactory5 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory5_CreateFontSetBuilder)( + IDWriteFactory7 *This, + IDWriteFontSetBuilder1 **fontset_builder); + + HRESULT (STDMETHODCALLTYPE *CreateInMemoryFontFileLoader)( + IDWriteFactory7 *This, + IDWriteInMemoryFontFileLoader **loader); + + HRESULT (STDMETHODCALLTYPE *CreateHttpFontFileLoader)( + IDWriteFactory7 *This, + const WCHAR *referrer_url, + const WCHAR *extra_headers, + IDWriteRemoteFontFileLoader **loader); + + DWRITE_CONTAINER_TYPE (STDMETHODCALLTYPE *AnalyzeContainerType)( + IDWriteFactory7 *This, + const void *data, + UINT32 data_size); + + HRESULT (STDMETHODCALLTYPE *UnpackFontFile)( + IDWriteFactory7 *This, + DWRITE_CONTAINER_TYPE container_type, + const void *data, + UINT32 data_size, + IDWriteFontFileStream **stream); + + /*** IDWriteFactory6 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontFaceReference)( + IDWriteFactory7 *This, + IDWriteFontFile *file, + UINT32 face_index, + DWRITE_FONT_SIMULATIONS simulations, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + IDWriteFontFaceReference1 **face_ref); + + HRESULT (STDMETHODCALLTYPE *CreateFontResource)( + IDWriteFactory7 *This, + IDWriteFontFile *file, + UINT32 face_index, + IDWriteFontResource **resource); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_GetSystemFontSet)( + IDWriteFactory7 *This, + WINBOOL include_downloadable, + IDWriteFontSet1 **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_GetSystemFontCollection)( + IDWriteFactory7 *This, + WINBOOL include_downloadable, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontCollectionFromFontSet)( + IDWriteFactory7 *This, + IDWriteFontSet *fontset, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection2 **collection); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateFontSetBuilder)( + IDWriteFactory7 *This, + IDWriteFontSetBuilder2 **builder); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory6_CreateTextFormat)( + IDWriteFactory7 *This, + const WCHAR *familyname, + IDWriteFontCollection *collection, + const DWRITE_FONT_AXIS_VALUE *axis_values, + UINT32 num_axis, + FLOAT fontsize, + const WCHAR *localename, + IDWriteTextFormat3 **format); + + /*** IDWriteFactory7 methods ***/ + HRESULT (STDMETHODCALLTYPE *IDWriteFactory7_GetSystemFontSet)( + IDWriteFactory7 *This, + WINBOOL include_downloadable, + IDWriteFontSet2 **fontset); + + HRESULT (STDMETHODCALLTYPE *IDWriteFactory7_GetSystemFontCollection)( + IDWriteFactory7 *This, + WINBOOL include_downloadable, + DWRITE_FONT_FAMILY_MODEL family_model, + IDWriteFontCollection3 **collection); + + END_INTERFACE +} IDWriteFactory7Vtbl; + +interface IDWriteFactory7 { + CONST_VTBL IDWriteFactory7Vtbl* lpVtbl; +}; + +#ifdef COBJMACROS +#ifndef WIDL_C_INLINE_WRAPPERS +/*** IUnknown methods ***/ +#define IDWriteFactory7_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) +#define IDWriteFactory7_AddRef(This) (This)->lpVtbl->AddRef(This) +#define IDWriteFactory7_Release(This) (This)->lpVtbl->Release(This) +/*** IDWriteFactory methods ***/ +#define IDWriteFactory7_CreateCustomFontCollection(This,loader,key,key_size,collection) (This)->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection) +#define IDWriteFactory7_RegisterFontCollectionLoader(This,loader) (This)->lpVtbl->RegisterFontCollectionLoader(This,loader) +#define IDWriteFactory7_UnregisterFontCollectionLoader(This,loader) (This)->lpVtbl->UnregisterFontCollectionLoader(This,loader) +#define IDWriteFactory7_CreateFontFileReference(This,path,writetime,font_file) (This)->lpVtbl->CreateFontFileReference(This,path,writetime,font_file) +#define IDWriteFactory7_CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) (This)->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file) +#define IDWriteFactory7_CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) (This)->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face) +#define IDWriteFactory7_CreateRenderingParams(This,params) (This)->lpVtbl->CreateRenderingParams(This,params) +#define IDWriteFactory7_CreateMonitorRenderingParams(This,monitor,params) (This)->lpVtbl->CreateMonitorRenderingParams(This,monitor,params) +#define IDWriteFactory7_RegisterFontFileLoader(This,loader) (This)->lpVtbl->RegisterFontFileLoader(This,loader) +#define IDWriteFactory7_UnregisterFontFileLoader(This,loader) (This)->lpVtbl->UnregisterFontFileLoader(This,loader) +#define IDWriteFactory7_CreateTypography(This,typography) (This)->lpVtbl->CreateTypography(This,typography) +#define IDWriteFactory7_GetGdiInterop(This,gdi_interop) (This)->lpVtbl->GetGdiInterop(This,gdi_interop) +#define IDWriteFactory7_CreateTextLayout(This,string,len,format,max_width,max_height,layout) (This)->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout) +#define IDWriteFactory7_CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) (This)->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout) +#define IDWriteFactory7_CreateEllipsisTrimmingSign(This,format,trimming_sign) (This)->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign) +#define IDWriteFactory7_CreateTextAnalyzer(This,analyzer) (This)->lpVtbl->CreateTextAnalyzer(This,analyzer) +#define IDWriteFactory7_CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) (This)->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution) +/*** IDWriteFactory1 methods ***/ +#define IDWriteFactory7_GetEudcFontCollection(This,collection,check_for_updates) (This)->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates) +/*** IDWriteFactory2 methods ***/ +#define IDWriteFactory7_GetSystemFontFallback(This,fallback) (This)->lpVtbl->GetSystemFontFallback(This,fallback) +#define IDWriteFactory7_CreateFontFallbackBuilder(This,fallbackbuilder) (This)->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder) +/*** IDWriteFactory3 methods ***/ +#define IDWriteFactory7_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) (This)->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis) +#define IDWriteFactory7_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) (This)->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params) +#define IDWriteFactory7_CreateFontFaceReference_(This,file,index,simulations,reference) (This)->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference) +#define IDWriteFactory7_GetFontDownloadQueue(This,queue) (This)->lpVtbl->GetFontDownloadQueue(This,queue) +/*** IDWriteFactory4 methods ***/ +#define IDWriteFactory7_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) (This)->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers) +#define IDWriteFactory7_ComputeGlyphOrigins_(This,run,baseline_origin,origins) (This)->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins) +#define IDWriteFactory7_ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) (This)->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins) +/*** IDWriteFactory5 methods ***/ +#define IDWriteFactory7_CreateInMemoryFontFileLoader(This,loader) (This)->lpVtbl->CreateInMemoryFontFileLoader(This,loader) +#define IDWriteFactory7_CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) (This)->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader) +#define IDWriteFactory7_AnalyzeContainerType(This,data,data_size) (This)->lpVtbl->AnalyzeContainerType(This,data,data_size) +#define IDWriteFactory7_UnpackFontFile(This,container_type,data,data_size,stream) (This)->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream) +/*** IDWriteFactory6 methods ***/ +#define IDWriteFactory7_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref) (This)->lpVtbl->IDWriteFactory6_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref) +#define IDWriteFactory7_CreateFontResource(This,file,face_index,resource) (This)->lpVtbl->CreateFontResource(This,file,face_index,resource) +#define IDWriteFactory7_CreateFontCollectionFromFontSet(This,fontset,family_model,collection) (This)->lpVtbl->IDWriteFactory6_CreateFontCollectionFromFontSet(This,fontset,family_model,collection) +#define IDWriteFactory7_CreateFontSetBuilder(This,builder) (This)->lpVtbl->IDWriteFactory6_CreateFontSetBuilder(This,builder) +#define IDWriteFactory7_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format) (This)->lpVtbl->IDWriteFactory6_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format) +/*** IDWriteFactory7 methods ***/ +#define IDWriteFactory7_GetSystemFontSet(This,include_downloadable,fontset) (This)->lpVtbl->IDWriteFactory7_GetSystemFontSet(This,include_downloadable,fontset) +#define IDWriteFactory7_GetSystemFontCollection(This,include_downloadable,family_model,collection) (This)->lpVtbl->IDWriteFactory7_GetSystemFontCollection(This,include_downloadable,family_model,collection) +#else +/*** IUnknown methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_QueryInterface(IDWriteFactory7* This,REFIID riid,void **ppvObject) { + return This->lpVtbl->QueryInterface(This,riid,ppvObject); +} +static FORCEINLINE ULONG IDWriteFactory7_AddRef(IDWriteFactory7* This) { + return This->lpVtbl->AddRef(This); +} +static FORCEINLINE ULONG IDWriteFactory7_Release(IDWriteFactory7* This) { + return This->lpVtbl->Release(This); +} +/*** IDWriteFactory methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_CreateCustomFontCollection(IDWriteFactory7* This,IDWriteFontCollectionLoader *loader,const void *key,UINT32 key_size,IDWriteFontCollection **collection) { + return This->lpVtbl->CreateCustomFontCollection(This,loader,key,key_size,collection); +} +static FORCEINLINE HRESULT IDWriteFactory7_RegisterFontCollectionLoader(IDWriteFactory7* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->RegisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory7_UnregisterFontCollectionLoader(IDWriteFactory7* This,IDWriteFontCollectionLoader *loader) { + return This->lpVtbl->UnregisterFontCollectionLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontFileReference(IDWriteFactory7* This,const WCHAR *path,const FILETIME *writetime,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateFontFileReference(This,path,writetime,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateCustomFontFileReference(IDWriteFactory7* This,const void *reference_key,UINT32 key_size,IDWriteFontFileLoader *loader,IDWriteFontFile **font_file) { + return This->lpVtbl->CreateCustomFontFileReference(This,reference_key,key_size,loader,font_file); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontFace(IDWriteFactory7* This,DWRITE_FONT_FACE_TYPE facetype,UINT32 files_number,IDWriteFontFile *const *font_files,UINT32 index,DWRITE_FONT_SIMULATIONS sim_flags,IDWriteFontFace **font_face) { + return This->lpVtbl->CreateFontFace(This,facetype,files_number,font_files,index,sim_flags,font_face); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateRenderingParams(IDWriteFactory7* This,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateRenderingParams(This,params); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateMonitorRenderingParams(IDWriteFactory7* This,HMONITOR monitor,IDWriteRenderingParams **params) { + return This->lpVtbl->CreateMonitorRenderingParams(This,monitor,params); +} +static FORCEINLINE HRESULT IDWriteFactory7_RegisterFontFileLoader(IDWriteFactory7* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->RegisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory7_UnregisterFontFileLoader(IDWriteFactory7* This,IDWriteFontFileLoader *loader) { + return This->lpVtbl->UnregisterFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateTypography(IDWriteFactory7* This,IDWriteTypography **typography) { + return This->lpVtbl->CreateTypography(This,typography); +} +static FORCEINLINE HRESULT IDWriteFactory7_GetGdiInterop(IDWriteFactory7* This,IDWriteGdiInterop **gdi_interop) { + return This->lpVtbl->GetGdiInterop(This,gdi_interop); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateTextLayout(IDWriteFactory7* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT max_width,FLOAT max_height,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateTextLayout(This,string,len,format,max_width,max_height,layout); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateGdiCompatibleTextLayout(IDWriteFactory7* This,const WCHAR *string,UINT32 len,IDWriteTextFormat *format,FLOAT layout_width,FLOAT layout_height,FLOAT pixels_per_dip,const DWRITE_MATRIX *transform,WINBOOL use_gdi_natural,IDWriteTextLayout **layout) { + return This->lpVtbl->CreateGdiCompatibleTextLayout(This,string,len,format,layout_width,layout_height,pixels_per_dip,transform,use_gdi_natural,layout); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateEllipsisTrimmingSign(IDWriteFactory7* This,IDWriteTextFormat *format,IDWriteInlineObject **trimming_sign) { + return This->lpVtbl->CreateEllipsisTrimmingSign(This,format,trimming_sign); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateTextAnalyzer(IDWriteFactory7* This,IDWriteTextAnalyzer **analyzer) { + return This->lpVtbl->CreateTextAnalyzer(This,analyzer); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateNumberSubstitution(IDWriteFactory7* This,DWRITE_NUMBER_SUBSTITUTION_METHOD method,const WCHAR *locale,WINBOOL ignore_user_override,IDWriteNumberSubstitution **substitution) { + return This->lpVtbl->CreateNumberSubstitution(This,method,locale,ignore_user_override,substitution); +} +/*** IDWriteFactory1 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_GetEudcFontCollection(IDWriteFactory7* This,IDWriteFontCollection **collection,WINBOOL check_for_updates) { + return This->lpVtbl->GetEudcFontCollection(This,collection,check_for_updates); +} +/*** IDWriteFactory2 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_GetSystemFontFallback(IDWriteFactory7* This,IDWriteFontFallback **fallback) { + return This->lpVtbl->GetSystemFontFallback(This,fallback); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontFallbackBuilder(IDWriteFactory7* This,IDWriteFontFallbackBuilder **fallbackbuilder) { + return This->lpVtbl->CreateFontFallbackBuilder(This,fallbackbuilder); +} +/*** IDWriteFactory3 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_CreateGlyphRunAnalysis(IDWriteFactory7* This,const DWRITE_GLYPH_RUN *run,const DWRITE_MATRIX *transform,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_MEASURING_MODE measuring_mode,DWRITE_GRID_FIT_MODE gridfit_mode,DWRITE_TEXT_ANTIALIAS_MODE antialias_mode,FLOAT origin_x,FLOAT origin_y,IDWriteGlyphRunAnalysis **analysis) { + return This->lpVtbl->IDWriteFactory3_CreateGlyphRunAnalysis(This,run,transform,rendering_mode,measuring_mode,gridfit_mode,antialias_mode,origin_x,origin_y,analysis); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateCustomRenderingParams(IDWriteFactory7* This,FLOAT gamma,FLOAT enhanced_contrast,FLOAT grayscale_enhanced_contrast,FLOAT cleartype_level,DWRITE_PIXEL_GEOMETRY pixel_geometry,DWRITE_RENDERING_MODE1 rendering_mode,DWRITE_GRID_FIT_MODE gridfit_mode,IDWriteRenderingParams3 **params) { + return This->lpVtbl->IDWriteFactory3_CreateCustomRenderingParams(This,gamma,enhanced_contrast,grayscale_enhanced_contrast,cleartype_level,pixel_geometry,rendering_mode,gridfit_mode,params); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontFaceReference_(IDWriteFactory7* This,IDWriteFontFile *file,UINT32 index,DWRITE_FONT_SIMULATIONS simulations,IDWriteFontFaceReference **reference) { + return This->lpVtbl->CreateFontFaceReference_(This,file,index,simulations,reference); +} +static FORCEINLINE HRESULT IDWriteFactory7_GetFontDownloadQueue(IDWriteFactory7* This,IDWriteFontDownloadQueue **queue) { + return This->lpVtbl->GetFontDownloadQueue(This,queue); +} +/*** IDWriteFactory4 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_TranslateColorGlyphRun(IDWriteFactory7* This,D2D1_POINT_2F baseline_origin,const DWRITE_GLYPH_RUN *run,const DWRITE_GLYPH_RUN_DESCRIPTION *run_desc,DWRITE_GLYPH_IMAGE_FORMATS desired_formats,DWRITE_MEASURING_MODE measuring_mode,const DWRITE_MATRIX *transform,UINT32 palette,IDWriteColorGlyphRunEnumerator1 **layers) { + return This->lpVtbl->IDWriteFactory4_TranslateColorGlyphRun(This,baseline_origin,run,run_desc,desired_formats,measuring_mode,transform,palette,layers); +} +static FORCEINLINE HRESULT IDWriteFactory7_ComputeGlyphOrigins_(IDWriteFactory7* This,const DWRITE_GLYPH_RUN *run,D2D1_POINT_2F baseline_origin,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins_(This,run,baseline_origin,origins); +} +static FORCEINLINE HRESULT IDWriteFactory7_ComputeGlyphOrigins(IDWriteFactory7* This,const DWRITE_GLYPH_RUN *run,DWRITE_MEASURING_MODE measuring_mode,D2D1_POINT_2F baseline_origin,const DWRITE_MATRIX *transform,D2D1_POINT_2F *origins) { + return This->lpVtbl->ComputeGlyphOrigins(This,run,measuring_mode,baseline_origin,transform,origins); +} +/*** IDWriteFactory5 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_CreateInMemoryFontFileLoader(IDWriteFactory7* This,IDWriteInMemoryFontFileLoader **loader) { + return This->lpVtbl->CreateInMemoryFontFileLoader(This,loader); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateHttpFontFileLoader(IDWriteFactory7* This,const WCHAR *referrer_url,const WCHAR *extra_headers,IDWriteRemoteFontFileLoader **loader) { + return This->lpVtbl->CreateHttpFontFileLoader(This,referrer_url,extra_headers,loader); +} +static FORCEINLINE DWRITE_CONTAINER_TYPE IDWriteFactory7_AnalyzeContainerType(IDWriteFactory7* This,const void *data,UINT32 data_size) { + return This->lpVtbl->AnalyzeContainerType(This,data,data_size); +} +static FORCEINLINE HRESULT IDWriteFactory7_UnpackFontFile(IDWriteFactory7* This,DWRITE_CONTAINER_TYPE container_type,const void *data,UINT32 data_size,IDWriteFontFileStream **stream) { + return This->lpVtbl->UnpackFontFile(This,container_type,data,data_size,stream); +} +/*** IDWriteFactory6 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontFaceReference(IDWriteFactory7* This,IDWriteFontFile *file,UINT32 face_index,DWRITE_FONT_SIMULATIONS simulations,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_axis,IDWriteFontFaceReference1 **face_ref) { + return This->lpVtbl->IDWriteFactory6_CreateFontFaceReference(This,file,face_index,simulations,axis_values,num_axis,face_ref); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontResource(IDWriteFactory7* This,IDWriteFontFile *file,UINT32 face_index,IDWriteFontResource **resource) { + return This->lpVtbl->CreateFontResource(This,file,face_index,resource); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontCollectionFromFontSet(IDWriteFactory7* This,IDWriteFontSet *fontset,DWRITE_FONT_FAMILY_MODEL family_model,IDWriteFontCollection2 **collection) { + return This->lpVtbl->IDWriteFactory6_CreateFontCollectionFromFontSet(This,fontset,family_model,collection); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateFontSetBuilder(IDWriteFactory7* This,IDWriteFontSetBuilder2 **builder) { + return This->lpVtbl->IDWriteFactory6_CreateFontSetBuilder(This,builder); +} +static FORCEINLINE HRESULT IDWriteFactory7_CreateTextFormat(IDWriteFactory7* This,const WCHAR *familyname,IDWriteFontCollection *collection,const DWRITE_FONT_AXIS_VALUE *axis_values,UINT32 num_axis,FLOAT fontsize,const WCHAR *localename,IDWriteTextFormat3 **format) { + return This->lpVtbl->IDWriteFactory6_CreateTextFormat(This,familyname,collection,axis_values,num_axis,fontsize,localename,format); +} +/*** IDWriteFactory7 methods ***/ +static FORCEINLINE HRESULT IDWriteFactory7_GetSystemFontSet(IDWriteFactory7* This,WINBOOL include_downloadable,IDWriteFontSet2 **fontset) { + return This->lpVtbl->IDWriteFactory7_GetSystemFontSet(This,include_downloadable,fontset); +} +static FORCEINLINE HRESULT IDWriteFactory7_GetSystemFontCollection(IDWriteFactory7* This,WINBOOL include_downloadable,DWRITE_FONT_FAMILY_MODEL family_model,IDWriteFontCollection3 **collection) { + return This->lpVtbl->IDWriteFactory7_GetSystemFontCollection(This,include_downloadable,family_model,collection); +} +#endif +#endif + +#endif + + +#endif /* __IDWriteFactory7_INTERFACE_DEFINED__ */ + +/* Begin additional prototypes for all interfaces */ + + +/* End additional prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif /* __dwrite_3_h__ */ diff --git a/scripts/misc-icons/LICENSE.md b/scripts/misc-icons/LICENSE.md new file mode 100644 index 0000000..da5b887 --- /dev/null +++ b/scripts/misc-icons/LICENSE.md @@ -0,0 +1,30 @@ +# CC0 1.0 Universal + +## Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an “owner”) of an original work of authorship and/or a database (each, a “Work”). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works (“Commons”) that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the “Affirmer”), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights (“Copyright and Related Rights”). Copyright and Related Rights include, but are not limited to, the following: + 1. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + 2. moral rights retained by the original author(s) and/or performer(s); + 3. publicity and privacy rights pertaining to a person’s image or likeness depicted in a Work; + 4. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(i), below; + 5. rights protecting the extraction, dissemination, use and reuse of data in a Work; + 6. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + 7. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “Waiver”). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer’s heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer’s express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer’s express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer’s Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the “License”). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer’s express Statement of Purpose. + +4. Limitations and Disclaimers. + 1. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + 2. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + 3. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person’s Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + 4. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. + +For more information, please see http://creativecommons.org/publicdomain/zero/1.0/. diff --git a/scripts/misc-icons/README.md b/scripts/misc-icons/README.md new file mode 100644 index 0000000..33061e0 --- /dev/null +++ b/scripts/misc-icons/README.md @@ -0,0 +1,14 @@ +# Misc Icons + +Currently these SVG icons are used in the about dialog, the script with the json +to generate the related truetype font. + +The GitHub, Discord and Open Collective SVG comes from the [Simple Icons] +collection. + +All the icons are licensed under the [Creative Common 0 License], +see the included [license] file. + +[license]: LICENSE.md +[Simple Icons]: https://github.com/simple-icons/simple-icons/ +[Creative Common 0 License]: https://creativecommons.org/publicdomain/zero/1.0/ diff --git a/scripts/misc-icons/generate_icons_font.json b/scripts/misc-icons/generate_icons_font.json new file mode 100644 index 0000000..ea09d7a --- /dev/null +++ b/scripts/misc-icons/generate_icons_font.json @@ -0,0 +1,9 @@ +{ + "glyph_map": { + "0xe000": "sfztools", + "0xe001": "github", + "0xe002": "discord", + "0xe003": "opencollective", + "0xe004": "sfzformat" + } +} diff --git a/scripts/misc-icons/generate_icons_font.py b/scripts/misc-icons/generate_icons_font.py new file mode 100755 index 0000000..a17d5a4 --- /dev/null +++ b/scripts/misc-icons/generate_icons_font.py @@ -0,0 +1,54 @@ +#!/usr/bin/fontforge + +import fontforge +import optparse +import json +import os +import sys + +CONFIG_FILE = os.path.splitext(__file__)[0] + '.json' +DATA = json.load(open(CONFIG_FILE)) + +def main(): + parser = optparse.OptionParser() + parser.add_option("-o", dest="output", + help="set the output file", + metavar="output") + parser.add_option("-n", dest="name", + help="set the font name", + metavar="name") + + (options, args) = parser.parse_args() + + output = options.output + name = options.name + + if output is None: + output = 'sfizz-misc-icons.ttf' + if name is None: + name = 'Sfizz Misc Icons' + + ### + sys.stderr.write('* Font name: %s\n' % (name)) + sys.stderr.write('* Output to: %s\n' % (output)) + + ### + font = fontforge.font() + font.familyname = name + font.fullname = name + font.descent = 0 + + for (unicode, asset_name) in DATA['glyph_map'].items(): + unicode = int(unicode, 16) + asset_path = 'icons/%s.svg' % (asset_name) + + glyph = font.createChar(unicode) + glyph.importOutlines(asset_path) + glyph.width = 800 + glyph.vwidth = 800 + + #font.save('sfizz-misc-icons.sfd') + font.generate(output) + +if __name__ == '__main__': + main() diff --git a/scripts/misc-icons/icons/discord.svg b/scripts/misc-icons/icons/discord.svg new file mode 100644 index 0000000..85d9849 --- /dev/null +++ b/scripts/misc-icons/icons/discord.svg @@ -0,0 +1 @@ +Discord icon \ No newline at end of file diff --git a/scripts/misc-icons/icons/github.svg b/scripts/misc-icons/icons/github.svg new file mode 100644 index 0000000..3899712 --- /dev/null +++ b/scripts/misc-icons/icons/github.svg @@ -0,0 +1 @@ +GitHub icon \ No newline at end of file diff --git a/scripts/misc-icons/icons/opencollective.svg b/scripts/misc-icons/icons/opencollective.svg new file mode 100644 index 0000000..ba98a24 --- /dev/null +++ b/scripts/misc-icons/icons/opencollective.svg @@ -0,0 +1 @@ +Open Collective icon \ No newline at end of file diff --git a/scripts/misc-icons/icons/sfzformat.svg b/scripts/misc-icons/icons/sfzformat.svg new file mode 100644 index 0000000..490e15d --- /dev/null +++ b/scripts/misc-icons/icons/sfzformat.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/scripts/misc-icons/icons/sfztools.svg b/scripts/misc-icons/icons/sfztools.svg new file mode 100644 index 0000000..67f62bd --- /dev/null +++ b/scripts/misc-icons/icons/sfztools.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/scripts/package-osx-bundles.sh b/scripts/package-osx-bundles.sh new file mode 100755 index 0000000..b858f98 --- /dev/null +++ b/scripts/package-osx-bundles.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Adapted from Distrho's package-osx-bundles.sh script + +set -e + +if [ -d build ]; then + cd build +else + echo "Please run this script from the root folder" + exit +fi + +rm -rf au +rm -rf lv2 +rm -rf vst3 + +mkdir au lv2 vst3 +mv sfizz.component au/ +mv sfizz.lv2 lv2/ +mv sfizz.vst3 vst3/ + +pkgbuild \ + --identifier "sfz.tools.sfizz.au.bundle" \ + --install-location "/Library/Audio/Plug-Ins/Components/" \ + --root "${PWD}/au/" \ + sfz-tools-sfizz-au-bundle.pkg + +pkgbuild \ + --identifier "sfz.tools.sfizz.lv2.bundle" \ + --install-location "/Library/Audio/Plug-Ins/LV2/" \ + --root "${PWD}/lv2/" \ + sfz-tools-sfizz-lv2-bundle.pkg + +pkgbuild \ + --identifier "sfz.tools.sfizz.vst3.bundle" \ + --install-location "/Library/Audio/Plug-Ins/VST3/" \ + --root "${PWD}/vst3/" \ + sfz-tools-sfizz-vst3-bundle.pkg + +cd .. diff --git a/scripts/plugin.pkg/package.xml b/scripts/plugin.pkg/package.xml new file mode 100644 index 0000000..51e64e9 --- /dev/null +++ b/scripts/plugin.pkg/package.xml @@ -0,0 +1,22 @@ + + + sfizz + + + + + + sfz-tools-sfizz-au-bundle.pkg + + + sfz-tools-sfizz-lv2-bundle.pkg + + + sfz-tools-sfizz-vst3-bundle.pkg + + + + + + + diff --git a/scripts/plugin.pkg/welcome.txt b/scripts/plugin.pkg/welcome.txt new file mode 100644 index 0000000..2db1115 --- /dev/null +++ b/scripts/plugin.pkg/welcome.txt @@ -0,0 +1,3 @@ +OSX installer for sfizz + +sfizz is a sample-based musical synthesizer. \ No newline at end of file diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh new file mode 100755 index 0000000..ecfc6ba --- /dev/null +++ b/scripts/run_clang_tidy.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +clang-tidy \ +library/src/sfizz/ADSREnvelope.cpp \ +library/src/sfizz/Curve.cpp \ +library/src/sfizz/Effects.cpp \ +library/src/sfizz/EQPool.cpp \ +library/src/sfizz/FilePool.cpp \ +library/src/sfizz/FilterPool.cpp \ +library/src/sfizz/MidiState.cpp \ +library/src/sfizz/Opcode.cpp \ +library/src/sfizz/Oversampler.cpp \ +library/src/sfizz/Panning.cpp \ +library/src/sfizz/sfizz.cpp \ +library/src/sfizz/Region.cpp \ +library/src/sfizz/RegionStateful.cpp \ +library/src/sfizz/SIMDHelpers.cpp \ +library/src/sfizz/simd/HelpersSSE.cpp \ +library/src/sfizz/simd/HelpersAVX.cpp \ +library/src/sfizz/Synth.cpp \ +library/src/sfizz/Voice.cpp \ +library/src/sfizz/effects/Eq.cpp \ +library/src/sfizz/effects/Filter.cpp \ +library/src/sfizz/effects/Lofi.cpp \ +library/src/sfizz/effects/Nothing.cpp \ +plugins/vst/SfizzVstController.cpp \ +plugins/vst/SfizzVstProcessor.cpp \ +plugins/vst/SfizzVstEditor.cpp \ +plugins/vst/SfizzVstState.cpp \ +-- \ +-Ilibrary/external/abseil-cpp \ +-Ilibrary/external/jsl/include \ +-Ilibrary/external/filesystem/include \ +-Ilibrary/external/atomic_queue/include \ +-Ilibrary/external/threadpool \ +-Ilibrary/src/external/hiir \ +-Ilibrary/src/external/pugixml/src \ +-Ilibrary/external/st_audiofile/src \ +-Ilibrary/external/st_audiofile/thirdparty/dr_libs \ +-Ilibrary/src/sfizz \ +-Ilibrary/src \ +-Ilibrary/src/sfizz/utility/bit_array \ +-Ilibrary/src/sfizz/utility/spin_mutex \ +-Ilibrary/src/external/spline \ +-Ilibrary/src/external/cpuid/src \ +-Ilibrary/external/simde \ +-Iplugins/common \ +-Iplugins/vst \ +-Iplugins/vst/external/VST_SDK/VST3_SDK \ +-Iplugins/editor/external/vstgui4 \ +-Iplugins/vst/external/ring_buffer \ +-Iplugins/editor/src \ +-DNDEBUG -std=c++17

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 + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/plugins/editor/resources/logo_orange@2x.png b/plugins/editor/resources/logo_orange@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fde46ddaf8bde28ffe85663b4617b67b3181a1dc GIT binary patch literal 29240 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 + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/plugins/editor/resources/logo_text.png b/plugins/editor/resources/logo_text.png new file mode 100644 index 0000000000000000000000000000000000000000..d683e44e1a582f68ca4ce15119afafea6b358c49 GIT binary patch literal 5414 zcmcgw^;;9(_a_8IBt%-eJBEOCZFGkN6p$DpF&Hr#Q3;VwDNzuVni6Bu4Wk96Q-*{v zCLl3JeDVGLA3pbaZan9_&g-7@!@bXa-B)HNy41JWZV?d?QS0jgEr^JS)2`QNH*Z{z z0*JZs>xt4&&o+RFh^FUXCmw))a=kXP27+t?Eun6Ka3>gq2o8r!dir_=xH$PiB%v_( zf;|`>i zlMuI|6Aaq-vXXbzHG52p=-I0bq_eYveXeIP^tX@InZw48*De2Usfh?xsFa(<)>hTNE0SH8t12!q3lBoj2&3mnWX- zT4w^V6&!RO2j;wqlm&ry!aUU*_KH0~hMr}s7PS#bvKleoui+xoY)CD8DSxS(Ow~G7 z?}SNYwBC&1wT|ip6~|la3uRfmFIuivX*UW9g_|nlEk?^HNhR0C{qNiJ#J96!ic8GF zjw{G=!u!2tC)L@uiOa911MA1k38plS);t@lpWc_2UOeg;ei2m7GPP5@J>J+Bi|gu~fVnu#1b<4# z*Yp^RsP7?FJABYfn~y~4Lj4%NWlS`qvhklAdTwj@xOnb!#KTXd=gY1-17J~7BmK7ZxigP5%NAZ>Sbp)-b`ik z4gC`5b>|*YmSRVKPe^`{iZkgmz~>9LkDo@)ycK#13u2}#b<}u#fDGrLDDG_;s6>&W7h*n~e0}gV z+M=Z_2mk(ZUVY}Y)iexSeA+fWb+g6wF}6tCxW#eek4-B{MI#yd9@(1HfsL0V!b$6| zQPAtPy?-D*GIC97ABXnXy0{6aFZ@fc) znaOUJNCN?Kn&q{9a^wh266%6fx5!9Rc7q*)Oy%xYrY=H>?Ju4^>EQbfwranPFcz>= z(`Qb2=?}B&l1S?eDb=w0lu#LlNTi$&+EcA+q=dUpeXbmBs7&@~Dtu}_CmB_!vQNb? zy(~<3^75fN56`Z^p16-?Wg)!^)Vkp5FKb|8VIm2!)vfdL49Xdk6J{Gm2)&B!6uvT` zE!E?Z9&sa#J{*wTa@_R^+65OLpf^4JMPn4*!?L_#<^@{w$MWdham8; zd)X#?uqyAX$NuG6{8g3`Y49cq+V9~`WR)6~-}^snhm+&w)-u02TpWPesXDWA_Y$x}zynYW4mEe)->SI~QJPHg zRKdNKmRX+4KTy`gMK55?DFRE>faHi7f>5A$^8w@+#gJ_uz4eMJ{VmrQl{*nphJ-2N z2MqQ)DFlts6j$6`eOy-^n-#IX%ZH0+MgGVM`k#-Acd|?m9OfuaAwmy%9CZtH$Aro_SMpcG zXLSR38paIA{<`g9H38nyU4`KzM7~2~_JL$1UB~9m<=4h%HVpz83xp#~x8CqwkM3iN zsg4dpyo<%0jwoo@VgJYoyI(kyxr|%*lAd7@BQ6F}nX4Q4x$_)&X=4x`%Q4`d)BKg@ zvq8!Mb?=DH_U19LX<2|deR*Z}z?}H0A>5D>h+(XoUt7C@0A&v-38f&iRWPTa>Pd57=(%|zE*r)>I3$^(>6?U<-OIKY4E(PLA#*k zU(u#J$o@Gsp0=xm-4lKh@GOgInJ31NyeVEi$Jt5Uz^panO-%#lYfvTDzJ2qHJi6_0 zo3dV2H#E1^Ed_WSo&f+e%D(lc>UR={`VXxA#W615p&37P7sJs)`cD3E#2-l^JI)A^ ze;(6Rg)$JaB!P9tXk1rmT^Y-b&I zd0XQR^3(8dnG%o*cfv%z2sn>oQcF*x%xTDUoXf}cL}|k(cto4~HVquW0TT`|NG><7 z?W>8zysqS0m}wE|<=jVJ7>hZl#jk;QIpnYbrN!#AV!fVYHOvi!LNiXTpRyc7Y;J1+KJ!P)dq zWMj_?Rr?S6fUVsWt z+L;Bq4~p=nHZ35v&yWkQ?(GOC8X-EG4yK4Nw#GWxdiJ(;(j~bMCo!$;>rLw0nfWJ9 zoQD>UHohZ<^qArkigw-hFQYdOnC{looz+=fZo4+nY%20|ZLl503B!k9q5ahdQ)*Sb zoE*Hq!B7I=&b9VVoVp{QcqHeBa)L_ThONYkS^5ycv1una*A@T#V62T5FR`YhxL7y2 zcJN*=PMr1^;Y7nJ0V0LkOeN!VT-j7&XMlsvSbXL*`?t)SpnG|I5}z~5@CPBStyxU_i7mPAn&KU>UlfS@? z^C?mV9B)c<1xgdxR#L1LM}eusz){hvmwsO#%?kWxbbU-N9t)chKVg^*7j~zC*>$7r z<~l~^FqY}9McKc!VfKSK33&S#2BjA)I4#XP%8~>6ef%f42jQuB#g()jf4Mr_hv`Pc z?firn-Wy1o^^mq$ljAm^WA}32SbI7yjc>gRXXJA1E6Qn$8nPEUOxneMK&^x8H1rzz z2#DP%DZ}2pfX3Wy0u8EId(Bs}zIY?my7#C_LWKYVFhSo48alj>zrHnX8}xn>rOB!8 zj8q%jBE&apYNG zO%D!V7;U_#f#if1K6x0$#p?Ak*C*(jVQc z(X+)}KU|NcH37N%3+}hV1C%f%&M%{Ujh*sxYO@P3>Jxv=kg{h26>4C(Q#egN_>co|u{g8Xno>v*s>q18=!_uUZj+L@%gS~eE}(RX z-81B0DovgkiCmm^q%0~ONFCH1D5M3@oL-=*Z3jMk8>sX2 zbr|g+TM71XZLe_hB!J`WeEWVIATli%_$P86MNR2VR zTkGsj0}Jxbfy0q|0CB7>0EB`V=ib9LTpu^sd4K}}b^&^!ws@4yc3 z{*A-Ejq;U@L#*j|Ci~8z9B;?cT} zqNV$9zBaC&)C#gg#q|<~e#?WZrwZTBgGIWJ*=m6wZo@blPbVKaTMZTTCd6-w;)Te% zjZ7_QEamYK+S4^Sv-R zU|TW>!x@`s(-`(jFQD7O(2`W>vwNb+6wzCd568m3@FD;cJ=T|5uA?DOhk2%J*ilEp z!8j?4l~!P0;PV9Q2h{MlIQE>x{XVYPaP-&Zts@jqe=!STkj!c>Z2r`;r07fhMcnJ$ zowTi~pcbb+Kko!Zwz14st=N9zNdgM}q+;vdZSN!p`NH(ApTn1D>^*ibUVk5e!?vv( zkM2L#ZD2UAAL$Rk)119-6;5^SVP0oP-3tf4Qt+~DaH8058}=)a#yzwmg>E}e$RY~Q zlFuAj+&$egzYo#fv`2t@HBzKM&53%G1VD4)blDvZdhZ0o1;+0vfQo&aloRa}# z3MHSW_*3_u`fA0z)3DO}(T{lTV$rZzuio8Pvw@nKd&!7~M8u!gw~El|o3km4n#A-f zlCGza{1i0PT_ijwZBrP-faBI2ejcwfpbC5zi_Xmjv)*f%adSq!VkI2Tw%Z5-wVT78 z@2UjTB&&8}f2sOszK$$XA3LpBW=MFq_35=GckLKaXsN~`@RmhN_ zKvSVGJ1K7mr)FZr3vfpg;yZzrb-Yi6u1HqnS|m&X(LW?6d`@^8w0ru3{I#l4Q8FCo zm(I+T1?EgLYr1CK!gO4(GB2$*1iDdSQW)AqgVea!exHLFm z!U{yd(=;4MNnkt$IakP^eAQ?SpmJ3|-XPM@PnvunmsHC9PnYHiOeB5Giw1fVnU##M zGAFnZ|5KYw-)$l*-tcZpulp(r?0x~8QMTDrcS7>+4#JW1rKR8RDW z=mvHIO|N*T4hQT?RD#zO9i+SR3Y9{oVNES7LkZ1+0KUJ5y|hMtykJnBQyX31Rq|n4 z0w^7E72#Y>W0I_U-r3uZH%V0atKF^66xvF!wiaE*r$FE553Hh7m+qpxio9{}{Uw^2 zm-DtGYdf#AJ#gjI)xC-sVZJpA!TXkXdC3=PfEVYXonlTantxHD3Zfn|X19{f7!4tST%PktH zzooF8r8{eAAlXfxB5?ctOl$;4x5_srNV~gw9ckwcoe??8jPQ + + sfizz logo + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + sfizz logo + 2020-05-16 + + + Tobiasz 'unfa' Karoń + + + + + CC-0 + + + + + + + + + + + + + + + diff --git a/plugins/editor/resources/logo_text@2x.png b/plugins/editor/resources/logo_text@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c3e28c637e5168d34c6262c2ed284a4871e6cc06 GIT binary patch literal 10935 zcmeHt^;cBg7xz_^P(~C)2?>>uZjcTM0Ria-iJ@WWp+g-M7*aX~qqE>z$aRxLXrq(7dUbKvE z4i+Yiw2CIqkj}3Kul|~0>B#=ky>kYexLQLTXf>?u!2mZWHzyk>9~&2cxL9r#mPY44 z8i=E(v$ds_EAZ|eC+$Z!Q#)%jT4{*AiM0c*G}zqQ?0*uhTwNW7I5^DyljsgMgV@_c z99;e>&EfbTkNJNr9A02&2nQECCr8(E(**!9dB{skYIw}-&U^Z3j8C^75GWG6_ek}a zqvGYIo+0l3DYJMfK@9iqtY2{})U>T)cVluZRFo<1tk&2bVXjgbuh+`gE4Hfo__Px= z^ykEm0sNx@D4g3Gc|LcJOw%sp! z6|>uqtkYeNQ^EyvX`In7*9QdcmAhZcUEIW93Z}(Hnd1Zdu!)U#IhDSpyQkG)`{bFT zFYw)BNTsOQ`j(*FAo~v4do=JfV!{3echkvCwKmZVZ?pA|&6WOaW3?Uy0dP#daHqK6 z{;YcUqXX5gXmzzH@A}H=y~78~=ARs&>LfE#OlXZe8*Td*v*-z23_w-3@vwJkRt*K8zk?7F$z-4jqT4lfmE|5DTcaEf|s3Njmiy(5XE*`Epo$|VdCOQF*J;-efJ&Lf@ zils*$)!nl`*X+%~_kr*PE+>n4Wsv#AAkhfg&-M&o$t=MQC@c2uK=SF$`@8aiX?_JZ znq84muTcRzDF3+`yG>q;n?+g>M`K+dnu~%b0Sc7m*JaktMzx`)F%Cz@7VJr$ii1`+ zhDS~PD!d$!n-4b%O0js>9-F6w)o#2zM1VVG4^K5^cSZj?l0w{Nf3n5TcMLf~PQ?SU zHCkQ2?jFSxUe=|rrtEoCWlVJk@G-IIBkb$a;aSF9diY0-CrtTFz~diXChE5fK!nZAayaGo2hP=O!-*=~cKU$jT4U}bHaCQ>U zjr*uCJDx086Qw%!!Y5m*D`~v<{Y6;@+;Ifs>aens;`ntBhtCh-UU-kFC*C$~X5^II zdhMQF*X15OvYN?uUND9?6%QjtfvF)*$!yGkN7bL->E<@bbSF#o_2;F&)#E5`&)m635D9oLr2Trj@4cH~5jZIBk5p;|1 z_#8U5R><38WR@c^H~#DV?!klCsAa!@MAjm5240g&F5Awhpit7;Zp+cpgnw39Czc(% zkE@Jt?68cZC6mI<*Y32&d8vG%e<5J??6*qi6CF@I=MnHpeHq*~(lC5MG`M>p%iqWx z@^kifPzL}|a}oDhZ%2dL6S1LSFI!*c>vOCWp|E4f_L|afZ}4Ig zg^KJg!U-UmL^Dq6M{`}9`kGnimHMNB_2!xzqS1>6BEV7M+Ef*1Gb!zt=Q~!b+OnId zOQJ2Pzte#5w)Plm#&PEDPT0EL=*DQ(eMA{h_ z*iW2cu%o44Aq0j-B}x)(2)fd`@UiV1*0d496g|$|xWE)jg8w~5PmFL68PIB(aBwO|dENHVe&%Re`xJ zr(^t?7iFUhW9pLVq(I5O(Pq=|mHJ-n4|dcUD%UfjG+RVuwC76O zVm%j~>Te$%1Xca9EkxHYEOC0-EIbrGM1C{)$=>Kqr)WNWBuE0~1}|)=MjPL;p)#gU zt>W-&s8gC0eARTYYj(r&wX$%JkBvVO7khcDwCy^v`Q2I6e!_!+JD3K&QdWgPr_>|X|{J816OBH z6+z#fP&2)wpLox76zI!0Ln5CS+)`euMKeClXwvA0Kr)UAcWUged zI2_Q5;qa4qCPR-QtPLBpjZu6Y$>l?g;Wgt775&pAeAP4#;(k--SS$B_1`bI8o=sx_1$qUP{`5LzZR5g`tr0lr-Bjk>HrkR6QpTmii zy7SgxUgD1bzgJT@m_;m=-BUTO=Pq36udZ&p9$K|~ZQENoc*+;O>-S>UCfS&jOm7~H z3@UN@`^Y>i&sD5&D&}2&AnR#MaF7O$XNB|Is>g@XLO;E#4l`$sd_8sH>XvH!we?k( zr&~&RtWK-^sMZ2*i+3y{q-Xw0@aIO}sJV5@p4o+{Xq5yh2K4SYY)tA+p2)9WwHIe_ zDBiCOK8<oqTH~sksC>QC*eoOKD>n#tcAy4Fb$Vw( zg?V>TJ}8WDexYTHAT~wYZ}zySu=Hby_Z_##HhWRe3k|6zpWa&?P0SyUxYkx>D{uOp z#+4I@=?TPK>$xKdvZTy3wXb!U)?2u5OdZ`dYuXgN^SM4N;8zv9>t@qam&QgAERbu# zi!p}av*zVy>#NQ-W&xItS>ff!cn^9NGha8;P}tUIUL2jgq985xcnxZ~>e0}fPcBf) z|MHQItFPmsgJ%E6&vdZvc)G7XrrX^(mvuVf)FiC0{8mQ0(6r99O_P#wCnxyRUF-gRj8UOjvc~{rs^KEq+4VM9HsEI-F zL4|vx0(r4V?=iO?SBYt^)ZAWthZ>HF<)SE)o}(eD+0_S&wC7-prf{wp6ibLn=_bu9 zZFuoi#z)!7_|9aiv#+Y8d(0h8mUAA=v^(~7L}09~3|uCp>B0QVW_kl?W>rF#{xoi5 za$mU&IJ!8&>RfME`=V^bcM_WCUzTn4zK))P@ieh3iJ=c!YkLy3vR$TY|+>;JM}#?)HE{wgRGrKAT^NV96<;*6ZwmbVeqk3EVo|y z)QyV!iSwNL*LZ@fZf5Ef{3|fGT;)dn)qns$Gkc_c4?+!OmcYWAQ;ZuwoUZ^r295Sv z={jG?!m8`4xYM`ru4YSoQs~LjPckOFYTodq3i)9S+^gQ;7V?gcO&5H4aTdio;cGTq zd$&T{+M=U2`+<}Ga`?~F-@&$HHp7Tnv&-G)pXbdCz3@01@py4&mfX`@$8xAgTify1Q+#4O7{miOWMj3FlZ<vu(6+CALLM`NxbbVTx)a?@m&pz$l=%!~1+M8Ph z7d~A=GbbyLe=%!}0?%jOug!z3WbaT+cd;suml32fgl3qBcpj~g>s6~|!^3TPeFB4G z7YdzGny2q3?ePn4Qo%>ON6|OmI{M-1eLRFq z&1wCS9}}|r$a)qQ*2(vBUPI;iT9#J3aG!-PVJ9P*mTY`p%<#x4+&FnMufxy>FZR)$ zD4wEUiYD^tL&M>(JI2}ZH&V5ta$C>e9e5(hkoDXg%?g{$n3}Gd2toS!2lg&9sPnCd zUY0!UY}FdFe}1fq32)MpdrHQ9JFA2*vJcz2dk0bQinDdjKVUiK`(cU^k`e4oXX?0~ zp0X==ao3}`U_!#jOzz;U2}g?k<#k9+2x31nuA<)HX}d6P@nq4G3hBgF9KxV|wmWi( z^nnvJ{)JT7yH5hYz-C44X#8RZwIn8FK#;K=JEcye{?m3-b&FiDj|VKw+pa|iM9Qo; zVQO=e9~U2gGK&wZvI`Rk4JSxt;I7tu=6euDVO0DK-_kKn5UgPh8xUyrO(}SKRsqJz zhv2)`2`*UROixO8rD^fMqNC`MLG(O)$d@ww`fCL=;iZd9bngtS*8=vfY6!a5NUcyR z%xMVRGqC8G0zImHY`Zg1Uh_OQDJ`u0EW~u7c)7hil;cOgLGY6n6>S4C_xQE~Oxc^^ z9n#HUiUz%x=z%5cG5&k&57w7n^_3q;4IBJu;nz71dNZ@C*R?OFO4#tBR%$SeT`#MQ zLik~BQj@^!RfMkrm?r%CjvGi)O*GG{_zPPH^NjBCCzFO?$cuCCVWyB{cQ(o%8`XSW zi`I@~7B3xZdZSMmssoYiTACf<7U~C7aht>8{E^qoKQd>1C;esFRz5qmk+qZ>6AHG6 z)YEf4rPzn|%EadtwYGix;O}U90MBHH^-QiD{*}I`sk8V4rX|rO3=?PW<)R0{x*ueX z?#I(VD1>pP6#8CSz1dOI@hlV#y1nP3s<+4htA&QMW&C_OHM!sf+r}A_^6IjMtv@5% z4<&(3{#hD1@MecmwbfaVcq@zixlc)|HCNKcT>&+>F<-<^NWI@q8a&6ci`L9Lk3Kyt ze{l9xREJk?Pet|t&2yPb`M&Lm3xwwZ-L}Z<#3wKg=Hlu{%N(12`A^TJn902gebOj~ za1s7?ZyFJthO>k|Z$iMqaa^oVI`{Jz($z@WKk9*@6ezK9tq^a|=}%i^UXX?Xma@;B z^$)1otVZ`rCqrc)F4s!-jZ~;UZLiR4@pXfbTyi(Vy+R(kzv*bIqy0h=v|#^I`+hd; z7+!VXa{FX*TSnddiHGtS z%cWq(C&*AzkJcdDrH@>nOU};D5g&#$?DJ+DgGWfoHm9Y|?`>xA7*F>;h)l~dz1Qf_ zKyvppwTxAeWkvUcnT=!2Kvriv@8R>1fd<8H&S)@i_&2Kq5}S}NFQF>FDGGQFSI^(FJSb&3h`G~@CQ$bLAgc#H}0y0M7GoSx>x*v zBHW#~kqZzo&QZGNG+$app5~C6jrk4uqT$P&bhYQ&D&aN^!6#R1&xa`=YGgGb6emdK zPJ)JSHn90OkNDNa<1tFD3si6)J-=TE)4ACGd~2A5Q3S6~B|~a_mqpEz%QpNJ+fPo_ zvq_HvHo?4moY}Rg4>XctpV5=D>W4;uDn5%m^X$6khI1?F>Mm?>eUh9z7YY^}m_(O* zU+j9$Kd@b88|}+u6o(W_&CR+z%va`=)G@L}BWPkpjj8za`(SOQ7HYV-7zH%+vqqLHP{Ql{y7B5|?qO(z5 z%fz95q=0mtVg9t{`B0;_=EQq+c@Ctg(K}|zNJ9RzlXFuY(Te-3o+PFJp!~zODhO<+ z=x|^RVnK|z>KG;*6KjnAD+}Eq`9}Dfv|Cu(23?1s7+UP2aCe9RPPNkfYPDn%1AKGhFZc}n&+}k7a zMDVFhrb%3$t@sfCeTHDMbOu8A zC#n{yM^iqEEKrMq%oNc(b}#i06>wb8o(FAgK4Ebn^VByX+4U(kd(>KBJ`u1N`N?j) z1FRM`2+y9OmGiK?T6Iwh#FAaT)x>0K)$Omax8+qShITBM@oGw1tNJ7 zcaon+C_cS?mXnRBzP^}>+vM_Q^vFbLu@qml=fB$6hFE!4P`mwt6`ekE+%I}WYJ@X5 z!BJt&)JAB8YFJMeVcw?7zKX~qMBTmSrqR+n5cnt>kS0*s%ah()X-9xxQPudf{=*G2h&(_X75xExb%*gZZ%|AgXZ$?W84QwRaQk>j9-wM-m|atEUHq3CV9erXeu>iyTH*V~?;g8To0z*5 z-;D?)Z;(-CMHlm&e6_?Vfvb5qxNg#}EV4Ck%J?H1@9~atEaQ$pi%#3^A$9t;lc!rn zfh~UHy9ks{4jjv7*ZGYdb0>LLs?FIFXT5pSdMDdB#UURKzjayKuombEPZCjRC!~If zoHMaVSzkA^cZV;sG;;bjNX<2jbgNPU;w6f)5Bf@}1W%2nyM0vpFoq0$274GKmmEgl zIXrXiYB0_h|M0X7 za+cWM4W^*d-Lhlv*rn!B1aR&h8y8j0(@z{;(jn;{k(@v)Y_UZuWWlj(sQR?eD+``; zZ6wkndcofe&q*yNhR%r3f60Fka7($%0JD!XJIiAVL%hzR$uM4X4$5{5&8*FOZgJ*~ zUDSl7m{^o3jFHCL}@7Uk0@6*b?!Z;a;ga*q*K_A1hZu`^{hXSzvS3N zqLb&{D<&(T5?iARPR8%pfEAE=iECkY3gjxGPg>5P1iY};hj=z8_xZecgE*Eu$*XB| zTX2^mwpKlDH>~t6(52`cS~_kgHt@^t(-!;i0eTD(C_>*mv-Vevko$x=`}80M`||-S z+tf(SQAW>c+?B8&^^G(Q?yBO~*9zS6__J=6SQR0yDndCH`dOE0zD;nKoQ8oRp(&cA zLQPEbdjn4*9^>8K?0qH^$(oB#?PKjQ%0|`pl+K4{wbKFILY+HqF9hhoFl^SpMU;0Z zz)RAYZqD&&mx|-UdHTS$kzF~NomE}(s=cv8lBqN=M)fM)?&MPFUy4k$b*{)0z!m+B zK|eam{3s*f57GDV8u`AIRC=WD5H)V1Ps0vUjHWAoI7U5o$xqmgjua?UcSgtA*T?M5 zvNqewWJ81F1RFHtDn5y#_%_Y6x$7BT&g}4|=tMOtzC@=oW$*a68gXGN9A~G!s3Di% zR~%Gv>Qwtun&^6`t|V<}R(&hr^=8p1b?X$S?((ZU4dC@GIz?{cS-aSU14I@*)G7gS zBR?ynD)IvUL*cu!!Rx-=^&`*B#fP>2Bg@QAjBMt6?Q?|;N`AINFZJsW=Bm?Rv98fO zRs@<9%_h3Zy`=Nu3)q$0kVtIpk6V-5jGq!5@`)*xe7d3EWa*4p=e&ds zukG#6l4az!L#PT&aI8t56+-SWDpe`Q%zQf-Ojk;b8q0osw)Ts#;4wV*{6l8!RH{dI zlc}Atjrr15GtU$FuXw8ObGHZ8aTU;VR!ekrK)UKX)A*f~(=Zhh9uS1aIyf1-I8{!P z0evoy&h=FdPk;Y@XI!9cAjYBjo;SqvE4{-rsy#n!`o1T_wWAb9xO35wOXpXlCQG8< zzaW<%CC|aqVNY))3HrQwMsrI&OES1yd=>c%-odtn>ztSRy8Z{OyFsJxLZ7~Os<$;w z$wGQ56g!WYm(PYvvDw5Dg#_qxU0?Gei{j(At`KsPXE;_N16djL-grCphN(myHBH$8o1#e>1Q=Pf)Y*b}8cYh}}J; zL7fSmdkhy|{PtA#9J)HjV04^bzZWb%6KI)z8d~ahi)@mq^E{4e)3@vPkJ(R9=kvi~ zQ6PI4kX9SBq1hZU=6YV!OiXNVaCcKMPL+I7`w8@xXF-j^@PK8$CQQ)n1B}tYYpC{^ z%VoZ%PW+?Dp?_C5nyi6<%q4p#!71w`xr58Ct?~W}JM&8+zc0JwPoPYhWQ~m$s)`ypB zF~6>GlTtg;7-Wu2`xu&x`}xbZ_?nwpya%-Lwf}?X#w;Re1JF7bt3$<`AjW^N0|oxh zvq1=XWBIz^G5L@+V!56VlQDk&)WoSa4Oa?VL2pgF;{@r_wFu`r6FPYtReL}4HFWDd z)XAy84U_sf57%_dOQ?0fFtB5npsv=M_HB>mXn77hdYDM~?D>JyU}$)gT6ZE`SY^tO z#p=KD?8tqc3~HhjuT&0Ogauo3@zR6CeIu|rExRh=eBh;|(LZFu_r2Zn29d13MOdwj z@=f$Yg2v)Ol}~H?#%dPMPM@&+65{Ar=FLKyhirs+%|B=H=&oGptGOuB|#P#%L zfK$cSUF`C&nEEZY(ihVC&rr}kWzyl-rYIg$c?hLw_rVm$WBy22(0ZxFvv)fp*nVtz zznkt+NE5BQVo~;q4)G7F0D%KPMxk$PLSoJ$YxpJ%L=O#chJ8Rxgtrv$chVw5Q*iGF zf8Ch6DEHmo7+LlrtLDSK%z=#m!VG_c6kVIJ zovAIOFG((JMjb3hKXgWA9J2dlc)BTf6_fI&&&f`mmhye%g#2T<7-JBfmr75k5>DWk zbU2+H6^#oN*%iPx2{rX2pC=Ze>q)0BB1v6Nuf*K@C6e=w`>yA6K2}+Gb_V!Vu-`mh z+G86hx&Dl;-`3YYlL@(33fG~-vs5-z5b5hzmm3m(AN>NHpRpsHeW2q@d%IFoL4J(Qs85k_TvI+=|CS zwzH(lSjbisWS*ZFxL^DfXcfDbwWk6TbW!~x1pe}fsE>&iss0Z&?$Of43AUlN1On1< zorm8ma%YnK54pw|jY;rF{a@HPE|LofC|sBGk#h|%!_d5jU|E3lC(CrZ?E=bl|L&tu zaeR@Bu~_W-FAt`EG47M=8QP5I4sgjE$tVq4vHu@-Ef&KY`Q!{MH(+pR$|TO5u;m}r z>=X1MiZ3z))5r*XLQ(bH$3oC=g8v6Wo4sTEt)qg)tVMAN=U>^-hRXj#sXbtMzB=zX z{!`+mB~iJwnUEk3aEu*FX4g!>ANB|fnbVjBxrR{S6FK7n$7HTupA>lq82>5yyOe;k z=Pec-cLXIe*wIiLUiVM{t$ZuwCOEq9v3@K0U4g5Mc)}G{bO*pC^3tA?m}t2C!4GiG z{N#DfIO`kMbARM2LUQtlC%`#d)}s{FfJ^02=!*=i{AKp>em)*hu_x|VR{9fqg~o3i zH+q>V=p0cS$V!jh#8CY^isq&vPp(m<_Cv-UD&jiD#hYbLH6lPZ^es=@<;X_J`9>EB&k)h^cT}!@QZ9VKk$+|LEN1T+rT%iU&@N@bo)w%@?Q#Fe$Sw2ApTM=@6{{WS# zlLaYI=urEuv{JUCME0G5;8Wwx;JrFu*HiJ7BAkGW zGnyZt7sSHpwt>fYFAWz4A1s}lOW+9N1w00AcBTQS zilo&W#<8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H101$LV zSaeirbZlh+b7pCJdLV3XXK#(|1=aun0338hSad;kbZBpK090>cX<>7EASZQhW??5F zOJQDZyGB7eQEig4LF*c4qr?mh809bTI zSad^gaCvfRXJ~W)LqjkiP<3K#X=5NnZ*5^|ZXiTuWNBkzbZKvHAZT=Sa5^t9V{&C- zbZK^FV{dJ3Z*FrgZ*pfZaCKsAX=7w>ZDDC{FM4HiZ!a+}FfYdAz4-tD2xv(}K~!ko zWL1!2kZScI_d@?2nHT^{1{a#>eESXqpMAT;dqlHL6k*n7`Cce%UgUd=zd zobMm@{J!sZe&5}5?>Xl#BGGr=UYI$75H|@uFX6^G?3%lXm~JJmz)`&^T2Y zw-uJm^@{^ye-RTOGdA0Mnz}II3~vrT`VGhKiUS(4$u6@@I}sXpDE=p|zvI`&-DkLQ zl|DM5<7?u9LYwQ+^@M*VoJ`!|v#ayzu+u=n zdFFAE|F>ju3Q*`2Eryn=_%Be9FABqF`_6(3sh|5;>E}15?UtOCC?vHKy`=J7p(I)I z!TF8u%M81vgi4$eaN*n^h_hj3N~jC+<=D8Z=w3cJAH+N^h3Jxl@j{lS?@c$P11I{^ zk|mowUZmF;=DBCQ+Z6|-kJH?=PniC6tA~*}k#+#-ltOgr7tw&y&6$QwARrAdUAiT6 zgn|v@jG|r&S(Ul;>Ql$`Gmdmhgz04*93=^^@S-Gs_t2&uU=3x4>U%*S3ondJ_ zp_M(Qv%f&uZn3gid1a%(7?vr8=wvR#+3Fmik$pXPn|v8lh$1BC&73nigE^3Um0fy{ zmQi^|cBY$3j$);Fe{NDvs#la^ImhJaHp$&f&}HN}Fy+pSM5zt;#jg+}u+F){Y=~3o}aYcK*uT6s)>4^IZkIE&zoo z#SeuqWck~X-YH*6D*S?-X6HxpALD`qjRE=b0(9>&x`NBp%q=`v2o#zhWaTbAjPy5& z1#CiGDEK+=5kA0Fb^H$;g)bG@kaAc~pSu4~xsGpLijbmfNas+5Gl6$60>UoghLtyv`WUf@Q;2Kgc{x{M`R&R#`Bnu>E?!`K zDa3fe>r{YXFHR_XOwgOkV3h-@-zBkCrHGr)uWXe9tL`)Yb*T<8y5e$i04rEhQ34o7 zN@7Yj@J(F2g;AFMl*BISr@5tH^2uI$kjY-Gax2?QEBkfH3{qN@tKGutP34p2;D(0s zJ?fR@)D<5h#gvBdYDX2yS!b!zwFVS3Wx_4&H zc5Gxr`3l~4pR4^J(pj~9AHCWQbqTr=9k@YfT;|TKdkc#bx23vKnp?Nu8?XKa-5ARH zt?qfgcJ+_y+L?MT?G&_~s9#Id_EF4ZHScifPHVj5Dm#1%36kwj9a%l|;!A z^phy|A@R~l5!d5vXx^%yMy$a$-Uo&H$Mj~Tb2NuN)Qj7zp)Ek8m9-&kevygjw~+F* zv?6wr2gVaE7R0f)@INR7v=lRyG>aedO?Cb9c4s?K*oQ~jcA$QTF_^UfX`iCmSNCup zLjv0>P(ElEa8PLb9MhFS0P$gN$TNDG^M*effX3AbJkk+`BHg5-3@C~S{FN!mG(kn@kjmDY zdQjvrX`Jg(h->D!98a6>VdHd})C9y*cW!<{`{BMW*hX4V`q z26sB?*mdIepz~{#N68V5;Hbh2x~S*8d6h6-Cx~w7ZU8Ub>HQhI$nLF7w3oR3yU|T^ zdyaENBY0Lp0~r>m1CcqXN8CHn3j}DopLH*?cQ=ZT1L3eYu%g#NvwQx$Fm8?@R^V0- zZCIcV=4E}M=6yFiZUQH!`ilB~$7SMX65Hr^fjU}Ql zx?kF_wCF62mNHAW<=_3`Jsw~4STq)(kWSpKE$s`X!S!1+9_NFh5PG*SOxG9s5F3(P z!?%FKK9T>o{&jbN#=skk!0_GhL7*}4gGFF?_}~yw_(rrC+CA+(ppd%=?Ao3jwU2@e zBcJ%8LF)$i9{zdk?6*MSzHN;^expq`z4bNM3JO8ClQy7XE%ZmbVG}TqZ^qS=B@@Hc zr#Z~nHu|CAsY_Gf0nvVFcjhcX<>7EASZQhW??5F zOJQDZyGB7eQEig4LF*c4qr?mh809bTI zSad^gaCvfRXJ~W)LqjkiP<3K#X=5NnZ*5^|ZXiTuWNBkzbZKvHAZT=Sa5^t9V{&C- zbZK^FV{dJ3Z*FrgZ*pfZaCKsAX=7w>ZDDC{FM4HiZ!a+}FfYdAz4-tD5jROhK~#9! z?Ol6(6y>>2AORFPV37(Um#Re+%drP9)oKwv9uI0u>9Mt*1C%32Ku@i<3TU;kfe>fH$tWetGO(Q#RM9M`@*B~u3$C#j9cuw z#dQSPs`uCed2|Th&P^eDJsqc2i`?qmKoFjr%IB8DzNP!W zihkR=y|dghkAWcM8y)P|ac%4N2hnjG$FWb`K^Q!KF_q8bW6rIQijLd1y{kQR9>9SC zk35gF9{%oOo(Uf19)rjhYH&2#gHLeseJ*-#+taH^Phi2A=NZqRN}P-i=T6=mJdb!@ zGTQE4V@9uVR>nzqY!^MZVR*0g(t804mG@fqZ*8wFp4nc8+uL0^G1r9iBn#th_^c3J zw`GI~Bg z6(RFPucswW9{dUryd*kfC-GJCNnl{q|8CJ4d$~yJIt2`rioV!Me7*cMAP5khv6DFc z#@B#AT+FkZJ@|{i0TW+~r&#t90rGEviIa;(XY8fHjRC+whv^9WoO>=oP@3_Q>s z92&IM+G&a4_fgJwgf}>-5#hbTQiYb+i230PqPK^l9t7`Uu_SzDXhi6E2v~)oVE^F9 zt!S`6#umCxXnXzd4w+!`2D2>lhiI)FwIx6sOi>SSuzYsdpF?$_AP9z#^pF?XO%HoJ zw1W(DAcV&_Oiv@GGKt80IUDSa<%F@^s1vRw zS(%9@+F*Km;ie4^TN9y;079tdGCkr7F&zlkaDN9!WFT3Fpam>jlXHLxq`?t;DC$AP z)CLDFIHNuT#P9{X<cE*K?gpRHA~|imwk8F%!4&n38yp!a1r8XZnx$u@ZjlF~ z_D1fG+%5e?>Lv}9=1RNq?PY7&Esxy9sl*q@xY`@G7|ls!{qSjt)@BT(!O}ex^+5Vv zgQGV^0UM-svOh&EW1Zz%DccbhDea${A*X7Jo=Nuau)+LbF((MvBsstj!p* zQ&@xP=|%NL{_s1~b7X*`U-q%W$%4tuyP|(1inF<-AgseTd~m zvwkR~>*X7SK^BYG8C8-x7x8V0P`#*M$aJWpLH4WfWFCzM6jx=uo}HC1k3JEtiw4=l zTw6t7WKpvQBiwgf%KWh`*%P!TcV%xz8_+ttV^`78^LLIeU~$HKYGd}v0YQJvS}PVN zmPdcey+!mNq7N8Do`~v4nr_F0e7Biu3K8B>`5Zd!mH$BAgN{Uhb~7EtXkSzhE{ZRc4LV3aQY#+j0O=lD#^W-XEqs1#Ev^{Hc)HRdP9Fyh_}Hqw=N>@R zAxDSi970az z65_BCB*3|7O6`3nVH8!=#p5TYiN%S)MAB9pH8>1YkAGPBHDcl@!dyhU{-n>a`X^C; zZUGIyQkNuZaK)te*h&mf0tDTOPurrwN#%(4fbcWV4)5jIjO)3Luyj;CR%%P>f5L$f&DD?9LAhnB+4>4fIzGUjL0;_hU+&}nbR?^wE_Ot#0sc zlye(_&wLKTJCQ`-g)TYIW{;v(k7oUZMti|AJ0ABw>zozWHY#VuM>zvIzzYV2Tw&FZ zTjZR?_^kiHOkuu`FgFr7Dg0i}CESJczo#@6bb!V6%T}L?Dpu#7QvgqlC=T(z`cvd0 zOk={YA{9%q8gpZXPZZ{6u#8_ur@c8F72~L$F7L;Jh;yILjmy>N0xwkMJjzq$Cc;u9 z+?L!|vHBDe!MulAMy^)eL!-UQWw}~hQSpBy=pWAu%5>AHwo;)7ZF~a zke5uP&do1n8ShCt?N$C)z5&xKxK%{w*V@2Qxv?OmV7LIxqNl*3+1SExG+XdYF3c(z zV)7Q23E?dG8_QTP)KR(1I)6<;AF5|i{?dVFlq@d#tgxdH%)p{K-@*zcvsd`KRna3@ zO$A{B7%wobyyEwT*L6XTF&8bj?#!b&j%9=y zKxJW}=$VocRBya!ql2vj954D_1RSMirtV6PqFFx`UR9|Y;q9@KYah$_G%6|!3nhz7 zF5`+N`yHkyE(VkWhKpt%ekn~vvJ9$)g1@c%R({MLS z6KLQ5$Ql)kRM%91z|3Wd>JpN3iNOB*W2~A|E3Q^m%T!u)WM+>})<)SOTu;?fvB+Wi zqRh1n5NNnhUCInd&N<=Ll<&Z*Dc{TgG_ouY9T${QT39H5rECJ#)2aT=VLGGY>2g3Y z%$f5%mQzOHw|px+FlN;YyzTZzM+N2TIw}hb<*UlOSrm6UOmC_K@QU$^4XR8N?>4|)4-ER22>Jw+@rFC z$y?RVlVeR~A6g$&QCe82IF0L7RuErUu;$=W1qiNlaBU0h(Pt1RgVhD-dzm{l$<#|%#5lE;UlSf5v_&Si^Z$LQ4Xl4 zw6IY1MD-x5XQ}VCu;t;O&A zs(rd{1RagOmde5cIM&6m_?Js)VWDxYdO{6%Frf~ud5q7um0J4GTw`2s=D2lEeE`d; zCt#ski7<~hTX-8^{bKcHd_47s=zROuf5PIQwr(jY328kT!BDSI@2EHXdqpr?y}iE4 zB=M(uuwxqeN6?Hw8Y9f?DL+EtC^_nId9McTMasHB-pHOdl2hAgJD|d4^%533eK}!e)H>QHoV9I*n z(DFsgSPKwER+ZTY#9Q64%q9XWtgRIY^MIvyUbKV|_QC|r2GiAR`Sv@w^{G}>D-gpe zORn8olaNe(m*6)CD4be#2y=8X~{}OTcI_Wj!z*;M&Ws?Je!V3pd*%+F#*we_{IzD33&VgEfay=2tAPEQIIS z`GV14%6fng^P%=Vnn+EJW?Td8Xzy>&={Tf$5qLlEqj?|YdyBvxysCWz6Xwq8r+mOy zWBKHuF`22Wn?qNxeS?{M3_5<)v01ZI^Op{H&0jnGJN!FOb+~taKKVbdT@~;X6O$~! zMCTd@G3+Bc3p#;;Av!mai(&Hs$1A;8z!aSqMQ7|JuCBTY2m(cC>?FG0)B+P-ABoP` zOM^e}0t7FM&e%y@lU@TRv`a*1>?E#NUk3)XqA&K+-g|D0-v9=ZMQ7|JZvMwjKyXlW z#!jNgvj>>C`KsuQeN1x9>ecjuDQ@XH?h?JRi|E~Q3lOA>&e+HP>(p1<2Tb(u61|xj z4V*n79e$Ag@BLo=fI!!~Nc3(-#K7AFFre*YdE0FNzRABL7z`Gjn;HAB<6!k5+=gQC z-@)kCyWtgsvO9k>g<){L=-rHnpF$bR&<5&drDzd0huq;4{&=84{y~qrewq=fO#IZbroT%VWSBjc}jn+)Qb3_BilF z11uMPn=vu=tiEyrcpw?X$&DFy73`$nr|;5(1?ZgEA^J5ln>+{JHYf}R1F%4E$b(%l zS9EG-xA}h94t|gYm7sya$!nobsG9tjJG>ztmTez@2aXZy6e2ec-v9sr07*qoM6N<$ Ef@g2{CIA2c literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/logo_text_white.png b/plugins/editor/resources/logo_text_white.png new file mode 100644 index 0000000000000000000000000000000000000000..312719e5dc5b1a7d128290e19c6e3244d9f59faf GIT binary patch literal 2591 zcmeH|{Xf&|AICpPW;W8<+$Cn!u|+m=Q*I8M%|=T$A6mL$G&Y*eZro+=No!Fm4kFem zOm50eDJ_vWPEtD3(K&KcN2wHYI&!M-^nHB)hwl&PdOY6O`*FSB&-eSf9}M+?PsXsC}Z^+#p)&`AED9t#(=>qAv)6K%P$Q8dooM1=Kwv09bu}#WXP9 zShzZ9Dkd|*-G?ygqky1;k<=xmYB3Y?(u`C7lG(a_MuSyX#Tqb=nOC@csFxT$iDpt zLcqUakB8#=`%$orDw}4 z&Q(?^E>_pnD(f!QH#9a~zS7*%+IIC?`}G?gs#~46ySnf6-0kiAx&N1Y1NR3XJRExT z>*J9pzl}cq{g1I{;}erp&tLpG{qoh!>o;#_-@TukU-+>2*T=t?J}v+A`Ad77bPxcv z?R-fD(8ToS?XMB{B<4Err{BiMG;eXEx7aRO6cTG|cDAMzyx0+%cY&(_EOslRPekYwNzR|1owQRX+$YZ#8rXV)W=~>oB zv;94Jq6_=2=C6)?zU;nE_eE-fz5fMGa;j{YT7#53S@yZ_qFAium}f=*WudI*95VR5 zx6+DwR(=ZHtJ9u-+RbQWZatt4>-V0dRc@A8^|>t@0p)gd9$Axj{z;jPiKD-{rF-<0 zq$J#(_R-_(D92ze<{VnEzA#p!hfh^W5OpU~DA+&Igirnl+Gbx{UTwgZKN-m}83TH{ z@hyuoSdDpOkSnQZWV4?BV2iY4GLf4`YA;smgSb2_oK_3;t&EFF0{8pDrS1ZG$p+@MTinH>{VI&fHqefK+F+Eb5g=p;l{8r|KfX7%{D`u7*fo4qnh6ZEom+UIf;kQjcQWxuWjHG6m=y_CR z0Jx!{74bR@qu~7P74z8)s}!+LO4GYS0jHv9&B^V=IR{u{pgAdKI|UPxd%J7VbXaqn zur_2YTBpv|vxjNo%(&!Z(;xvwVS%*P?b?LBFKeXdU2In~{(yGJ$(A^5qo5?H?W~Fx zcgchJDfD1pK9R+}R-HoteCCU^Jk25R$l-6O*c{34rsOO})hQ&LE;(GST zO7I;!=CokPYJP^cxWyVliel(M3EPIQ!6gW{>L(h4%VbIuty$T&-G;eux@n6PWqIA??1 zizdU4+ZZ?oEt9n=$AG@tDG;3QQtU_Ocv|kBzEEmF!6`HFZ>li2k?Zs6EwC(de=(@{ z-qJx6@o2(i=foiK(y!mkuz3Q0RjF24J6OgD<1BD36duGNNH0;=jsKu9ljr4tFR)G5 zp6!Zj@t|!@{L*et`mmk+@C3X!MCOE-Uxe&C)-;hSQ^q&FOvzOwp=c%5zK^8=;^uW; zWjFln0=AGJyhGr+nocO7tfc80{UDRERpVm>Nn2NT39N4EW|38|aJ&y1AkUuAlR0D~ zhwkg3j>J_K)|X$06S8WOI|V;ZBL5dgNpEYLrR+C!C|S9+l2jSRFqrCQnQnepqti|5 z*~kf5Mxp&Msp%8`N)^QBr*Z=?{i*cx5fh3t>@#jI(ho$!UeX-B!yv1-ZXaYJ@$D6j zYaMap%4p2R4X=c?x#(BWng+_yfMQ$5^QZoEx`ng3MzUIJ>DWl;W%(q7vT>ln@|{HE zRU^HgF!cPCJND(sQ~ literal 0 HcmV?d00001 diff --git a/plugins/editor/resources/logo_text_white@2x.png b/plugins/editor/resources/logo_text_white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d3464ae7d25283a215a2b59cf5e74922f50b5e10 GIT binary patch literal 4056 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 + +SColorRGB::SColorRGB(const CColor &cc) +{ + r = cc.normRed(); + g = cc.normGreen(); + b = cc.normBlue(); + a = cc.normAlpha(); +} + +SColorRGB::SColorRGB(const SColorHCY &hcy) +{ + ColorSpaces::vec3 vhcy{{hcy.h, hcy.c, hcy.y}}; + ColorSpaces::vec3 vrgb = ColorSpaces::hcy_to_rgb(vhcy); + r = vrgb[0]; + g = vrgb[1]; + b = vrgb[2]; + a = hcy.a; +} + +CColor SColorRGB::toColor() const +{ + CColor cc; + cc.setNormRed(r); + cc.setNormGreen(g); + cc.setNormBlue(b); + cc.setNormAlpha(a); + return cc; +} + +SColorHCY::SColorHCY(const SColorRGB &rgb) +{ + ColorSpaces::vec3 vrgb{{rgb.r, rgb.g, rgb.b}}; + ColorSpaces::vec3 vhcy = ColorSpaces::rgb_to_hcy(vrgb); + h = vhcy[0]; + c = vhcy[1]; + y = vhcy[2]; + a = rgb.a; +} + +static int hexDigitFromChar(char c) +{ + return (c >= '0' && c <= '9') ? (c - '0') : + (c >= 'a' && c <= 'z') ? (c - 'a' + 10) : + (c >= 'A' && c <= 'Z') ? (c - 'A' + 10) : -1; +} + +bool colorFromHex(absl::string_view hex, CColor& color) +{ + if (hex.empty() || hex[0] != '#') + return false; + + hex = hex.substr(1, hex.size()); + size_t length = hex.size(); + uint32_t rgba = 0; + if (length == 6 || length == 8) { + for (size_t i = 0; i < length; ++i) { + int d = hexDigitFromChar(hex[i]); + if (d == -1) + return false; + + rgba = (rgba << 4) | d; + } + } + if (length == 6) + rgba = (rgba << 8) | 0xff; + + color.red = rgba >> 24; + color.green = (rgba >> 16) & 0xff; + color.blue = (rgba >> 8) & 0xff; + color.alpha = rgba & 0xff; + + return true; +} diff --git a/plugins/editor/src/editor/ColorHelpers.h b/plugins/editor/src/editor/ColorHelpers.h new file mode 100644 index 0000000..c79501c --- /dev/null +++ b/plugins/editor/src/editor/ColorHelpers.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "utility/vstgui_before.h" +#include "vstgui/lib/ccolor.h" +#include "utility/vstgui_after.h" +#include + +using namespace VSTGUI; + +struct SColorRGB; +struct SColorHCY; + +struct SColorRGB { + SColorRGB() = default; + explicit SColorRGB(const CColor &cc); + explicit SColorRGB(const SColorHCY &hcy); + SColorRGB(float r, float g, float b, float a = 1.0) : r(r), g(g), b(b), a(a) {} + CColor toColor() const; + + float r {}, g {}, b {}, a { 1.0 }; +}; + +struct SColorHCY { + SColorHCY() = default; + explicit SColorHCY(const CColor &cc) : SColorHCY(SColorRGB(cc)) {} + explicit SColorHCY(const SColorRGB &rgb); + SColorHCY(float h, float c, float y, float a = 1.0) : h(h), c(c), y(y), a(a) {} + CColor toColor() const { return SColorRGB(*this).toColor(); } + + float h {}, c {}, y {}, a { 1.0 }; +}; + +bool colorFromHex(absl::string_view hex, CColor& color); diff --git a/plugins/editor/src/editor/DlgAbout.cpp b/plugins/editor/src/editor/DlgAbout.cpp new file mode 100644 index 0000000..6bf5ed8 --- /dev/null +++ b/plugins/editor/src/editor/DlgAbout.cpp @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "DlgAbout.h" +#include "GUIComponents.h" +#include "GUIDefs.h" +#include "GitBuildId.h" +#include "NativeHelpers.h" + +#include "utility/vstgui_before.h" +#include "vstgui/vstgui.h" +#include "utility/vstgui_after.h" + +#include +#if 0 +#include +#include +#endif +using namespace gui; + +SAboutDialog::SAboutDialog(const CRect& bounds) + : CViewContainer(bounds) +{ + SharedPointer logo = owned(new CBitmap("logo_orange.png")); + setBackgroundColor(kColorControlsScrollerTransparency); + + CView* aboutView = nullptr; + { + auto createaboutView = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + CViewContainer* container = new CViewContainer(bounds); + container->setBackgroundColor(kColorTransparent); + return container; + }; + + auto createLogo = [&logo](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + CViewContainer* container = new CViewContainer(bounds); + container->setBackgroundColor(kColorTransparent); + container->setBackground(logo); + return container; + }; + + auto createInfoBox = [](const CRect& bounds, int, const char*, CHoriTxtAlign align, int fontsize) { + CMultiLineTextLabel* lbl = new CMultiLineTextLabel(bounds); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + lbl->setFontColor(kWhiteCColor); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setHoriAlign(align); + + const char* version = SFIZZ_VERSION; + std::string versionBuf; + if (GitBuildId[0]) { + versionBuf = absl::StrCat(SFIZZ_VERSION ".", GitBuildId); + version = versionBuf.c_str(); + } +#if 0 + absl::TimeZone utc = absl::UTCTimeZone(); + absl::Time time = absl::Now(); + absl::CivilYear date = absl::ToCivilYear(time, utc); + // u8"Copyright 2019-", date.year(), u8" by SFZTools Team,\n" +#endif + lbl->setText(absl::StrCat( + u8"Version ", version, u8"\n" + u8"Copyright 2019-2023 by SFZTools Team,\n" + u8"licensed under BSD 2-clause license.")); + return lbl; + }; + + auto createDescriptionBox = [](const CRect& bounds, int, const char*, CHoriTxtAlign align, int fontsize) { + CMultiLineTextLabel* lbl = new CMultiLineTextLabel(bounds); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + lbl->setFontColor(kWhiteCColor); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setHoriAlign(align); + lbl->setText(absl::StrCat( + u8"Maintainers: Paul Ferrand, Jean-Pierre Cimalando\n" + u8"Contributors: Andrea Zanellato, Alexander Mitchell, Michael Willis,\n" + u8"Tobiasz \"unfa\" Karoń, Kinwie, Atsushi Eno, Dominique Würtz,\n" + u8"Even Brenden et al.")); + return lbl; + }; + + auto createHoverBox = [](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + auto font = makeOwned("Roboto", fontsize); + lbl->setFontColor(kColorOrange); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setHoriAlign(align); + lbl->setFont(font); + return lbl; + }; + + auto createSysInfoKeyBox = [](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CMultiLineTextLabel* lbl = new CMultiLineTextLabel(bounds); + auto font = makeOwned("Roboto", fontsize, kBoldFace); + lbl->setFont(font); + lbl->setFontColor(kColorOrange); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setHoriAlign(align); + lbl->setText(label); + return lbl; + }; + + auto createSysInfoValueBox = [](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CMultiLineTextLabel* lbl = new CMultiLineTextLabel(bounds); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + lbl->setFontColor(kWhiteCColor); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setHoriAlign(align); + lbl->setText(label); + return lbl; + }; + + auto createGlyphButton = [this](UTF8StringPtr glyph, const CRect& bounds, int tag, int fontsize) { + STextButton* btn = new STextButton(bounds, this, tag, glyph); + btn->setFont(makeOwned("Sfizz Misc Icons", fontsize)); + btn->setTextColor(kWhiteCColor); + btn->setHighlightColor(kColorOrange); + btn->setFrameColor(kColorTransparent); + btn->setFrameColorHighlighted(kColorTransparent); + btn->setGradient(nullptr); + btn->setGradientHighlighted(nullptr); + return btn; + }; + auto createButtonSfztools = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createGlyphButton(u8"\ue000", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "SFZ Tools"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createButtonGithub = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createGlyphButton(u8"\ue001", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Source code"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createButtonDiscord = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createGlyphButton(u8"\ue002", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Community chat"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createButtonOpencollective = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createGlyphButton(u8"\ue003", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Support us"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createButtonSfzformat = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createGlyphButton(u8"\ue004", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "SFZ Format"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + + #include "layout/about.hpp" + } + addView(aboutView); + + CRect aboutBounds = aboutView->getViewSize(); + aboutBounds.centerInside(CRect(bounds).originize()); + aboutView->setViewSize(aboutBounds); + + /// + sysInfoTemplate_ = lblSysInfoValue_->getText(); + sysInfoVariables_["%Pluginformat%"] = {}; + sysInfoVariables_["%HostOS%"] = getOperatingSystemName(); + sysInfoVariables_["%HostCPU%"] = getProcessorName(); + sysInfoVariables_["%HostBits%"] = std::to_string(8 * sizeof(void*)); + sysInfoVariables_["%HostProgram%"] = getCurrentProcessName(); + updateSysInfo(); +} + +void SAboutDialog::setPluginFormat(const std::string& pluginFormat) +{ + sysInfoVariables_["%PluginFormat%"] = pluginFormat; + updateSysInfo(); +} + +void SAboutDialog::setPluginHost(const std::string& pluginHost) +{ + sysInfoVariables_["%HostProgram%"] = pluginHost; + updateSysInfo(); +} + +void SAboutDialog::updateSysInfo() +{ + std::string text = sysInfoTemplate_; + for (const auto& infoKeyValue : sysInfoVariables_) { + size_t pos = text.find(infoKeyValue.first); + if (pos != text.npos) + text.replace(pos, infoKeyValue.first.size(), infoKeyValue.second); + } + lblSysInfoValue_->setText(UTF8String(text)); +} + +void SAboutDialog::buttonHoverEnter(CControl* btn, const char* text) +{ + CRect rect = lblHover_->getViewSize(); + CRect btnRect = btn->getViewSize(); + rect.left = btnRect.left - 100; + rect.right = btnRect.right + 100; + lblHover_->setViewSize(rect); + + lblHover_->setText(text); + lblHover_->setVisible(true); + lblHover_->invalid(); +} + +void SAboutDialog::buttonHoverLeave(CControl* btn) +{ + (void)btn; + lblHover_->setVisible(false); +} + +CMouseEventResult SAboutDialog::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + CMouseEventResult result = CViewContainer::onMouseDown(where, buttons); + + if (result != kMouseEventHandled) { + setVisible(false); + result = kMouseEventHandled; + } + + return result; +} + +#if VSTGUI_MORE_THAN_4_10 +void SAboutDialog::onKeyboardEvent (KeyboardEvent& event, CFrame* frame) +{ + auto vstKeyCode = toVstKeyCode (event); + if (event.type == EventType::KeyDown && vstKeyCode.virt == VKEY_ESCAPE) + { + setVisible(false); + frame->unregisterKeyboardHook(this); + event.consumed = true; + } +} +#else +int32_t SAboutDialog::onKeyDown (const VstKeyCode& keyCode, CFrame* frame) +{ + if (keyCode.virt == VKEY_ESCAPE) { + setVisible(false); + frame->unregisterKeyboardHook(this); + return 1; + } + + return -1; +} + +int32_t SAboutDialog::onKeyUp (const VstKeyCode& keyCode, CFrame* frame) +{ + (void)keyCode; + (void)frame; + return -1; +} +#endif + +void SAboutDialog::valueChanged(CControl *ctl) +{ + int32_t tag = ctl->getTag(); + float value = ctl->getValue(); + + switch (tag) { + case kTagButtonSfztools: + if (value != 1) + break; + + Call::later([]() { + openURLWithExternalProgram("https://sfz.tools/sfizz/"); + }); + break; + + case kTagButtonGithub: + if (value != 1) + break; + + Call::later([]() { + openURLWithExternalProgram("https://github.com/sfztools/sfizz"); + }); + break; + + case kTagButtonDiscord: + if (value != 1) + break; + + Call::later([]() { + openURLWithExternalProgram("https://discord.gg/3ArE9Mw"); + }); + break; + + case kTagButtonOpencollective: + if (value != 1) + break; + + Call::later([]() { + openURLWithExternalProgram("https://opencollective.com/sfztools"); + }); + break; + + case kTagButtonSfzformat: + if (value != 1) + break; + + Call::later([]() { + openURLWithExternalProgram("https://sfzformat.com/"); + }); + break; + + default: + break; + } +} diff --git a/plugins/editor/src/editor/DlgAbout.h b/plugins/editor/src/editor/DlgAbout.h new file mode 100644 index 0000000..4f36c21 --- /dev/null +++ b/plugins/editor/src/editor/DlgAbout.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "utility/vstgui_before.h" +#include "vstgui/lib/cviewcontainer.h" +#include "vstgui/vstgui.h" +#include "utility/vstgui_after.h" +#include + +#define VSTGUI_MORE_THAN_4_10 ((VSTGUI_VERSION_MAJOR > 4) \ + || (VSTGUI_VERSION_MAJOR == 4 && VSTGUI_VERSION_MINOR > 10)) + +using namespace VSTGUI; + +class SAboutDialog : public CViewContainer, public IControlListener, public IKeyboardHook { + + enum { + kTagButtonSfztools, + kTagButtonGithub, + kTagButtonDiscord, + kTagButtonOpencollective, + kTagButtonSfzformat + }; + +public: + explicit SAboutDialog(const CRect& bounds); + + void setPluginFormat(const std::string& pluginFormat); + void setPluginHost(const std::string& pluginHost); + +#if VSTGUI_MORE_THAN_4_10 + void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override; +#else + int32_t onKeyDown(const VstKeyCode& code, CFrame* frame) override; + int32_t onKeyUp(const VstKeyCode& code, CFrame* frame) override; +#endif + +protected: + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; + + // IControlListener + void valueChanged(CControl* ctl) override; + +private: + void updateSysInfo(); + + void buttonHoverEnter(CControl* btn, const char* text); + void buttonHoverLeave(CControl* btn); + + CTextLabel* lblHover_ = {}; + CTextLabel* lblSysInfoValue_ = {}; + std::string sysInfoTemplate_; + std::map sysInfoVariables_; +}; diff --git a/plugins/editor/src/editor/EditIds.cpp b/plugins/editor/src/editor/EditIds.cpp new file mode 100644 index 0000000..34ea4e1 --- /dev/null +++ b/plugins/editor/src/editor/EditIds.cpp @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "EditIds.h" + +EditRange EditRange::get(EditId id) +{ + switch (id) { + default: + assert(false); + return {}; + case EditId::Volume: + return { 0, -60, 6 }; + case EditId::Polyphony: + return { 64, 1, 256 }; + case EditId::Oversampling: + return { 0, 0, 3 }; + case EditId::PreloadSize: + return { 8192, 1024, 65536 }; + case EditId::ScalaRootKey: + return { 60, 0, 127 }; + case EditId::TuningFrequency: + return { 440, 300, 500 }; + case EditId::StretchTuning: + return { 0, 0, 1 }; + case EditId::SampleQuality: + return { 2, 0, 10 }; + case EditId::OscillatorQuality: + return { 1, 0, 3 }; + case EditId::FreewheelingSampleQuality: + return { 10, 0, 10 }; + case EditId::FreewheelingOscillatorQuality: + return { 3, 0, 3 }; + case EditId::SustainCancelsRelease: + return { 0, 0, 1 }; + case EditId::UIActivePanel: + return { 0, 0, 255 }; + case EditId::UIZoom: + return { 100, 100, 300 }; + } +} diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h new file mode 100644 index 0000000..f6f6c1a --- /dev/null +++ b/plugins/editor/src/editor/EditIds.h @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "sfizz/Config.h" +#include + +enum class EditId : int { + SfzFile, + Volume, + Polyphony, + Oversampling, + PreloadSize, + ScalaFile, + ScalaRootKey, + TuningFrequency, + StretchTuning, + SampleQuality, + OscillatorQuality, + FreewheelingSampleQuality, + FreewheelingOscillatorQuality, + SustainCancelsRelease, + CanEditUserFilesDir, + UserFilesDir, + FallbackFilesDir, + // + #define KEY_RANGE(Name) Name##0, Name##Last = Name##0 + 128 - 1 + #define CC_RANGE(Name) Name##0, Name##Last = Name##0 + sfz::config::numCCs - 1 + #define METER_RANGE(Name) Name##0, Name##Last = Name##0 + 16 - 1 + // + KEY_RANGE(Key), + CC_RANGE(Controller), + // + KEY_RANGE(KeyUsed), + KEY_RANGE(KeyLabel), + KEY_RANGE(KeyswitchUsed), + KEY_RANGE(KeyswitchLabel), + CC_RANGE(ControllerUsed), + CC_RANGE(ControllerDefault), + CC_RANGE(ControllerLabel), + // + METER_RANGE(Level), + // + UINumCurves, + UINumMasters, + UINumGroups, + UINumRegions, + UINumPreloadedSamples, + UINumActiveVoices, + UIActivePanel, + UIZoom, + // + BackgroundImage, + ControlsImage, + // + PluginFormat, + PluginHost, + PluginOutputs, + // + #undef KEY_RANGE + #undef CC_RANGE + #undef METER_RANGE +}; + +struct EditRange { + float def = 0.0; + float min = 0.0; + float max = 1.0; + constexpr EditRange() = default; + constexpr EditRange(float def, float min, float max) + : def(def), min(min), max(max) {} + float extent() const noexcept { return max - min; } + static EditRange get(EditId id); +}; + +#define DEFINE_EDIT_ID_RANGE_HELPERS(type, Type, IdPrefix) \ + inline bool editIdIs##Type(EditId id) \ + { \ + return int(id) >= int(EditId::IdPrefix##0) && \ + int(id) <= int(EditId::IdPrefix##Last); \ + } \ + inline EditId editIdFor##Type(int value) \ + { \ + EditId id = EditId(int(EditId::IdPrefix##0) + value); \ + assert(editIdIs##Type(id)); \ + return id; \ + } \ + inline int type##ForEditId(EditId id) \ + { \ + assert(editIdIs##Type(id)); \ + return int(id) - int(EditId::IdPrefix##0); \ + } + +// defines editIdForCC, ccForEditId, editIdIsCC, etc.. +DEFINE_EDIT_ID_RANGE_HELPERS(cc, CC, Controller) +DEFINE_EDIT_ID_RANGE_HELPERS(level, Level, Level) +DEFINE_EDIT_ID_RANGE_HELPERS(key, Key, Key) +DEFINE_EDIT_ID_RANGE_HELPERS(keyUsed, KeyUsed, KeyUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(keyLabel, KeyLabel, KeyLabel) +DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchUsed, KeyswitchUsed, KeyswitchUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchLabel, KeyswitchLabel, KeyswitchLabel) +DEFINE_EDIT_ID_RANGE_HELPERS(ccUsed, CCUsed, ControllerUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(ccDefault, CCDefault, ControllerDefault) +DEFINE_EDIT_ID_RANGE_HELPERS(ccLabel, CCLabel, ControllerLabel) + +#undef DEFINE_EDIT_ID_RANGE_HELPERS diff --git a/plugins/editor/src/editor/EditValue.h b/plugins/editor/src/editor/EditValue.h new file mode 100644 index 0000000..b8d911d --- /dev/null +++ b/plugins/editor/src/editor/EditValue.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +#include + +class EditValue { +public: + constexpr EditValue() : tag(Nil) {} + EditValue(float value) { reset(value); } + EditValue(std::string value) { reset(value); } + ~EditValue() { reset(); } + + void reset() noexcept + { + if (tag == String) + destruct(u.s); + tag = Nil; + } + + void reset(float value) noexcept + { + reset(); + u.f = value; + tag = Float; + } + + void reset(std::string value) noexcept + { + reset(); + new (&u.s) std::string(std::move(value)); + tag = String; + } + + float to_float() const + { + if (tag != Float) + throw std::runtime_error("the tagged union does not contain `float`"); + return u.f; + } + + const std::string& to_string() const + { + if (tag != String) + throw std::runtime_error("the tagged union does not contain `string`"); + return u.s; + } + +private: + template static void destruct(T& obj) { obj.~T(); } + +private: + enum TypeTag { Nil, Float, String }; + union Union { + constexpr explicit Union(float f = 0.0f) noexcept : f(f) {} + ~Union() noexcept {} + float f; + std::string s; + }; + TypeTag tag { Nil }; + Union u; +}; diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp new file mode 100644 index 0000000..81e5d37 --- /dev/null +++ b/plugins/editor/src/editor/Editor.cpp @@ -0,0 +1,2345 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "Editor.h" +#include "EditorController.h" +#include "EditIds.h" +#include "GUIDefs.h" +#include "GUIComponents.h" +#include "GUIHelpers.h" +#include "GUIPiano.h" +#include "GitBuildId.h" +#include "DlgAbout.h" +#include "ImageHelpers.h" +#include "NativeHelpers.h" +#include "VSTGUIHelpers.h" +#include "BitArray.h" +#include "Theme.h" +#include "plugin/MessageUtils.h" +#include "plugin/SfizzSettings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utility/vstgui_before.h" +#include "vstgui/vstgui.h" +#include "utility/vstgui_after.h" + +using namespace VSTGUI; +using namespace gui; + +const int Editor::viewWidth { 775 }; +const int Editor::viewHeight { 515 }; + +struct Editor::Impl : EditorController::Receiver, + public IControlListener, + public Theme::ChangeListener +{ + EditorController* ctrl_ = nullptr; + CFrame* frame_ = nullptr; + SharedPointer frameDisabler_; + SharedPointer mainView_; + + std::string currentSfzFile_; + std::string currentScalaFile_; + std::string currentThemeName_; + std::string userFilesDir_; + std::string fallbackFilesDir_; + bool multi_ { false }; + + float zoom_ = 1.0f; + + int currentKeyswitch_ = -1; + std::unordered_map keyswitchNames_; + + SharedPointer memQueryTimer_; + + enum { + kPanelGeneral, + kPanelInfo, + kPanelControls, + kPanelSettings, + kNumPanels, + }; + + unsigned activePanel_ = 0; + CViewContainer* subPanels_[kNumPanels] = {}; + STextButton* panelButtons_[kNumPanels] = {}; + + enum { + kTagLoadSfzFile, + kTagEditSfzFile, + kTagCreateNewSfzFile, + kTagOpenSfzFolder, + kTagPreviousSfzFile, + kTagNextSfzFile, + kTagFileOperations, + kTagSetMainVolume, + kTagSetNumVoices, + kTagSetOversampling, + kTagSetPreloadSize, + kTagLoadScalaFile, + kTagResetScalaFile, + kTagSetScalaRootKey, + kTagSetTuningFrequency, + kTagSetStretchedTuning, + kTagSetSampleQuality, + kTagSetOscillatorQuality, + kTagSetFreewheelingSampleQuality, + kTagSetFreewheelingOscillatorQuality, + kTagSetSustainCancelsRelease, + kTagSetCCVolume, + kTagSetCCPan, + kTagChooseUserFilesDir, + kTagAbout, + kTagThemeMenu, + kTagZoomMenu, + kTagSetDefaultZoom, + kTagFirstChangePanel, + kTagLastChangePanel = kTagFirstChangePanel + kNumPanels - 1, + }; + + STextButton* sfzFileLabel_ = nullptr; + CTextLabel* scalaFileLabel_ = nullptr; + STextButton* scalaFileButton_ = nullptr; + STextButton* scalaResetButton_ = nullptr; + CControl *volumeSlider_ = nullptr; + CTextLabel* volumeLabel_ = nullptr; + SValueMenu *numVoicesSlider_ = nullptr; + CTextLabel* numVoicesLabel_ = nullptr; + SValueMenu *oversamplingSlider_ = nullptr; + CTextLabel* oversamplingLabel_ = nullptr; + SValueMenu *preloadSizeSlider_ = nullptr; + CTextLabel* preloadSizeLabel_ = nullptr; + SValueMenu *scalaRootKeySlider_ = nullptr; + SValueMenu *scalaRootOctaveSlider_ = nullptr; + CTextLabel* scalaRootKeyLabel_ = nullptr; + SValueMenu* tuningFrequencyDropdown_ = nullptr; + CTextEdit* tuningFrequencyEdit_ = nullptr; + CTextLabel* tuningFrequencyLabel_ = nullptr; + CControl *stretchedTuningSlider_ = nullptr; + CTextLabel* stretchedTuningLabel_ = nullptr; + SValueMenu *sampleQualitySlider_ = nullptr; + SValueMenu *oscillatorQualitySlider_ = nullptr; + SValueMenu *freewheelingSampleQualitySlider_ = nullptr; + SValueMenu *freewheelingOscillatorQualitySlider_ = nullptr; + CCheckBox *sustainCancelsReleaseCheckbox_ = nullptr; + CTextLabel* keyswitchLabel_ = nullptr; + CTextLabel* keyswitchInactiveLabel_ = nullptr; + CTextLabel* keyswitchBadge_ = nullptr; + CTextLabel* lblHover_ = nullptr; + COptionMenu* themeMenu_ = nullptr; + COptionMenu* zoomMenu_ = nullptr; + STextButton* defaultZoomButton_ = nullptr; + std::unique_ptr theme_; + + STitleContainer* userFilesGroup_ = nullptr; + STextButton* userFilesDirButton_ = nullptr; + + CTextLabel* infoCurvesLabel_ = nullptr; + CTextLabel* infoMastersLabel_ = nullptr; + CTextLabel* infoGroupsLabel_ = nullptr; + CTextLabel* infoRegionsLabel_ = nullptr; + CTextLabel* infoSamplesLabel_ = nullptr; + CTextLabel* infoVoicesLabel_ = nullptr; + + CViewContainer* imageContainer_ = nullptr; + + CTextLabel* memoryLabel_ = nullptr; + + SActionMenu* fileOperationsMenu_ = nullptr; + + SPiano* piano_ = nullptr; + + SControlsPanel* controlsPanel_ = nullptr; + + SKnobCCBox* volumeCCKnob_ = nullptr; + SKnobCCBox* panCCKnob_ = nullptr; + + SLevelMeter* meters_[16] {}; + SAboutDialog* aboutDialog_ = nullptr; + + SharedPointer backgroundBitmap_; + SharedPointer defaultBackgroundBitmap_; + SharedPointer controlsBitmap_; + + CTextLabel* sfizzVersionLabel_ = nullptr; + + SKnobCCBox* getSecondaryCCKnob(unsigned cc) + { + switch (cc) { + case 7: return volumeCCKnob_ ? volumeCCKnob_ : nullptr; + case 10: return panCCKnob_ ? panCCKnob_ : nullptr; + default: return nullptr; + } + } + + void uiReceiveValue(EditId id, const EditValue& v) override; + void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override; + + // queued OSC API; sends OSC with intermediate delay between messages + // to prevent message bursts overloading the buffer + void sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args); + void clearQueuedOSC(); + void tickOSCQueue(CVSTGUITimer* timer); + std::queue oscSendQueue_; + SharedPointer oscSendQueueTimer_; + + void createFrameContents(); + SLevelMeter* createVMeter(const CRect& bounds, int, const char*, CHoriTxtAlign, int); + + template + void adjustMinMaxToEditRange(Control* c, EditId id) + { + if (!c) + return; + const EditRange er = EditRange::get(id); + c->setMin(er.min); + c->setMax(er.max); + c->setDefaultValue(er.def); + } + + void chooseSfzFile(); + void createNewSfzFile(); + void changeSfzFile(const std::string& filePath); + void changeToNextSfzFile(long offset); + void chooseScalaFile(); + void changeScalaFile(const std::string& filePath); + void chooseUserFilesDir(); + std::string getFileChooserInitialDir(const std::string& previousFilePath) const; + + static bool scanDirectoryFiles(const fs::path& dirPath, std::function filter, std::vector& fileNames); + + static absl::string_view simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty); + + void updateSfzFileLabel(const std::string& filePath); + void updateScalaFileLabel(const std::string& filePath); + void updateUserFilesDirLabel(const std::string& filePath); + static void updateLabelWithFileName(CTextLabel* label, const std::string& filePath, absl::string_view removedSuffix); + static void updateButtonWithFileName(STextButton* button, const std::string& filePath, absl::string_view removedSuffix); + static void updateSButtonWithFileName(STextButton* button, const std::string& filePath, absl::string_view removedSuffix); + void updateVolumeLabel(float volume); + void updateNumVoicesLabel(int numVoices); + void updateOversamplingLabel(int oversamplingLog2); + void updatePreloadSizeLabel(int preloadSize); + void updateScalaRootKeyLabel(int rootKey); + void updateStretchedTuningLabel(float stretchedTuning); + void buttonHoverEnter(CControl* btn, const char* text); + void buttonHoverLeave(CControl* btn); + + absl::string_view getCurrentKeyswitchName() const; + void updateKeyswitchNameLabel(); + + void updateKeyUsed(unsigned key, bool used); + void updateKeyLabel(unsigned key, const char* label); + void updateKeyswitchUsed(unsigned key, bool used); + void updateCCUsed(unsigned cc, bool used); + void updateCCValue(unsigned cc, float value); + void updateCCDefaultValue(unsigned cc, float value); + void updateCCLabel(unsigned cc, const char* label); + void updateSWLastCurrent(int sw); + void updateSWLastLabel(unsigned sw, const char* label); + void updateBackgroundImage(const char* filepath); + void updateControlsImage(const char* filepath); + void updateMemoryUsed(uint64_t mem); + + // edition of CC by UI + void performCCValueChange(unsigned cc, float value); + void performCCBeginEdit(unsigned cc); + void performCCEndEdit(unsigned cc); + + void setActivePanel(unsigned panelId); + void setupCurrentPanel(); + void applyBackgroundForCurrentPanel(); + + static void formatLabel(CTextLabel* label, const char* fmt, ...); + static void vformatLabel(CTextLabel* label, const char* fmt, va_list ap); + + // IControlListener + void valueChanged(CControl* ctl) override; + void enterOrLeaveEdit(CControl* ctl, bool enter); + void controlBeginEdit(CControl* ctl) override; + void controlEndEdit(CControl* ctl) override; + + // Theme + void onThemeChanged() override; + std::vector> OnThemeChanged; + + // Zoom + void setZoom(int zoom); + + // Misc + static std::string getUnicodeNoteName(unsigned key) + { + const char* keyNames[12] = { + u8"C", u8"C♯", u8"D", u8"D♯", u8"E", + u8"F", u8"F♯", u8"G", u8"G♯", u8"A", u8"A♯", u8"B", + }; + int octave = static_cast(key / 12) - 1; + const char* keyName = keyNames[key % 12]; + return std::string(keyName) + ' ' + std::to_string(octave); + } +}; + +Editor::Editor(EditorController& ctrl) + : impl_(new Impl) +{ + Impl& impl = *impl_; + + impl.ctrl_ = &ctrl; + + ctrl.decorate(&impl); + + impl.createFrameContents(); +} + +Editor::~Editor() +{ + Impl& impl = *impl_; + + close(); + + EditorController& ctrl = *impl.ctrl_; + ctrl.decorate(nullptr); +} + +void Editor::open(CFrame& frame) +{ + Impl& impl = *impl_; + + fprintf(stderr, "[sfizz] The resource path of the bundle is %s\n", + getResourceBasePath().u8string().c_str()); + + impl.frame_ = &frame; + + frame.addView(impl.mainView_.get()); + + SfizzSettings settings; + int zoom = atoi(settings.load_or("default_zoom", "100").c_str()); + impl.setZoom(zoom); + fprintf(stderr, "[sfizz] zoom factor: %f\n", impl.frame_->getZoom()); + + impl.frameDisabler_ = makeOwned(&frame); + + impl.memQueryTimer_ = makeOwned([this](CVSTGUITimer*) { + impl_->sendQueuedOSC("/mem/buffers", "", nullptr); + }, 1000, true); + + uint32_t oscSendInterval = 1; // milliseconds + impl.oscSendQueueTimer_ = makeOwned( + [this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); }, + oscSendInterval, false); +} + +void Editor::close() +{ + Impl& impl = *impl_; + + impl.clearQueuedOSC(); + impl.oscSendQueueTimer_ = nullptr; + + impl.memQueryTimer_ = nullptr; + + impl.frameDisabler_ = nullptr; + + if (impl.frame_) { + impl.frame_->removeView(impl.mainView_.get(), false); + impl.frame_ = nullptr; + } +} + +SLevelMeter* Editor::Impl::createVMeter(const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + SLevelMeter* meter = new SLevelMeter(bounds); + Palette* palette = &theme_->invertedPalette; + meter->setFrameColor(kColorTransparent); + meter->setNormalFillColor(kColorMeterNormal); + meter->setDangerFillColor(kColorMeterDanger); + meter->setBackColor(palette->knobInactiveTrack); + return meter; +}; + +void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) +{ + switch (id) { + case EditId::SfzFile: + { + const std::string& value = v.to_string(); + currentSfzFile_ = value; + updateSfzFileLabel(value); + } + break; + case EditId::Volume: + { + const float value = v.to_float(); + if (volumeSlider_) + volumeSlider_->setValue(value); + updateVolumeLabel(value); + } + break; + case EditId::Polyphony: + { + const int value = static_cast(v.to_float()); + if (numVoicesSlider_) + numVoicesSlider_->setValue(value); + updateNumVoicesLabel(value); + } + break; + case EditId::Oversampling: + { + const int value = static_cast(v.to_float()); + + int log2Value = 0; + for (int f = value; f > 1; f /= 2) + ++log2Value; + + if (oversamplingSlider_) + oversamplingSlider_->setValue(log2Value); + updateOversamplingLabel(log2Value); + } + break; + case EditId::PreloadSize: + { + const int value = static_cast(v.to_float()); + if (preloadSizeSlider_) + preloadSizeSlider_->setValue(value); + updatePreloadSizeLabel(value); + } + break; + case EditId::ScalaFile: + { + const std::string& value = v.to_string(); + currentScalaFile_ = value; + updateScalaFileLabel(value); + } + break; + case EditId::ScalaRootKey: + { + const int value = std::max(0, static_cast(v.to_float())); + if (scalaRootKeySlider_) + scalaRootKeySlider_->setValue(value % 12); + if (scalaRootOctaveSlider_) + scalaRootOctaveSlider_->setValue(value / 12); + updateScalaRootKeyLabel(value); + } + break; + case EditId::TuningFrequency: + { + const float value = v.to_float(); + if (tuningFrequencyEdit_) + tuningFrequencyEdit_->setValue(value); + } + break; + case EditId::StretchTuning: + { + const float value = v.to_float(); + if (stretchedTuningSlider_) + stretchedTuningSlider_->setValue(value); + updateStretchedTuningLabel(value); + } + break; + case EditId::SampleQuality: + { + const int value = static_cast(v.to_float()); + if (CControl* slider = sampleQualitySlider_) { + slider->setValue(float(value)); + slider->invalid(); + } + } + break; + case EditId::OscillatorQuality: + { + const int value = static_cast(v.to_float()); + if (CControl* slider = oscillatorQualitySlider_) { + slider->setValue(float(value)); + slider->invalid(); + } + } + break; + case EditId::FreewheelingSampleQuality: + { + const int value = static_cast(v.to_float()); + if (CControl* slider = freewheelingSampleQualitySlider_) { + slider->setValue(float(value)); + slider->invalid(); + } + } + break; + case EditId::FreewheelingOscillatorQuality: + { + const int value = static_cast(v.to_float()); + if (CControl* slider = freewheelingOscillatorQualitySlider_) { + slider->setValue(float(value)); + slider->invalid(); + } + } + break; + case EditId::SustainCancelsRelease: + { + const bool value = v.to_float(); + if (CControl* checkbox = sustainCancelsReleaseCheckbox_) { + checkbox->setValue(value); + checkbox->invalid(); + } + } + break; + case EditId::CanEditUserFilesDir: + { + if (STitleContainer* group = userFilesGroup_) + group->setVisible(v.to_float()); + break; + } + case EditId::UserFilesDir: + { + userFilesDir_ = v.to_string(); + updateUserFilesDirLabel(userFilesDir_); + break; + } + case EditId::FallbackFilesDir: + { + fallbackFilesDir_ = v.to_string(); + break; + } + case EditId::PluginFormat: + aboutDialog_->setPluginFormat(v.to_string()); + break; + case EditId::PluginHost: + aboutDialog_->setPluginHost(v.to_string()); + break; + case EditId::UINumCurves: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoCurvesLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UINumMasters: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoMastersLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UINumGroups: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoGroupsLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UINumRegions: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoRegionsLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UINumPreloadedSamples: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoSamplesLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UINumActiveVoices: + { + const int value = static_cast(v.to_float()); + if (CTextLabel* label = infoVoicesLabel_) + formatLabel(label, "%u", value); + } + break; + case EditId::UIActivePanel: + { + const int value = static_cast(v.to_float()); + setActivePanel(value); + } + break; + case EditId::BackgroundImage: + { + const std::string& value = v.to_string(); + updateBackgroundImage(value.c_str()); + } + break; + case EditId::ControlsImage: + { + const std::string& value = v.to_string(); + updateControlsImage(value.c_str()); + } + break; + case EditId::PluginOutputs: + { + const int value = static_cast(v.to_float()); + if (!meters_[0]) + return; + + const auto firstMeterRect = meters_[0]->getViewSize(); + const auto minLeft = firstMeterRect.left; + auto maxRight = firstMeterRect.right; + auto numMeters = 1; + for (; numMeters < 16; ++numMeters) { + if (!meters_[numMeters]) + break; + + maxRight = meters_[numMeters]->getViewSize().right; + } + const auto meterWidth = std::max(1.0, (maxRight - minLeft - value + 1) / value); + + auto meterParent = meters_[0]->getParentView()->asViewContainer(); + const auto roundRectRadius = [value] { + if (value < 4) + return 5.0; + else if (value < 8) + return 3.0; + else if (value < 16) + return 1.0; + else + return 0.0; + }(); + + for (int i = 0; i < 16; ++i) { + if (meters_[i]) { + meterParent->removeView(meters_[i]); + } + + if (i < value) { + double left { minLeft + i * (meterWidth + 1) }; + CRect rect { left, firstMeterRect.top, left + meterWidth, firstMeterRect.bottom }; + meters_[i] = createVMeter(rect, -1, "", kCenterText, 14); + meters_[i]->setRoundRectRadius(roundRectRadius); + meterParent->addView(meters_[i]); + } + } + break; + } + default: + if (editIdIsKey(id)) { + const int key = keyForEditId(id); + const float value = v.to_float(); + if (SPiano* piano = piano_) + piano->setKeyValue(key, value); + } + else if (editIdIsKeyUsed(id)) { + updateKeyUsed(keyUsedForEditId(id), v.to_float() != 0); + } + else if (editIdIsKeyLabel(id)) { + updateKeyLabel(keyLabelForEditId(id), v.to_string().c_str()); + } + else if (editIdIsKeyswitchUsed(id)) { + updateKeyswitchUsed(keyswitchUsedForEditId(id), v.to_float() != 0); + } + else if (editIdIsKeyswitchLabel(id)) { + updateSWLastLabel(keyswitchLabelForEditId(id), v.to_string().c_str()); + } + else if (editIdIsCC(id)) { + updateCCValue(unsigned(ccForEditId(id)), v.to_float()); + } + else if (editIdIsCCUsed(id)) { + bool used = v.to_float() != 0; + updateCCUsed(ccUsedForEditId(id), used); + } + else if (editIdIsCCDefault(id)) { + updateCCDefaultValue(ccDefaultForEditId(id), v.to_float()); + } + else if (editIdIsCCLabel(id)) { + updateCCLabel(ccLabelForEditId(id), v.to_string().c_str()); + } + else if (editIdIsLevel(id)) { + const float value = v.to_float(); + if (SLevelMeter* meter = meters_[levelForEditId(id)]) + meter->setValue(value); + } + break; + } +} + +void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) +{ + unsigned indices[8]; + + if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) { + updateSWLastCurrent(args[0].i); + } + else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "N")) { + updateSWLastCurrent(-1); + } + else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) { + updateMemoryUsed(args[0].h); + } + else { + //fprintf(stderr, "Receive unhandled OSC: %s\n", path); + } +} + +void Editor::Impl::sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args) +{ + if (!frame_) + return; + + uint32_t oscSize = sfizz_prepare_message(nullptr, 0, path, sig, args); + std::string oscData(oscSize, '\0'); + sfizz_prepare_message(&oscData[0], oscSize, path, sig, args); + oscSendQueue_.push(std::move(oscData)); + oscSendQueueTimer_->start(); +} + +void Editor::Impl::clearQueuedOSC() +{ + while (!oscSendQueue_.empty()) + oscSendQueue_.pop(); +} + +void Editor::Impl::tickOSCQueue(CVSTGUITimer* timer) +{ + if (oscSendQueue_.empty()) { + timer->stop(); + return; + } + const std::string& msg = oscSendQueue_.front(); + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + if (sfizz_extract_message(msg.data(), msg.size(), buffer, sizeof(buffer), &path, &sig, &args) > 0) + ctrl_->uiSendMessage(path, sig, args); + oscSendQueue_.pop(); +} + +void Editor::Impl::createFrameContents() +{ + CViewContainer* mainView; + Theme* theme; + + SharedPointer backgroundAbout = owned(new CBitmap("background_button_about.png")); + SharedPointer background = owned(new CBitmap("background.png")); + SharedPointer knob48 = owned(new CBitmap("knob48.png")); + + // TODO: is this used somewhere? + SharedPointer logoText = owned(new CBitmap("logo_text.png")); + + defaultBackgroundBitmap_ = background; + backgroundBitmap_ = background; + controlsBitmap_ = nullptr; + + { + theme = new Theme; + theme_.reset(theme); + + theme->listener = this; + OnThemeChanged.clear(); + OnThemeChanged.reserve(128); + + Palette& invertedPalette = theme->invertedPalette; + Palette& defaultPalette = theme->normalPalette; + Palette* palette = &defaultPalette; + auto enterPalette = [&palette](Palette& p) { palette = &p; }; + + auto createLogicalGroup = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + CViewContainer* container = new CViewContainer(bounds); + container->setBackgroundColor(kColorTransparent); + return container; + }; + auto createRoundedGroup = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + auto* box = new SBoxContainer(bounds); + box->setCornerRadius(10.0); + OnThemeChanged.push_back([box, palette]() { + box->setBackgroundColor(palette->boxBackground); + }); + return box; + }; + auto createSquaredGroup = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + auto* box = new CViewContainer(bounds); + OnThemeChanged.push_back([box, palette]() { + box->setBackgroundColor(palette->boxBackground); + }); + return box; + }; + auto createSquaredTransparentGroup = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + auto* box = new CViewContainer(bounds); + box->setBackgroundColor(kColorInfoTransparency); + return box; + }; +#if 0 + auto createTitleGroup = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign, int fontsize) { + auto* box = new STitleContainer(bounds, label); + box->setCornerRadius(10.0); + OnThemeChanged.push_back([box, palette]() { + box->setBackgroundColor(palette->boxBackground); + box->setTitleFontColor(palette->titleBoxText); + box->setTitleBackgroundColor(palette->titleBoxBackground); + }); + auto font = makeOwned("Roboto", fontsize); + box->setTitleFont(font); + return box; + }; +#endif + auto createAboutButton = [this, &backgroundAbout]( + const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) { + SHoverButton* btn = new SHoverButton(bounds, this, tag, backgroundAbout); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "About sfizz..."); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createLabel = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + OnThemeChanged.push_back([lbl, palette]() { + lbl->setFontColor(palette->text); + }); + lbl->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + return lbl; + }; + auto createInfoLabel = [](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + lbl->setFontColor(kWhiteCColor); + lbl->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + return lbl; + }; + auto createInactiveLabel = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + OnThemeChanged.push_back([lbl, palette]() { + lbl->setFontColor(palette->inactiveText); + }); + lbl->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + return lbl; + }; + auto createHLine = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + int y = static_cast(0.5 * (bounds.top + bounds.bottom)); + CRect lineBounds(bounds.left, y, bounds.right, y + 1); + CViewContainer* hline = new CViewContainer(lineBounds); + OnThemeChanged.push_back([hline, palette]() { + hline->setBackgroundColor(palette->text); + }); + return hline; + }; + auto createValueLabel = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + lbl->setFrameColor(kColorTransparent); + lbl->setBackColor(kColorTransparent); + OnThemeChanged.push_back([lbl, palette]() { + lbl->setFontColor(palette->text); + }); + lbl->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + return lbl; + }; + auto createBadge = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + lbl->setFrameColor(kColorTransparent); + OnThemeChanged.push_back([lbl, palette]() { + lbl->setBackColor(palette->valueBackground); + lbl->setFontColor(palette->valueText); + }); + lbl->setHoriAlign(align); + lbl->setStyle(CParamDisplay::kRoundRectStyle); + lbl->setRoundRectRadius(5.0); + auto font = makeOwned("Roboto", fontsize); + lbl->setFont(font); + return lbl; + }; + + auto createClickableLabel = [this, &palette](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) { + STextButton* button = new STextButton(bounds, this, tag, label); + auto font = makeOwned("Roboto", fontsize); + button->setFont(font); + button->setTextAlignment(align); + OnThemeChanged.push_back([button, palette]() { + button->setTextColor(palette->text); + button->setInactiveColor(palette->inactiveText); + button->setHighlightColor(palette->highlightedText); + }); + button->setFrameColor(kColorTransparent); + button->setFrameColorHighlighted(kColorTransparent); + SharedPointer gradient = owned(CGradient::create(0.0, 1.0, kColorTransparent, kColorTransparent)); + button->setGradient(gradient); + button->setGradientHighlighted(gradient); + return button; + }; + auto createValueButton = [this, &palette](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) { + STextButton* button = new STextButton(bounds, this, tag, label); + auto font = makeOwned("Roboto", fontsize); + button->setFont(font); + button->setTextAlignment(align); + OnThemeChanged.push_back([button, palette]() { + button->setTextColor(palette->valueText); + button->setInactiveColor(palette->inactiveText); + button->setHighlightColor(palette->highlightedText); + SharedPointer gradient = owned(CGradient::create(0.0, 1.0, palette->valueBackground, palette->valueBackground)); + button->setGradient(gradient); + button->setGradientHighlighted(gradient); + }); + button->setFrameColor(kColorTransparent); + button->setFrameColorHighlighted(kColorTransparent); + return button; + }; + auto createValueMenu = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign align, int fontsize) { + SValueMenu* vm = new SValueMenu(bounds, this, tag); + vm->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + vm->setFont(font); + OnThemeChanged.push_back([vm, palette]() { + vm->setFontColor(palette->valueText); + vm->setBackColor(palette->valueBackground); + }); + vm->setFrameColor(kColorTransparent); + vm->setStyle(CParamDisplay::kRoundRectStyle); + vm->setRoundRectRadius(5.0); + return vm; + }; + auto createOptionMenu = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign align, int fontsize) { + auto* cb = new COptionMenu(bounds, this, tag); + cb->setHoriAlign(align); + auto font = makeOwned("Roboto", fontsize); + cb->setFont(font); + OnThemeChanged.push_back([cb, palette]() { + cb->setFontColor(palette->valueText); + cb->setBackColor(palette->valueBackground); + }); + cb->setFrameColor(kColorTransparent); + cb->setStyle(CParamDisplay::kRoundRectStyle); + cb->setRoundRectRadius(5.0); + return cb; + }; + auto createHoverBox = [this, &palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) { + CTextLabel* lbl = new CTextLabel(bounds, label); + auto font = makeOwned("Roboto", fontsize); +#if 0 + OnThemeChanged.push_back([lbl, palette]() { + lbl->setFontColor(palette->valueText); + lbl->setBackColor(palette->valueBackground); + lbl->setFrameColor(palette->valueText); + }); +#else + // XP-style tooltips: Theme-based tooltips are nearly unreadable + lbl->setFontColor(kBlackCColor); + lbl->setBackColor(kColorTooltipBackground); + lbl->setFrameColor(kBlackCColor); + lbl->setStyle(CParamDisplay::kRoundRectStyle); + lbl->setRoundRectRadius(5.0); +#endif + lbl->setHoriAlign(align); + lbl->setFont(font); + lbl->setAutosizeFlags(kAutosizeAll); + return lbl; + }; + auto createGlyphButton = [this, &palette](UTF8StringPtr glyph, const CRect& bounds, int tag, int fontsize) { + STextButton* btn = new STextButton(bounds, this, tag, glyph); + btn->setFont(makeOwned("Sfizz Fluent System F20", fontsize)); + OnThemeChanged.push_back([btn, palette]() { + btn->setTextColor(palette->icon); + btn->setHighlightColor(palette->iconHighlight); + }); + btn->setFrameColor(kColorTransparent); + btn->setFrameColorHighlighted(kColorTransparent); + btn->setGradient(nullptr); + btn->setGradientHighlighted(nullptr); + return btn; + }; + auto createHomeButton = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + auto* btn = createGlyphButton(u8"\ue1d6", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Home"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createInfoButton = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + auto* btn = createGlyphButton(u8"\ue1e7", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Information"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createCCButton = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + auto* btn = createGlyphButton(u8"\ue253", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Controls"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; + auto createSettingsButton = [this, &createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + auto* btn = createGlyphButton(u8"\ue2e4", bounds, tag, fontsize); + btn->OnHoverEnter = [this, btn]() { buttonHoverEnter(btn, "Settings"); }; + btn->OnHoverLeave = [this, btn]() { buttonHoverLeave(btn); }; + return btn; + }; +#if 0 + auto createEditFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue148", bounds, tag, fontsize); + }; + auto createLoadFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue1a3", bounds, tag, fontsize); + }; +#endif + auto createPreviousFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue0d9", bounds, tag, fontsize); + }; + auto createNextFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue0da", bounds, tag, fontsize); + }; + auto createResetSomethingButton = [&createValueButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + STextButton* btn = createValueButton(bounds, tag, u8"\ue13a", kCenterText, fontsize); + btn->setFont(makeOwned("Sfizz Fluent System F20", fontsize)); + return btn; + }; + auto createPiano = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) { + SPiano* piano = new SPiano(bounds); + auto font = makeOwned("Roboto", fontsize); + piano->setFont(font); + OnThemeChanged.push_back([piano, palette]() { + piano->setFontColor(palette->text); + piano->setBackColor(palette->boxBackground); + }); + return piano; + }; + auto createChevronDropDown = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) { + SActionMenu* menu = new SActionMenu(bounds, this); + menu->setTitle(u8"\ue0d7"); + menu->setFont(makeOwned("Sfizz Fluent System F20", fontsize)); + OnThemeChanged.push_back([menu, palette]() { + menu->setFontColor(palette->icon); + menu->setHoverColor(palette->iconHighlight); + }); + menu->setFrameColor(kColorTransparent); + menu->setBackColor(kColorTransparent); + return menu; + }; + auto createChevronValueDropDown = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + SValueMenu* menu = new SValueMenu(bounds, this, tag); + menu->setValueToStringFunction2([](float, std::string& result, CParamDisplay*) -> bool { + result = u8"\ue0d7"; + return true; + }); + menu->setFont(makeOwned("Sfizz Fluent System F20", fontsize)); + OnThemeChanged.push_back([menu, palette]() { + menu->setFontColor(palette->icon); + menu->setHoverColor(palette->iconHighlight); + }); + menu->setFrameColor(kColorTransparent); + menu->setBackColor(kColorTransparent); + return menu; + }; + auto createKnob48 = [this, &knob48](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) { + return new CAnimKnob(bounds, this, tag, 31, 48, knob48); + }; + auto createStyledKnob = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) { + SStyledKnob* knob = new SStyledKnob(bounds, this, tag); + OnThemeChanged.push_back([knob, palette]() { + knob->setActiveTrackColor(palette->knobActiveTrack); + knob->setInactiveTrackColor(palette->knobInactiveTrack); + knob->setLineIndicatorColor(palette->knobLineIndicator); + }); + return knob; + }; + auto createKnobCCBox = [this, &palette](const CRect& bounds, int tag, const char* label, CHoriTxtAlign, int fontsize) { + SKnobCCBox* box = new SKnobCCBox(bounds, this, tag); + auto font = makeOwned("Roboto", fontsize); + box->setNameLabelText(label); + box->setNameLabelFont(font); + box->setKnobFont(font); + box->setCCLabelText(label); + box->setCCLabelFont(font); + OnThemeChanged.push_back([box, palette]() { + box->setNameLabelFontColor(palette->knobText); + box->setValueEditFontColor(palette->knobText); + auto shadingColor = palette->knobText; + shadingColor.alpha = 70; + box->setShadingRectangleColor(shadingColor); + box->setCCLabelFontColor(palette->knobLabelText); + box->setCCLabelBackColor(palette->knobLabelBackground); + box->setKnobFontColor(palette->knobText); + box->setKnobLineIndicatorColor(palette->knobLineIndicator); + box->setKnobActiveTrackColor(palette->knobActiveTrack); + box->setKnobInactiveTrackColor(palette->knobInactiveTrack); + }); + return box; + }; + auto createBackground = [&background](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + CViewContainer* container = new CViewContainer(bounds); + container->setBackground(background); + return container; + }; + auto createControlsPanel = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) { + auto* panel = new SControlsPanel(bounds); + auto font = makeOwned("Roboto", fontsize); + panel->setCCLabelFont(font); + panel->setKnobFont(font); + panel->setKnobFontColor(kWhiteCColor); + panel->setKnobLineIndicatorColor(kWhiteCColor); + panel->setKnobRotatorColor(kColorControlsTransparency); + panel->setNameLabelFont(font); + panel->setNameLabelFontColor(kWhiteCColor); + panel->setNameLabelBackColor(kColorControlsTransparency); + OnThemeChanged.push_back([panel, palette]() { + panel->setValueEditFontColor(palette->knobText); + auto shadingColor = palette->knobText; + shadingColor.alpha = 70; + panel->setShadingRectangleColor(shadingColor); + panel->setCCLabelFontColor(palette->knobLabelText); + panel->setCCLabelBackColor(palette->knobLabelBackground); + panel->setKnobActiveTrackColor(palette->knobActiveTrack); + panel->setKnobInactiveTrackColor(palette->knobInactiveTrack); + }); + return panel; + }; + + auto createCheckbox = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign, int) { + auto* checkbox = new CCheckBox(bounds, this, tag, label); + return checkbox; + }; + + auto createTextEdit = [this, &palette] (const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) { + auto* edit = new CTextEdit(bounds, this, tag, label, nullptr); + auto font = makeOwned("Roboto", fontsize); + edit->setFont(font); + edit->setHoriAlign(align); + edit->setFrameColor(kColorTransparent); + edit->setStyle(CParamDisplay::kRoundRectStyle); + edit->setRoundRectRadius(5.0); + OnThemeChanged.push_back([edit, palette]() { + edit->setFontColor(palette->valueText); + edit->setBackColor(palette->valueBackground); + }); + return edit; + }; + + #include "layout/main.hpp" + + OnThemeChanged.push_back([mainView, theme]() { + mainView->setBackgroundColor(theme->frameBackground); + }); + + OnThemeChanged.push_back([this, theme]() { + for (unsigned i = 0, n = sizeof(meters_)/sizeof(meters_[0]); i < n; ++i ) { + Palette& palette = theme->invertedPalette; + const auto meter = meters_[i]; + if (meter) + meter->setBackColor(palette.knobInactiveTrack); + } + }); + +#if LINUX + if (!isZenityAvailable()) { + CRect bounds = mainView->getViewSize(); + + CViewContainer* box = new CViewContainer(bounds); + mainView->addView(box); + box->setBackgroundColor(kColorTransparentDark); + + CRect textSize = CRect(0, 0, 400, 80).centerInside(bounds); + CMultiLineTextLabel* textLabel = new CMultiLineTextLabel(textSize); + box->addView(textLabel); + textLabel->setTextInset(CPoint(10.0, 10.0)); + textLabel->setStyle(CParamDisplay::kRoundRectStyle); + textLabel->setRoundRectRadius(10.0); + textLabel->setFrameColor(CColor(0xb2, 0xb2, 0xb2)); + textLabel->setBackColor(CColor(0x2e, 0x34, 0x36)); + auto font = makeOwned("Roboto", 16.0); + textLabel->setFont(font); + textLabel->setLineLayout(CMultiLineTextLabel::LineLayout::wrap); + textLabel->setText( + "The required program \"zenity\" is missing.\n" + "Install this software package first, and restart sfizz."); + } +#endif + + mainView_ = owned(mainView); + } + + if (CTextLabel* label = sfizzVersionLabel_) { + std::string version = GitBuildId[0] ? absl::StrCat(SFIZZ_VERSION ".", GitBuildId) : SFIZZ_VERSION; + label->setText(absl::StrCat(u8"sfizz ", version)); + } + + /// + currentThemeName_ = theme->loadCurrentName(); + theme->load(currentThemeName_); + + /// + SAboutDialog* aboutDialog = new SAboutDialog(mainView->getViewSize()); + mainView->addView(aboutDialog); + aboutDialog_ = aboutDialog; + aboutDialog->setVisible(false); + + /// + SharedPointer fileDropTarget = owned(new SFileDropTarget); + + fileDropTarget->setFileDropFunction([this](const std::string& file) { + changeSfzFile(file); + }); + + mainView_->setDropTarget(fileDropTarget); + + /// + adjustMinMaxToEditRange(volumeSlider_, EditId::Volume); + adjustMinMaxToEditRange(numVoicesSlider_, EditId::Polyphony); + adjustMinMaxToEditRange(oversamplingSlider_, EditId::Oversampling); + adjustMinMaxToEditRange(preloadSizeSlider_, EditId::PreloadSize); + if (scalaRootKeySlider_) { + scalaRootKeySlider_->setMin(0.0); + scalaRootKeySlider_->setMax(11.0); + scalaRootKeySlider_->setDefaultValue( + static_cast(EditRange::get(EditId::ScalaRootKey).def) % 12); + } + if (scalaRootOctaveSlider_) { + scalaRootOctaveSlider_->setMin(0.0); + scalaRootOctaveSlider_->setMax(10.0); + scalaRootOctaveSlider_->setDefaultValue( + static_cast(EditRange::get(EditId::ScalaRootKey).def) / 12); + } + adjustMinMaxToEditRange(tuningFrequencyDropdown_, EditId::TuningFrequency); + adjustMinMaxToEditRange(tuningFrequencyEdit_, EditId::TuningFrequency); + tuningFrequencyEdit_->setWheelInc(0.1f / EditRange::get(EditId::TuningFrequency).extent()); + adjustMinMaxToEditRange(stretchedTuningSlider_, EditId::StretchTuning); + adjustMinMaxToEditRange(sampleQualitySlider_, EditId::SampleQuality); + adjustMinMaxToEditRange(oscillatorQualitySlider_, EditId::OscillatorQuality); + adjustMinMaxToEditRange(freewheelingSampleQualitySlider_, EditId::FreewheelingSampleQuality); + adjustMinMaxToEditRange(freewheelingOscillatorQualitySlider_, EditId::FreewheelingOscillatorQuality); + adjustMinMaxToEditRange(sustainCancelsReleaseCheckbox_, EditId::SustainCancelsRelease); + adjustMinMaxToEditRange(zoomMenu_, EditId::UIZoom); + + for (int value : {1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 224, 256}) + numVoicesSlider_->addEntry(std::to_string(value), value); + + for (int log2value = 0; log2value <= 3; ++log2value) { + int value = 1 << log2value; + oversamplingSlider_->addEntry(std::to_string(value) + "x", log2value); + } + + oversamplingSlider_->setValueToStringFunction2( + [](float value, std::string& result, CParamDisplay*) -> bool + { + result = std::to_string(1 << static_cast(value)) + "x"; + return true; + }); + + for (int value : { 100, 125, 150, 175, 200, 225, 250, 275, 300 }) { + zoomMenu_->addEntry(std::to_string(value) + "%", value); + } + + zoomMenu_->setValueToStringFunction2( + [](float value, std::string& result, CParamDisplay*) -> bool + { + result = std::to_string(static_cast(value) * 100) + "%"; + return true; + }); + + for (int log2value = 10; log2value <= 16; ++log2value) { + int value = 1 << log2value; + char text[256]; + sprintf(text, "%lu kB", static_cast(value / 1024 * sizeof(float))); + text[sizeof(text) - 1] = '\0'; + preloadSizeSlider_->addEntry(text, value); + } + preloadSizeSlider_->setValueToStringFunction2( + [](float value, std::string& result, CParamDisplay*) -> bool + { + result = std::to_string(static_cast(std::round(value * (1.0 / 1024 * sizeof(float))))) + " kB"; + return true; + }); + + static const std::pair tuningFrequencies[] = { + {380.0f, "English pitchpipe 380 (1720)"}, + {409.0f, "Handel fork 409 (1780)"}, + {415.0f, "Baroque 415"}, + {422.5f, "Handel fork 422.5 (1740)"}, + {423.2f, "Dresden opera 423.2 (1815)"}, + {435.0f, "French Law 435 (1859)"}, + {439.0f, "British Phil 439 (1896)"}, + {440.0f, "International 440"}, + {442.0f, "European 442"}, + {445.0f, "Germany, China 445"}, + {451.0f, "La Scala in Milan 451 (18th)"}, + }; + + for (std::pair value : tuningFrequencies) + tuningFrequencyDropdown_->addEntry(value.second, value.first); + + tuningFrequencyEdit_->setValueToStringFunction( + [](float value, char result[256], CParamDisplay*) -> bool + { + sprintf(result, "%.1f Hz", value); + return true; + }); + + tuningFrequencyEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { + float value; + if (absl::SimpleAtof(txt, &value)) { + result = value; + return true; + } + + return false; + }); + + static const char* notesInOctave[12] = { + "C", "C#", "D", "D#", "E", + "F", "F#", "G", "G#", "A", "A#", "B", + }; + for (int note = 0; note < 12; ++note) + scalaRootKeySlider_->addEntry(notesInOctave[note], note); + for (int octave = 0; octave <= 10; ++octave) + scalaRootOctaveSlider_->addEntry(std::to_string(octave - 1), octave); + scalaRootKeySlider_->setValueToStringFunction2( + [](float value, std::string& result, CParamDisplay*) -> bool + { + result = notesInOctave[std::max(0, static_cast(value)) % 12]; + return true; + }); + scalaRootOctaveSlider_->setValueToStringFunction2( + [](float value, std::string& result, CParamDisplay*) -> bool + { + result = std::to_string(static_cast(value) - 1); + return true; + }); + + if (SActionMenu* menu = fileOperationsMenu_) { + menu->addEntry("Load file", kTagLoadSfzFile); + menu->addEntry("Edit file", kTagEditSfzFile); + menu->addEntry("Create new file", kTagCreateNewSfzFile); + menu->addEntry("Open SFZ folder", kTagOpenSfzFolder); + } + + static const std::array sampleQualityLabels {{ + "Nearest", "Linear", "Polynomial", + "Sinc 8", "Sinc 12", "Sinc 16", "Sinc 24", + "Sinc 36", "Sinc 48", "Sinc 60", "Sinc 72", + }}; + + auto setupSampleQualityMenu = [&] (SValueMenu* menu) { + if (menu) { + for (size_t i = 0; i < sampleQualityLabels.size(); ++i) + menu->addEntry(sampleQualityLabels[i], float(i)); + menu->setValueToStringFunction2([](float value, std::string& result, CParamDisplay*) -> bool { + int index = int(value); + if (index < 0 || unsigned(index) >= sampleQualityLabels.size()) + return false; + result = sampleQualityLabels[unsigned(index)]; + return true; + }); + } + }; + + setupSampleQualityMenu(sampleQualitySlider_); + setupSampleQualityMenu(freewheelingSampleQualitySlider_); + + static const std::array oscillatorQualityLabels {{ + "Nearest", "Linear", "High", "Dual-High", + }}; + + auto setupOscillatorQualityMenu = [&] (SValueMenu* menu) { + if (menu) { + for (size_t i = 0; i < oscillatorQualityLabels.size(); ++i) + menu->addEntry(oscillatorQualityLabels[i], float(i)); + menu->setValueToStringFunction2([](float value, std::string& result, CParamDisplay*) -> bool { + int index = int(value); + if (index < 0 || unsigned(index) >= oscillatorQualityLabels.size()) + return false; + result = oscillatorQualityLabels[unsigned(index)]; + return true; + }); + } + }; + + setupOscillatorQualityMenu(oscillatorQualitySlider_); + setupOscillatorQualityMenu(freewheelingOscillatorQualitySlider_); + + if (SPiano* piano = piano_) { + piano->onKeyPressed = [this](unsigned key, float vel) { + uint8_t msg[3]; + msg[0] = 0x90; + msg[1] = static_cast(key); + msg[2] = static_cast(std::max(1, static_cast(vel * 127))); + ctrl_->uiSendMIDI(msg, sizeof(msg)); + }; + piano->onKeyReleased = [this](unsigned key, float vel) { + uint8_t msg[3]; + msg[0] = 0x80; + msg[1] = static_cast(key); + msg[2] = static_cast(vel * 127); + ctrl_->uiSendMIDI(msg, sizeof(msg)); + }; + } + + if (SControlsPanel* panel = controlsPanel_) { + panel->ValueChangeFunction = [this](uint32_t cc, float value) { + performCCValueChange(cc, value); + updateCCValue(cc, value); + }; + panel->BeginEditFunction = [this](uint32_t cc) { + performCCBeginEdit(cc); + }; + panel->EndEditFunction = [this](uint32_t cc) { + performCCEndEdit(cc); + }; + } + + if (SKnobCCBox* box = volumeCCKnob_) { + unsigned ccNumber = 7; + box->setCCLabelText(("CC " + std::to_string(ccNumber)).c_str()); + } + if (SKnobCCBox* box = panCCKnob_) { + unsigned ccNumber = 10; + box->setCCLabelText(("CC " + std::to_string(ccNumber)).c_str()); + } + + updateKeyswitchNameLabel(); + + /// + CViewContainer* panel; + activePanel_ = 0; + + // all panels + for (unsigned currentPanel = 0; currentPanel < kNumPanels; ++currentPanel) { + panel = subPanels_[currentPanel]; + + if (!panel) + continue; + + panel->setVisible(currentPanel == activePanel_); + } + + setupCurrentPanel(); + + if (COptionMenu* menu = themeMenu_) { + const std::vector& names = Theme::getAvailableNames(); + size_t index = ~size_t(0); + for (size_t i = 0, n = names.size(); i < n; ++i) { + const std::string& name = names[i]; + menu->addEntry(UTF8String(name)); + if (name == currentThemeName_) + index = i; + } + if (index != ~size_t(0)) + menu->setCurrent(index); + } +} + +void Editor::Impl::chooseSfzFile() +{ + SharedPointer fs = owned(CNewFileSelector::create(frame_)); + + fs->setTitle("Load SFZ file"); + fs->addFileExtension(CFileExtension("SFZ", "sfz")); + + // also add extensions of importable files + fs->addFileExtension(CFileExtension("WAV", "wav")); + fs->addFileExtension(CFileExtension("FLAC", "flac")); + fs->addFileExtension(CFileExtension("OGG", "ogg")); + fs->addFileExtension(CFileExtension("MP3", "mp3")); + fs->addFileExtension(CFileExtension("AIF", "aif")); + fs->addFileExtension(CFileExtension("AIFF", "aiff")); + fs->addFileExtension(CFileExtension("AIFC", "aifc")); + // Decent samples + fs->addFileExtension(CFileExtension("DSPRESET", "dspreset")); + + std::string initialDir = getFileChooserInitialDir(currentSfzFile_); + if (!initialDir.empty()) + fs->setInitialDirectory(initialDir.c_str()); + + frameDisabler_->disable(); + bool runOk = fs->runModal(); + frameDisabler_->enable(); + + if (runOk) { + UTF8StringPtr file = fs->getSelectedFile(0); + if (file) + changeSfzFile(file); + } +} + +/// +static const char defaultSfzText[] = + "sample=*sine" "\n" + "ampeg_attack=0.02 ampeg_release=0.1" "\n"; + +static void createDefaultSfzFileIfNotExisting(const fs::path& path) +{ + if (!fs::exists(path)) + fs::ofstream { path } << defaultSfzText; +} + +/// +void Editor::Impl::createNewSfzFile() +{ + SharedPointer fs = owned(CNewFileSelector::create(frame_, CNewFileSelector::kSelectSaveFile)); + + fs->setTitle("Create SFZ file"); + fs->addFileExtension(CFileExtension("SFZ", "sfz")); + + std::string initialDir = getFileChooserInitialDir(currentSfzFile_); + if (!initialDir.empty()) + fs->setInitialDirectory(initialDir.c_str()); + + frameDisabler_->disable(); + bool runOk = fs->runModal(); + frameDisabler_->enable(); + + if (runOk) { + UTF8StringPtr file = fs->getSelectedFile(0); + std::string fileStr; + if (file && !absl::EndsWithIgnoreCase(file, ".sfz")) { + fileStr = std::string(file) + ".sfz"; + file = fileStr.c_str(); + } + if (file) { + createDefaultSfzFileIfNotExisting(fs::u8path(file)); + changeSfzFile(file); + openFileInExternalEditor(file); + } + } +} + +void Editor::Impl::changeSfzFile(const std::string& filePath) +{ + ctrl_->uiSendValue(EditId::SfzFile, filePath); + currentSfzFile_ = filePath; + updateSfzFileLabel(filePath); +} + +void Editor::Impl::changeToNextSfzFile(long offset) +{ + if (currentSfzFile_.empty()) + return; + + const fs::path filePath = fs::u8path(currentSfzFile_); + const fs::path dirPath = filePath.parent_path(); + + // extract file names of regular files from the sfz directory + std::vector fileNames; + fileNames.reserve(64); + + auto fileFilter = [](const fs::path &name) -> bool { + std::string ext = name.extension().u8string(); + absl::AsciiStrToLower(&ext); + return ext == ".sfz"; + }; + + if (!scanDirectoryFiles(dirPath, fileFilter, fileNames)) + return; + + // sort file names + const size_t size = fileNames.size(); + if (size == 0) + return; + + std::sort(fileNames.begin(), fileNames.end()); + + // find our current position in the file name list + size_t currentIndex = 0; + const fs::path currentFileName = filePath.filename(); + + while (currentIndex + 1 < size && fileNames[currentIndex] < currentFileName) + ++currentIndex; + + // advance to the next or previous item + typedef typename std::make_signed::type signed_size_t; + + size_t newIndex = static_cast(currentIndex) + offset; + if (static_cast(newIndex) < 0) + newIndex = static_cast(newIndex) % + static_cast(size) + size; + newIndex %= size; + + if (newIndex != currentIndex) { + const fs::path newFilePath = dirPath / fileNames[newIndex]; + changeSfzFile(newFilePath.u8string()); + } +} + +void Editor::Impl::chooseScalaFile() +{ + SharedPointer fs = owned(CNewFileSelector::create(frame_)); + + fs->setTitle("Load Scala file"); + fs->addFileExtension(CFileExtension("SCL", "scl")); + + std::string initialDir = getFileChooserInitialDir(currentScalaFile_); + if (!initialDir.empty()) + fs->setInitialDirectory(initialDir.c_str()); + + frameDisabler_->disable(); + bool runOk = fs->runModal(); + frameDisabler_->enable(); + + if (runOk) { + UTF8StringPtr file = fs->getSelectedFile(0); + if (file) + changeScalaFile(file); + } +} + +void Editor::Impl::changeScalaFile(const std::string& filePath) +{ + ctrl_->uiSendValue(EditId::ScalaFile, filePath); + currentScalaFile_ = filePath; + updateScalaFileLabel(filePath); +} + +void Editor::Impl::chooseUserFilesDir() +{ + SharedPointer fs = owned( + CNewFileSelector::create(frame_, CNewFileSelector::kSelectDirectory)); + + fs->setTitle("Set user files directory"); + + frameDisabler_->disable(); + bool runOk = fs->runModal(); + frameDisabler_->enable(); + + if (runOk) { + UTF8StringPtr dir = fs->getSelectedFile(0); + if (dir) { + userFilesDir_ = std::string(dir); + updateUserFilesDirLabel(userFilesDir_); + ctrl_->uiSendValue(EditId::UserFilesDir, userFilesDir_); + } + } +} + +std::string Editor::Impl::getFileChooserInitialDir(const std::string& previousFilePath) const +{ + fs::path initialPath; + + if (!previousFilePath.empty()) + initialPath = fs::u8path(previousFilePath).parent_path(); + else if (!userFilesDir_.empty()) + initialPath = fs::u8path(userFilesDir_); + else if (!fallbackFilesDir_.empty()) + initialPath = fs::u8path(fallbackFilesDir_); + + std::string initialDir = initialPath.u8string(); + if (!initialDir.empty()) + initialDir.push_back('/'); + + return initialDir; +} + +bool Editor::Impl::scanDirectoryFiles(const fs::path& dirPath, std::function filter, std::vector& fileNames) +{ + std::error_code ec; + fs::directory_iterator it { dirPath, ec }; + + if (ec) + return false; + + fileNames.clear(); + + while (!ec && it != fs::directory_iterator()) { + const fs::directory_entry& ent = *it; + + std::error_code fileEc; + const fs::file_status status = ent.status(fileEc); + if (fileEc) + continue; + + if (status.type() == fs::file_type::regular) { + fs::path fileName = ent.path().filename(); + if (!filter || filter(fileName)) + fileNames.push_back(std::move(fileName)); + } + + it.increment(ec); + } + + if (ec) + return false; + + return true; +} + +absl::string_view Editor::Impl::simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty) +{ + if (path.empty()) + return ifEmpty; + +#if defined (_WIN32) + size_t pos = path.find_last_of("/\\"); +#else + size_t pos = path.rfind('/'); +#endif + path = (pos != path.npos) ? path.substr(pos + 1) : path; + + if (!removedSuffix.empty() && absl::EndsWithIgnoreCase(path, removedSuffix)) + path.remove_suffix(removedSuffix.size()); + + return path; +} + +void Editor::Impl::updateSfzFileLabel(const std::string& filePath) +{ + updateButtonWithFileName(sfzFileLabel_, filePath, ".sfz"); +} + +void Editor::Impl::updateScalaFileLabel(const std::string& filePath) +{ + updateLabelWithFileName(scalaFileLabel_, filePath, ".scl"); + updateButtonWithFileName(scalaFileButton_, filePath, ".scl"); +} + +void Editor::Impl::updateUserFilesDirLabel(const std::string& filePath) +{ + updateButtonWithFileName(userFilesDirButton_, filePath, {}); +} + +void Editor::Impl::updateLabelWithFileName(CTextLabel* label, const std::string& filePath, absl::string_view removedSuffix) +{ + if (!label) + return; + + std::string fileName = std::string(simplifiedFileName(filePath, removedSuffix, "")); + label->setText(fileName.c_str()); +} + +void Editor::Impl::updateButtonWithFileName(STextButton* button, const std::string& filePath, absl::string_view removedSuffix) +{ + if (!button) + return; + + std::string fileName = std::string(simplifiedFileName(filePath, removedSuffix, {})); + if (!fileName.empty()) { + button->setTitle(fileName.c_str()); + button->setInactive(false); + } + else { + button->setTitle("No file"); + button->setInactive(true); + } +} + +void Editor::Impl::updateVolumeLabel(float volume) +{ + CTextLabel* label = volumeLabel_; + if (!label) + return; + + char text[64]; + sprintf(text, "%.1f dB", volume); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::updateNumVoicesLabel(int numVoices) +{ + CTextLabel* label = numVoicesLabel_; + if (!label) + return; + + char text[64]; + sprintf(text, "%d", numVoices); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::updateOversamplingLabel(int oversamplingLog2) +{ + CTextLabel* label = oversamplingLabel_; + if (!label) + return; + + char text[64]; + sprintf(text, "%dx", 1 << oversamplingLog2); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::updatePreloadSizeLabel(int preloadSize) +{ + CTextLabel* label = preloadSizeLabel_; + if (!label) + return; + + char text[64]; + sprintf(text, "%d kB", static_cast(std::round(preloadSize * (1.0 / 1024)))); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::updateScalaRootKeyLabel(int rootKey) +{ + CTextLabel* label = scalaRootKeyLabel_; + if (!label) + return; + + static const char *octNoteNames[12] = { + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", + }; + + auto noteName = [](int key) -> std::string + { + int octNum; + int octNoteNum; + if (key >= 0) { + octNum = key / 12 - 1; + octNoteNum = key % 12; + } + else { + octNum = -2 - (key + 1) / -12; + octNoteNum = (key % 12 + 12) % 12; + } + return std::string(octNoteNames[octNoteNum]) + std::to_string(octNum); + }; + + label->setText(noteName(rootKey)); +} + +void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning) +{ + CTextLabel* label = stretchedTuningLabel_; + if (!label) + return; + + char text[64]; + sprintf(text, "%.3f", stretchedTuning); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::buttonHoverEnter(CControl* btn, const char* text) +{ + lblHover_->setText(text); + lblHover_->sizeToFit(); + CRect rect = lblHover_->getViewSize(); + CRect btnRect = btn->getViewSize(); + auto height = rect.getHeight(); + auto width = rect.getWidth(); + rect.left = btnRect.left; + rect.top = btnRect.bottom + 2; + rect.setWidth(width + 10); + rect.setHeight(height); + lblHover_->setViewSize(rect); + lblHover_->setVisible(true); + lblHover_->invalid(); +} + +void Editor::Impl::buttonHoverLeave(CControl* btn) +{ + (void)btn; + lblHover_->setVisible(false); + lblHover_->invalid(); +} + +absl::string_view Editor::Impl::getCurrentKeyswitchName() const +{ + int sw = currentKeyswitch_; + if (sw == -1) + return {}; + + auto it = keyswitchNames_.find(static_cast(sw)); + if (it == keyswitchNames_.end()) + return {}; + + return it->second; +} + +void Editor::Impl::updateKeyswitchNameLabel() +{ + CTextLabel* label = keyswitchLabel_; + CTextLabel* badge = keyswitchBadge_; + CTextLabel* inactiveLabel = keyswitchInactiveLabel_; + + int sw = currentKeyswitch_; + const std::string name { getCurrentKeyswitchName() }; + + if (sw == -1) { + if (badge) + badge->setVisible(false); + if (label) + label->setVisible(false); + if (inactiveLabel) + inactiveLabel->setVisible(true); + } + else { + if (badge) { + badge->setText(getUnicodeNoteName(sw)); + badge->setVisible(true); + } + if (label) { + label->setText(name.c_str()); + label->setVisible(true); + } + if (inactiveLabel) + inactiveLabel->setVisible(false); + } +} + +void Editor::Impl::updateKeyUsed(unsigned key, bool used) +{ + if (SPiano* piano = piano_) + piano->setKeyUsed(key, used); +} + +void Editor::Impl::updateKeyLabel(unsigned key, const char* label) +{ + // TODO nothing done with this info currently + (void)key; + (void)label; +} + +void Editor::Impl::updateKeyswitchUsed(unsigned key, bool used) +{ + if (SPiano* piano = piano_) + piano->setKeyswitchUsed(key, used); +} + +void Editor::Impl::updateCCUsed(unsigned cc, bool used) +{ + if (SControlsPanel* panel = controlsPanel_) + panel->setControlUsed(cc, used); +} + +void Editor::Impl::updateCCValue(unsigned cc, float value) +{ + if (SControlsPanel* panel = controlsPanel_) + panel->setControlValue(cc, value); + + if (SKnobCCBox* other = getSecondaryCCKnob(cc)) { + other->setValue(value); + other->invalid(); + } +} + +void Editor::Impl::updateCCDefaultValue(unsigned cc, float value) +{ + if (SControlsPanel* panel = controlsPanel_) + panel->setControlDefaultValue(cc, value); + + if (SKnobCCBox* other = getSecondaryCCKnob(cc)) + other->setDefaultValue(value); +} + +void Editor::Impl::updateCCLabel(unsigned cc, const char* label) +{ + if (SControlsPanel* panel = controlsPanel_) + panel->setControlLabelText(cc, label); +} + +void Editor::Impl::updateSWLastCurrent(int sw) +{ + if (currentKeyswitch_ == sw) + return; + currentKeyswitch_ = sw; + updateKeyswitchNameLabel(); +} + +void Editor::Impl::updateSWLastLabel(unsigned sw, const char* label) +{ + keyswitchNames_[sw].assign(label); + if ((unsigned)currentKeyswitch_ == sw) + updateKeyswitchNameLabel(); +} + +void Editor::Impl::updateBackgroundImage(const char* filepath) +{ + backgroundBitmap_ = loadAnyFormatImage(filepath); + + if (!backgroundBitmap_) + backgroundBitmap_ = defaultBackgroundBitmap_; + + applyBackgroundForCurrentPanel(); +} + +void Editor::Impl::updateControlsImage(const char* filepath) +{ + controlsBitmap_ = loadAnyFormatImage(filepath); + + if (!controlsBitmap_) { + return; + } + + applyBackgroundForCurrentPanel(); +} + +void Editor::Impl::setupCurrentPanel() +{ + for (unsigned i = 0; i < kNumPanels; ++i) { + if (STextButton* button = panelButtons_[i]) + button->setHighlighted(i == activePanel_); + } + + applyBackgroundForCurrentPanel(); +} + +void Editor::Impl::applyBackgroundForCurrentPanel() +{ + CBitmap* bitmap; + imageContainer_->setBackgroundColor(theme_->frameBackground); + + if (activePanel_ == kPanelGeneral || activePanel_ == kPanelInfo) { + bitmap = backgroundBitmap_; + } else if (activePanel_ == kPanelControls) { + bitmap = controlsBitmap_; + if (!controlsBitmap_) { + imageContainer_->setBackground(nullptr); + return; + } + } else { + return; + } + + downscaleToWidthAndHeight(bitmap, imageContainer_->getViewSize().getSize()); + + // Centered image + CCoord xoffset = + (imageContainer_->getViewSize().getWidth() - bitmap->getWidth()) / 2; + + imageContainer_->setBackgroundOffset(CPoint(-xoffset, 0)); + imageContainer_->setBackground(bitmap); +} + +void Editor::Impl::updateMemoryUsed(uint64_t mem) +{ + if (CTextLabel* label = memoryLabel_) { + double value = mem / 1e3; + const char* unit = "kB"; + int precision = 0; + if (value >= 1e3) { + value /= 1e3; + unit = "MB"; + } + if (value >= 1e3) { + value /= 1e3; + unit = "GB"; + precision = 1; + } + char textbuf[128]; + snprintf(textbuf, sizeof(textbuf), "%.*f %s", precision, value, unit); + label->setText(textbuf); + } +} + +void Editor::Impl::performCCValueChange(unsigned cc, float value) +{ + EditorController& ctrl = *ctrl_; + ctrl.uiSendValue(editIdForCC(int(cc)), value); +} + +void Editor::Impl::performCCBeginEdit(unsigned cc) +{ + // TODO(jpc) CC as parameters and automation + (void)cc; +} + +void Editor::Impl::performCCEndEdit(unsigned cc) +{ + // TODO(jpc) CC as parameters and automation + (void)cc; +} + +void Editor::Impl::setActivePanel(unsigned panelId) +{ + panelId = std::max(0, std::min(kNumPanels - 1, static_cast(panelId))); + + if (activePanel_ != panelId) { + if (subPanels_[activePanel_]) + subPanels_[activePanel_]->setVisible(false); + if (subPanels_[panelId]) + subPanels_[panelId]->setVisible(true); + activePanel_ = panelId; + setupCurrentPanel(); + } +} + +void Editor::Impl::setZoom(int zoom) +{ + if (!zoomMenu_) + return; + + float zoomFactor = static_cast(zoom) / 100; + + std::vector values = + { 100, 125, 150, 175, 200, 225, 250, 275, 300 }; + + size_t pos = std::distance(values.cbegin(), + find(values.cbegin(), values.cend(), zoom)); + + if (pos < values.size()) { + zoom_ = zoomFactor; + zoomMenu_->setCurrent(pos); + frame_->setZoom(zoom_); + } else { + zoom_ = 1.0f; + zoomMenu_->setCurrent(0); + } +} + +void Editor::Impl::formatLabel(CTextLabel* label, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vformatLabel(label, fmt, ap); + va_end(ap); +} + +void Editor::Impl::vformatLabel(CTextLabel* label, const char* fmt, va_list ap) +{ + char text[256]; + vsprintf(text, fmt, ap); + text[sizeof(text) - 1] = '\0'; + label->setText(text); +} + +void Editor::Impl::valueChanged(CControl* ctl) +{ + int32_t tag = ctl->getTag(); + float value = ctl->getValue(); + EditorController& ctrl = *ctrl_; + + switch (tag) { + case kTagLoadSfzFile: + if (value != 1) + break; + + Call::later([this]() { chooseSfzFile(); }); + break; + + case kTagEditSfzFile: + if (value != 1) + break; + + if (!currentSfzFile_.empty()) + openFileInExternalEditor(currentSfzFile_.c_str()); + break; + + case kTagCreateNewSfzFile: + if (value != 1) + break; + + Call::later([this]() { createNewSfzFile(); }); + break; + + case kTagOpenSfzFolder: + if (value != 1) + break; + + if (!userFilesDir_.empty()) + openDirectoryInExplorer(userFilesDir_.c_str()); + else if (!fallbackFilesDir_.empty()) + openDirectoryInExplorer(fallbackFilesDir_.c_str()); + break; + + case kTagPreviousSfzFile: + if (value != 1) + break; + + Call::later([this]() { changeToNextSfzFile(-1); }); + break; + + case kTagNextSfzFile: + if (value != 1) + break; + + Call::later([this]() { changeToNextSfzFile(+1); }); + break; + + case kTagLoadScalaFile: + if (value != 1) + break; + + Call::later([this]() { chooseScalaFile(); }); + break; + + case kTagResetScalaFile: + if (value != 1) + break; + + changeScalaFile(std::string()); + break; + + case kTagSetMainVolume: + ctrl.uiSendValue(EditId::Volume, value); + updateVolumeLabel(value); + break; + + case kTagSetCCVolume: + performCCValueChange(7, value); + updateCCValue(7, value); + break; + + case kTagSetCCPan: + performCCValueChange(10, value); + updateCCValue(10, value); + break; + + case kTagSetNumVoices: + ctrl.uiSendValue(EditId::Polyphony, value); + updateNumVoicesLabel(static_cast(value)); + break; + + case kTagSetOversampling: + ctrl.uiSendValue(EditId::Oversampling, static_cast(1 << static_cast(value))); + updateOversamplingLabel(static_cast(value)); + break; + + case kTagSetPreloadSize: + ctrl.uiSendValue(EditId::PreloadSize, value); + updatePreloadSizeLabel(static_cast(value)); + break; + + case kTagSetScalaRootKey: + { + if (scalaRootKeySlider_ && scalaRootOctaveSlider_) { + int key = static_cast(scalaRootKeySlider_->getValue()); + int octave = static_cast(scalaRootOctaveSlider_->getValue()); + int midiKey = key + 12 * octave; + ctrl.uiSendValue(EditId::ScalaRootKey, midiKey); + updateScalaRootKeyLabel(midiKey); + } + } + break; + + case kTagSetTuningFrequency: + ctrl.uiSendValue(EditId::TuningFrequency, value); + if (tuningFrequencyEdit_) + tuningFrequencyEdit_->setValue(value); + break; + + case kTagSetSampleQuality: + ctrl.uiSendValue(EditId::SampleQuality, value); + break; + + case kTagSetOscillatorQuality: + ctrl.uiSendValue(EditId::OscillatorQuality, value); + break; + + case kTagSetFreewheelingSampleQuality: + ctrl.uiSendValue(EditId::FreewheelingSampleQuality, value); + break; + + case kTagSetFreewheelingOscillatorQuality: + ctrl.uiSendValue(EditId::FreewheelingOscillatorQuality, value); + break; + + case kTagSetSustainCancelsRelease: + ctrl.uiSendValue(EditId::SustainCancelsRelease, value); + break; + + case kTagSetStretchedTuning: + ctrl.uiSendValue(EditId::StretchTuning, value); + updateStretchedTuningLabel(value); + break; + + case kTagChooseUserFilesDir: + if (value != 1) + break; + + Call::later([this]() { chooseUserFilesDir(); }); + break; + + case kTagAbout: + if (value != 1) + break; + + Call::later([this]() { + aboutDialog_->setVisible(true); + frame_->registerKeyboardHook(aboutDialog_); + }); + break; + + case kTagThemeMenu: + { + currentThemeName_ = Theme::getAvailableNames()[int(value)]; + Theme::storeCurrentName(currentThemeName_); + theme_->load(currentThemeName_); + } + break; + + case kTagZoomMenu: { + std::vector values = + { 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0 }; + + auto zoom = values.at(value); + zoom_ = zoom; +#if 0 + fprintf(stderr, "[sfizz] valueChanged: %f\n", zoom_); + // TODO: Recreate frame with the new size + frame_->setZoom(zoom_); +#endif + } break; + + case kTagSetDefaultZoom: { + SfizzSettings settings; + char zoom[64]; + sprintf(zoom, "%i", static_cast(zoom_ * 100)); + zoom[sizeof(zoom) - 1] = '\0'; + settings.store("default_zoom", zoom); + } break; + default: + if (tag >= kTagFirstChangePanel && tag <= kTagLastChangePanel) { + int panelId = tag - kTagFirstChangePanel; + ctrl.uiSendValue(EditId::UIActivePanel, static_cast(panelId)); + setActivePanel(panelId); + } + break; + } +} + +void Editor::Impl::enterOrLeaveEdit(CControl* ctl, bool enter) +{ + int32_t tag = ctl->getTag(); + EditId id; + + switch (tag) { + case kTagSetMainVolume: id = EditId::Volume; break; + case kTagSetNumVoices: id = EditId::Polyphony; break; + case kTagSetOversampling: id = EditId::Oversampling; break; + case kTagSetPreloadSize: id = EditId::PreloadSize; break; + case kTagSetScalaRootKey: id = EditId::ScalaRootKey; break; + case kTagSetTuningFrequency: id = EditId::TuningFrequency; break; + case kTagSetStretchedTuning: id = EditId::StretchTuning; break; + case kTagSetCCVolume: id = editIdForCC(7); break; + case kTagSetCCPan: id = editIdForCC(10); break; + default: return; + } + + EditorController& ctrl = *ctrl_; + if (enter) + ctrl.uiBeginSend(id); + else + ctrl.uiEndSend(id); +} + +void Editor::Impl::controlBeginEdit(CControl* ctl) +{ + enterOrLeaveEdit(ctl, true); +} + +void Editor::Impl::controlEndEdit(CControl* ctl) +{ + enterOrLeaveEdit(ctl, false); +} + +void Editor::Impl::onThemeChanged() +{ + for (std::function &function : OnThemeChanged) { + if (function) + function(); + } + if (CFrame* frame = frame_) + frame->invalid(); +} diff --git a/plugins/editor/src/editor/Editor.h b/plugins/editor/src/editor/Editor.h new file mode 100644 index 0000000..dd3aeba --- /dev/null +++ b/plugins/editor/src/editor/Editor.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include +class EditorController; + +#include "utility/vstgui_before.h" +#include "vstgui/lib/vstguifwd.h" +#include "utility/vstgui_after.h" +using VSTGUI::CFrame; + +class Editor { +public: + static const int viewWidth; + static const int viewHeight; + + explicit Editor(EditorController& ctrl); + ~Editor(); + + void open(CFrame& frame); + void close(); + +private: + struct Impl; + std::unique_ptr impl_; +}; diff --git a/plugins/editor/src/editor/EditorController.h b/plugins/editor/src/editor/EditorController.h new file mode 100644 index 0000000..bbe8011 --- /dev/null +++ b/plugins/editor/src/editor/EditorController.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "EditValue.h" +#include +#include +#include +#include +enum class EditId : int; + +class EditorController { +public: + virtual ~EditorController() {} + + // called by Editor + virtual void uiSendValue(EditId id, const EditValue& v) = 0; + virtual void uiBeginSend(EditId id) = 0; + virtual void uiEndSend(EditId id) = 0; + virtual void uiSendMIDI(const uint8_t* msg, uint32_t len) = 0; + virtual void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) = 0; + class Receiver; + void decorate(Receiver* r) { r_ = r; } + + class Receiver { + public: + virtual ~Receiver() {} + virtual void uiReceiveValue(EditId id, const EditValue& v) = 0; + virtual void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) = 0; + }; + + // called by DSP + void uiReceiveValue(EditId id, const EditValue& v); + void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args); + +private: + Receiver* r_ = nullptr; +}; + +inline void EditorController::uiReceiveValue(EditId id, const EditValue& v) +{ + if (r_) + r_->uiReceiveValue(id, v); +} + +inline void EditorController::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) +{ + if (r_) + r_->uiReceiveMessage(path, sig, args); +} diff --git a/plugins/editor/src/editor/EditorLibs.cpp b/plugins/editor/src/editor/EditorLibs.cpp new file mode 100644 index 0000000..af8e9d1 --- /dev/null +++ b/plugins/editor/src/editor/EditorLibs.cpp @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#define STB_IMAGE_IMPLEMENTATION +#include "EditorLibs.h" diff --git a/plugins/editor/src/editor/EditorLibs.h b/plugins/editor/src/editor/EditorLibs.h new file mode 100644 index 0000000..ecd11f1 --- /dev/null +++ b/plugins/editor/src/editor/EditorLibs.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include diff --git a/plugins/editor/src/editor/GUIComponents.cpp b/plugins/editor/src/editor/GUIComponents.cpp new file mode 100644 index 0000000..6b2a6dd --- /dev/null +++ b/plugins/editor/src/editor/GUIComponents.cpp @@ -0,0 +1,1350 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "GUIDefs.h" +#include "GUIComponents.h" +#include "ColorHelpers.h" +#include +#include + +#include "utility/vstgui_before.h" +#include "vstgui/lib/cdrawcontext.h" +#include "vstgui/lib/cgraphicspath.h" +#include "vstgui/lib/cvstguitimer.h" +#include "vstgui/lib/cframe.h" +#include "utility/vstgui_after.h" +#include "absl/strings/numbers.h" + +using namespace gui; + +/// +SBoxContainer::SBoxContainer(const CRect& size) + : CViewContainer(size) +{ + CViewContainer::setBackgroundColor(kColorTransparent); +} + +void SBoxContainer::setCornerRadius(CCoord radius) +{ + cornerRadius_ = radius; + invalid(); +} + +void SBoxContainer::setBackgroundColor(const CColor& color) +{ + backgroundColor_ = color; + invalid(); +} + +CColor SBoxContainer::getBackgroundColor() const +{ + return backgroundColor_; +} + +void SBoxContainer::drawRect(CDrawContext* dc, const CRect& updateRect) +{ + CRect bounds = getViewSize(); + + dc->setDrawMode(kAntiAliasing); + + SharedPointer path = owned(dc->createGraphicsPath()); + path->addRoundRect(bounds, cornerRadius_); + + dc->setFillColor(backgroundColor_); + dc->drawGraphicsPath(path.get(), CDrawContext::kPathFilled); + + CViewContainer::drawRect(dc, updateRect); +} + +/// +STitleContainer::STitleContainer(const CRect& size, UTF8StringPtr text) + : SBoxContainer(size), text_(text ? text : ""), titleFont_(kNormalFont) +{ +} + +void STitleContainer::setTitleFont(CFontRef font) +{ + titleFont_ = font; + invalid(); +} + +void STitleContainer::setTitleFontColor(CColor color) +{ + titleFontColor_ = color; + invalid(); +} + +void STitleContainer::setTitleBackgroundColor(CColor color) +{ + titleBackgroundColor_ = color; + invalid(); +} + +void STitleContainer::drawRect(CDrawContext* dc, const CRect& updateRect) +{ + SBoxContainer::drawRect(dc, updateRect); + + CRect bounds = getViewSize(); + CCoord cornerRadius = cornerRadius_; + + dc->setDrawMode(kAntiAliasing); + + CCoord fontHeight = titleFont_->getSize(); + CCoord titleHeight = fontHeight + 8.0; + + CRect titleBounds = bounds; + titleBounds.bottom = titleBounds.top + titleHeight; + + SharedPointer path = owned(dc->createGraphicsPath()); + path->beginSubpath(titleBounds.getBottomRight()); + path->addLine(titleBounds.getBottomLeft()); + path->addArc(CRect(titleBounds.left, titleBounds.top, titleBounds.left + 2.0 * cornerRadius, titleBounds.top + 2.0 * cornerRadius), 180., 270., true); + path->addArc(CRect(titleBounds.right - 2.0 * cornerRadius, titleBounds.top, titleBounds.right, titleBounds.top + 2.0 * cornerRadius), 270., 360., true); + path->closeSubpath(); + + dc->setFillColor(titleBackgroundColor_); + dc->drawGraphicsPath(path, CDrawContext::kPathFilled); + + dc->setFont(titleFont_); + dc->setFontColor(titleFontColor_); + dc->drawString(text_.c_str(), titleBounds, kCenterText); +} + +/// +void SFileDropTarget::setFileDropFunction(FileDropFunction f) +{ + dropFunction_ = std::move(f); +} + +DragOperation SFileDropTarget::onDragEnter(DragEventData data) +{ + op_ = isFileDrop(data.drag) ? + DragOperation::Copy : DragOperation::None; + return op_; +} + +DragOperation SFileDropTarget::onDragMove(DragEventData data) +{ + (void)data; + return op_; +} + +void SFileDropTarget::onDragLeave(DragEventData data) +{ + (void)data; + op_ = DragOperation::None; +} + +bool SFileDropTarget::onDrop(DragEventData data) +{ + if (op_ != DragOperation::Copy || !isFileDrop(data.drag)) + return false; + + IDataPackage::Type type; + const void* bytes; + uint32_t size = data.drag->getData(0, bytes, type); + std::string path(reinterpret_cast(bytes), size); + + if (dropFunction_) + dropFunction_(path); + + return true; +} + +bool SFileDropTarget::isFileDrop(IDataPackage* package) +{ + return package->getCount() == 1 && + package->getDataType(0) == IDataPackage::kFilePath; +} + +/// +SValueMenu::SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag) + : CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this))) +{ + setListener(listener); + setTag(tag); + setWheelInc(0.0f); +} + +void SValueMenu::setHoverColor(const CColor& color) +{ + hoverColor_ = color; + invalid(); +} + +CMenuItem* SValueMenu::addEntry(CMenuItem* item, float value, int32_t index) +{ + if (index < 0 || index > getNbEntries()) { + menuItems_.emplace_back(owned(item)); + menuItemValues_.emplace_back(value); + } + else + { + menuItems_.insert(menuItems_.begin() + index, owned(item)); + menuItemValues_.insert(menuItemValues_.begin() + index, value); + } + return item; +} + +CMenuItem* SValueMenu::addEntry(const UTF8String& title, float value, int32_t index, int32_t itemFlags) +{ + if (title == "-") + return addSeparator(index); + CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags); + return addEntry(item, value, index); +} + +CMenuItem* SValueMenu::addSeparator(int32_t index) +{ + CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator); + return addEntry(item, 0.0f, index); +} + +int32_t SValueMenu::getNbEntries() const +{ + return static_cast(menuItems_.size()); +} + +void SValueMenu::draw(CDrawContext* dc) +{ + CColor backupColor = fontColor; + if (hovered_) + fontColor = hoverColor_; + CParamDisplay::draw(dc); + if (hovered_) + fontColor = backupColor; +} + +CMouseEventResult SValueMenu::onMouseEntered(CPoint& where, const CButtonState& buttons) +{ + hovered_ = true; + invalid(); + return CParamDisplay::onMouseEntered(where, buttons); +} + +CMouseEventResult SValueMenu::onMouseExited(CPoint& where, const CButtonState& buttons) +{ + hovered_ = false; + invalid(); + return CParamDisplay::onMouseExited(where, buttons); +} + +CMouseEventResult SValueMenu::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + (void)where; + + if (buttons & (kLButton|kRButton|kApple)) { + CFrame* frame = getFrame(); + CRect bounds = getViewSize(); + + CPoint frameWhere = bounds.getBottomLeft(); + this->localToFrame(frameWhere); + + auto self = shared(this); + frame->doAfterEventProcessing([self, frameWhere]() { + if (CFrame* frame = self->getFrame()) { + SharedPointer menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle)); + for (const SharedPointer& item : self->menuItems_) { + menu->addEntry(item); + item->remember(); // above call does not increment refcount + } + menu->setFont(self->getFont()); + menu->setFontColor(self->getFontColor()); + menu->setBackColor(self->getBackColor()); + menu->popup(frame, frameWhere + CPoint(0.0, 1.0)); + } + }); + return kMouseDownEventHandledButDontNeedMovedOrUpEvents; + } + + return kMouseEventNotHandled; +} + +bool SValueMenu::onWheel(const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) +{ + (void)where; + (void)buttons; + + if (axis != kMouseWheelAxisY) + return false; + + float wheelInc = getWheelInc(); + if (wheelInc != 0) { + float oldValue = getValue(); + setValueNormalized(getValueNormalized() + distance * wheelInc); + if (getValue() != oldValue) { + valueChanged(); + invalid(); + } + } + return true; +} + +void SValueMenu::onItemClicked(int32_t index) +{ + float oldValue = getValue(); + setValue(menuItemValues_[index]); + if (getValue() != oldValue) { + valueChanged(); + invalid(); + } +} + +/// +SActionMenu::SActionMenu(const CRect& bounds, IControlListener* listener) + : CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this))) +{ + setListener(listener); + + auto toString = [](float, std::string& result, CParamDisplay* display) { + result = static_cast(display)->getTitle(); + return true; + }; + + setValueToStringFunction2(toString); +} + +void SActionMenu::setTitle(std::string title) +{ + title_ = std::move(title); + invalid(); +} + +void SActionMenu::setHoverColor(const CColor& color) +{ + hoverColor_ = color; + invalid(); +} + +CMenuItem* SActionMenu::addEntry(CMenuItem* item, int32_t tag, int32_t index) +{ + if (index < 0 || index > getNbEntries()) { + menuItems_.emplace_back(owned(item)); + menuItemTags_.emplace_back(tag); + } + else + { + menuItems_.insert(menuItems_.begin() + index, owned(item)); + menuItemTags_.insert(menuItemTags_.begin() + index, tag); + } + return item; +} + +CMenuItem* SActionMenu::addEntry(const UTF8String& title, int32_t tag, int32_t index, int32_t itemFlags) +{ + if (title == "-") + return addSeparator(index); + CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags); + return addEntry(item, tag, index); +} + +CMenuItem* SActionMenu::addSeparator(int32_t index) +{ + CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator); + return addEntry(item, 0.0f, index); +} + +int32_t SActionMenu::getNbEntries() const +{ + return static_cast(menuItems_.size()); +} + +void SActionMenu::draw(CDrawContext* dc) +{ + CColor backupColor = fontColor; + if (hovered_) + fontColor = hoverColor_; + CParamDisplay::draw(dc); + if (hovered_) + fontColor = backupColor; +} + +CMouseEventResult SActionMenu::onMouseEntered(CPoint& where, const CButtonState& buttons) +{ + hovered_ = true; + invalid(); + return CParamDisplay::onMouseEntered(where, buttons); +} + +CMouseEventResult SActionMenu::onMouseExited(CPoint& where, const CButtonState& buttons) +{ + hovered_ = false; + invalid(); + return CParamDisplay::onMouseExited(where, buttons); +} + +CMouseEventResult SActionMenu::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + (void)where; + + if (buttons & (kLButton|kRButton|kApple)) { + CFrame* frame = getFrame(); + CRect bounds = getViewSize(); + + CPoint frameWhere = bounds.getBottomLeft(); + this->localToFrame(frameWhere); + + auto self = shared(this); + frame->doAfterEventProcessing([self, frameWhere]() { + if (CFrame* frame = self->getFrame()) { + SharedPointer menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle)); + for (const SharedPointer& item : self->menuItems_) { + menu->addEntry(item); + item->remember(); // above call does not increment refcount + } + menu->setFont(self->getFont()); + menu->setFontColor(self->getFontColor()); + menu->setBackColor(self->getBackColor()); + menu->popup(frame, frameWhere + CPoint(0.0, 1.0)); + } + }); + return kMouseDownEventHandledButDontNeedMovedOrUpEvents; + } + + return kMouseEventNotHandled; +} + +void SActionMenu::onItemClicked(int32_t index) +{ + setTag(menuItemTags_[index]); + setValue(1.0f); + if (listener) + listener->valueChanged(this); + setValue(0.0f); + if (listener) + listener->valueChanged(this); +} + +/// +CMouseEventResult SHoverButton::onMouseEntered(CPoint& where, const CButtonState& buttons) { + hovered_ = true; + if (OnHoverEnter) + OnHoverEnter(); + invalid(); + return CKickButton::onMouseEntered(where, buttons); +} + +CMouseEventResult SHoverButton::onMouseExited(CPoint& where, const CButtonState& buttons) { + hovered_ = false; + if (OnHoverLeave) + OnHoverLeave(); + invalid(); + return CKickButton::onMouseExited(where, buttons); +} + +void SHoverButton::draw(CDrawContext* dc) { + CPoint where(offset.x, offset.y); + bounceValue(); + if (hovered_) + where.y += heightOfOneImage; + if (getDrawBackground()) + getDrawBackground()->draw(dc, getViewSize(), where); + setDirty(false); +} + +/// +void STextButton::setHighlightColor(const CColor& color) +{ + highlightColor_ = color; + invalid(); +} + +void STextButton::setInactiveColor(const CColor& color) +{ + inactiveColor_ = color; + invalid(); +} + +void STextButton::setInactive(bool b) +{ + inactive_ = b; + invalid(); +} + +void STextButton::setHighlighted(bool b) +{ + highlighted_ = b; + invalid(); +} + +void STextButton::draw(CDrawContext* context) +{ + CColor backupColor = textColor; + if (inactive_) + textColor = inactiveColor_; // textColor is protected + else if (hovered_ || highlighted_) + textColor = highlightColor_; + else if (inactive_) + textColor = inactiveColor_; + CTextButton::draw(context); + textColor = backupColor; +} + + +CMouseEventResult STextButton::onMouseEntered (CPoint& where, const CButtonState& buttons) +{ + hovered_ = true; + if (OnHoverEnter) + OnHoverEnter(); + invalid(); + return CTextButton::onMouseEntered(where, buttons); +} + +CMouseEventResult STextButton::onMouseExited (CPoint& where, const CButtonState& buttons) +{ + hovered_ = false; + if (OnHoverLeave) + OnHoverLeave(); + invalid(); + return CTextButton::onMouseExited(where, buttons); +} + +/// +SStyledKnob::SStyledKnob(const CRect& size, IControlListener* listener, int32_t tag) + : CKnobBase(size, listener, tag, nullptr) +{ +} + +void SStyledKnob::setActiveTrackColor(const CColor& color) +{ + if (activeTrackColor_ == color) + return; + activeTrackColor_ = color; + invalid(); +} + +void SStyledKnob::setInactiveTrackColor(const CColor& color) +{ + if (inactiveTrackColor_ == color) + return; + inactiveTrackColor_ = color; + invalid(); +} + +void SStyledKnob::setLineIndicatorColor(const CColor& color) +{ + if (lineIndicatorColor_ == color) + return; + lineIndicatorColor_ = color; + invalid(); +} + +void SStyledKnob::setRotatorColor(const CColor &color) +{ + if (rotatorColor_ == color) + return; + rotatorColor_ = color; + invalid(); +} + +void SStyledKnob::setFont(CFontRef font) +{ + if (font_ == font) + return; + font_ = font; + invalid(); +} + +void SStyledKnob::setFontColor(CColor fontColor) +{ + if (fontColor_ == fontColor) + return; + fontColor_ = fontColor; + invalid(); +} + +void SStyledKnob::setValueToStringFunction(ValueToStringFunction func) +{ + valueToStringFunction_ = std::move(func); + invalid(); +} + +void SStyledKnob::draw(CDrawContext* dc) +{ + const CCoord lineWidth = 4.0; + const CCoord indicatorLineLength = 8.0; + const CCoord angleSpread = 250.0; + const CCoord angle1 = 270.0 - 0.5 * angleSpread; + const CCoord angle2 = 270.0 + 0.5 * angleSpread; + + dc->setDrawMode(kAntiAliasing); + + const CRect bounds = getViewSize(); + + // compute inner bounds + CRect rect(bounds); + rect.setWidth(std::min(rect.getWidth(), rect.getHeight())); + rect.setHeight(rect.getWidth()); + rect.centerInside(bounds); + rect.extend(-lineWidth, -lineWidth); + + CRect knobRect(rect); + knobRect.centerInside(bounds); + knobRect.extend(-lineWidth, -lineWidth); + + SharedPointer path; + + // rotator + path = owned(dc->createGraphicsPath()); + path->addEllipse(knobRect); + + dc->setFillColor(rotatorColor_); + dc->drawGraphicsPath(path, CDrawContext::kPathFilled); + + // inactive track + path = owned(dc->createGraphicsPath()); + path->addArc(rect, angle1, angle2, true); + + dc->setFrameColor(inactiveTrackColor_); + dc->setLineWidth(lineWidth); + dc->setLineStyle(kLineSolid); + dc->drawGraphicsPath(path, CDrawContext::kPathStroked); + + // active track + const CCoord v = getValueNormalized(); + const CCoord vAngle = angle1 + v * angleSpread; + path = owned(dc->createGraphicsPath()); + path->addArc(rect, angle1, vAngle, true); + + dc->setFrameColor(activeTrackColor_); + dc->setLineWidth(lineWidth + 0.5); + dc->setLineStyle(kLineSolid); + dc->drawGraphicsPath(path, CDrawContext::kPathStroked); + + // indicator line + { + CCoord module1 = 0.5 * rect.getWidth() - indicatorLineLength; + CCoord module2 = 0.5 * rect.getWidth(); + std::complex c1 = std::polar(module1, vAngle * (M_PI / 180.0)); + std::complex c2 = std::polar(module2, vAngle * (M_PI / 180.0)); + + CPoint p1(c1.real(), c1.imag()); + CPoint p2(c2.real(), c2.imag()); + p1.offset(rect.getCenter()); + p2.offset(rect.getCenter()); + + dc->setFrameColor(lineIndicatorColor_); + dc->setLineWidth(1.0); + dc->setLineStyle(kLineSolid); + dc->drawLine(p1, p2); + } + + if (valueToStringFunction_ && fontColor_.alpha > 0 && !hideValue_) { + std::string text; + if (valueToStringFunction_(getValue(), text)) { + dc->setFont(font_); + dc->setFontColor(fontColor_); + dc->drawString(text.c_str(), bounds); + } + } +} + +void CFilledRect::draw(CDrawContext* dc) +{ + CRect bounds = getViewSize(); + dc->setFillColor(color_); + bool isRounded = radius_ > 0.0; + if (isRounded) { + auto roundRect = owned(dc->createRoundRectGraphicsPath(bounds, radius_)); + dc->drawGraphicsPath(roundRect, CDrawContext::kPathFilled); + } else { + dc->drawRect(bounds, kDrawFilled); + } +} + +/// +SKnobCCBox::SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag) + : CViewContainer(size), + label_(makeOwned(CRect())), + valueEdit_(makeOwned(CRect(), listener, tag)), + knob_(makeOwned(CRect(), listener, tag)), + ccLabel_(makeOwned(CRect())), + shadingRectangle_(makeOwned(CRect())), + menuEntry_(makeOwned("Use HDCC", tag)), + menuListener_(owned(new MenuListener(*this))) +{ + setBackgroundColor(kColorTransparent); + + label_->setText("Parameter"); + label_->setBackColor(kColorTransparent); + label_->setFrameColor(kColorTransparent); + label_->setFontColor(kBlackCColor); + label_->setStyle(CParamDisplay::kRoundRectStyle); + label_->setRoundRectRadius(5.0); + + knob_->setLineIndicatorColor(kBlackCColor); + + ccLabel_->setText("CC 1"); + ccLabel_->setStyle(CParamDisplay::kRoundRectStyle); + ccLabel_->setRoundRectRadius(5.0); + ccLabel_->setFrameColor(kColorTransparent); + ccLabel_->setFontColor(kWhiteCColor); + + valueEdit_->setBackColor(kColorTransparent); + valueEdit_->setFrameColor(kColorTransparent); + valueEdit_->setFontColor(kBlackCColor); + valueEdit_->registerViewListener(this); + setHDMode(false); + valueEdit_->setVisible(false); + + shadingRectangle_->setVisible(false); + + addView(label_); + label_->remember(); + addView(knob_); + knob_->remember(); + addView(shadingRectangle_); + shadingRectangle_->remember(); + addView(valueEdit_); + valueEdit_->remember(); + addView(ccLabel_); + ccLabel_->remember(); + updateViewColors(); + updateViewSizes(); +} + +SKnobCCBox::~SKnobCCBox() +{ + valueEdit_->unregisterViewListener(this); +} + +void SKnobCCBox::setHDMode(bool mode) +{ + if (mode) { + auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + std::string s = std::to_string(value + 0.005f); + text = s.substr(0, 4); + return true; + }; + knob_->setValueToStringFunction([valueToString](float value, std::string& text) { + return valueToString(value, text, nullptr); + }); + valueEdit_->setValueToStringFunction2(valueToString); + + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { + float value; + if (absl::SimpleAtof(txt, &value)) { + result = value; + return true; + } + + return false; + }); + menuEntry_->setTitle("Use low-res. CC"); + } else { + auto valueToString = [](float value, std::string& text, VSTGUI::CParamDisplay*) -> bool { + text = std::to_string(std::lround(value * 127)); + return true; + }; + knob_->setValueToStringFunction([valueToString](float value, std::string& text) { + return valueToString(value, text, nullptr); + }); + valueEdit_->setValueToStringFunction2(valueToString); + + valueEdit_->setStringToValueFunction([](UTF8StringPtr txt, float& result, CTextEdit*) -> bool { + float value; + if (absl::SimpleAtof(txt, &value)) { + result = value / 127.0f; + return true; + } + + return false; + }); + menuEntry_->setTitle("Use high-res. CC"); + } + + hdMode_ = mode; + valueEdit_->setValue(valueEdit_->getValue()); + invalid(); +} + +CMouseEventResult SKnobCCBox::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + if (buttons.isRightButton()) { + CFrame* frame = getFrame(); + CPoint frameWhere = where; + frameWhere.offset(-getViewSize().left, -getViewSize().top); + this->localToFrame(frameWhere); + + auto self = shared(this); + frame->doAfterEventProcessing([self, frameWhere]() { + if (CFrame* frame = self->getFrame()) { + SharedPointer menu = + owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle)); + menu->addEntry(self->menuEntry_); + self->menuEntry_->remember(); // above call does not increment refcount + + menu->setFont(self->getValueEditFont()); + menu->setFontColor(self->getValueEditFontColor()); + menu->setBackColor(self->getValueEditBackColor()); + menu->popup(frame, frameWhere); + } + }); + return kMouseEventHandled; + } else if (buttons.isDoubleClick() && !valueEdit_->isVisible()) { + valueEdit_->setVisible(true); + shadingRectangle_->setVisible(true); + knob_->setHideValue(true); + valueEdit_->takeFocus(); + invalid(); + return kMouseEventHandled; + } + + return CViewContainer::onMouseDown(where, buttons); +} + +void SKnobCCBox::viewLostFocus (CView* view) +{ + if (view == valueEdit_.get()) { + shadingRectangle_->setVisible(false); + valueEdit_->setVisible(false); + knob_->setHideValue(false); + invalid(); + } +} + +void SKnobCCBox::setHue(float hue) +{ + hue_ = hue; + updateViewColors(); +} + +void SKnobCCBox::setNameLabelFont(CFontRef font) +{ + label_->setFont(font); + updateViewSizes(); +} + +void SKnobCCBox::setValueEditFont(CFontRef font) +{ + label_->setFont(font); + updateViewSizes(); +} + +void SKnobCCBox::setCCLabelFont(CFontRef font) +{ + ccLabel_->setFont(font); + updateViewSizes(); +} + +void SKnobCCBox::setValue(float value) +{ + float oldValue = knob_->getValue(); + knob_->setValue(value); + valueEdit_->setValue(value); + if (value != oldValue) + invalid(); +} + +void SKnobCCBox::setDefaultValue(float value) +{ + knob_->setDefaultValue(value); + valueEdit_->setDefaultValue(value); +} + +void SKnobCCBox::updateViewSizes() +{ + const CRect size = getViewSize(); + const CCoord ypad = 4.0; + + const CFontRef nameFont = label_->getFont(); + const CFontRef ccFont = ccLabel_->getFont(); + const CFontRef valueFont = valueEdit_->getFont(); + + nameLabelSize_ = CRect(0.0, 0.0, size.getWidth(), nameFont->getSize() + 2 * ypad); + ccLabelSize_ = CRect(0.0, size.getHeight() - ccFont->getSize() - 2 * ypad, size.getWidth(), size.getHeight()); + knobSize_ = CRect(0.0, nameLabelSize_.bottom, size.getWidth(), ccLabelSize_.top); + valueEditSize_ = CRect( + size.getWidth() / 2 - valueFont->getSize(), + size.getHeight() / 2 - valueFont->getSize() / 2, + size.getWidth() / 2 + valueFont->getSize(), + size.getHeight() / 2 + valueFont->getSize() / 2 + ); + + // remove knob side areas + CCoord side = std::max(0.0, knobSize_.getWidth() - knobSize_.getHeight()); + knobSize_.extend(-0.5 * side, 0.0); + shadingRectangleSize_ = knobSize_; + shadingRectangleSize_.bottom -= ypad; + + // + label_->setViewSize(nameLabelSize_); + knob_->setViewSize(knobSize_); + ccLabel_->setViewSize(ccLabelSize_); + valueEdit_->setViewSize(valueEditSize_); + shadingRectangle_->setViewSize(shadingRectangleSize_); + + invalid(); +} + +void SKnobCCBox::updateViewColors() +{ + const float knobLuma = 0.4; + const float ccLuma = 0.25; + + SColorHCY knobActiveTrackColor { hue_, 1.0, knobLuma }; + SColorHCY knobInactiveTrackColor { 0.0, 0.0, knobLuma }; + knob_->setActiveTrackColor(knobActiveTrackColor.toColor()); + knob_->setInactiveTrackColor(knobInactiveTrackColor.toColor()); + + SColorHCY ccLabelColor { hue_, 1.0, ccLuma }; + ccLabel_->setBackColor(ccLabelColor.toColor()); + + invalid(); +} + +/// +SControlsPanel::SControlsPanel(const CRect& size) + : CScrollView( + size, CRect(), + CScrollView::kVerticalScrollbar|CScrollView::kDontDrawFrame|CScrollView::kAutoHideScrollbars), + listener_(new ControlSlotListener(this)) +{ + // slot 0 always exists, keep the default style on the views there + getOrCreateSlot(0); + + setBackgroundColor(kColorTransparent); + + setScrollbarWidth(10.0); + + relayoutTrigger_ = makeOwned( + [this](CVSTGUITimer* timer) { timer->stop(); updateLayout(); }, + 1, false); +} + +void SControlsPanel::setControlUsed(uint32_t index, bool used) +{ + ControlSlot* slot = getSlot(index); + if (!slot && !used) + return; + if (!slot) + slot = getOrCreateSlot(index); + if (used != slot->used) { + slot->used = used; + relayoutTrigger_->start(); + } +} + +std::string SControlsPanel::getDefaultLabelText(uint32_t index) +{ + (void)index; + return {}; +} + +SControlsPanel::ControlSlot* SControlsPanel::getSlot(uint32_t index) +{ + ControlSlot* slot = nullptr; + if (index < slots_.size()) + slot = slots_[index].get(); + return slot; +} + +SControlsPanel::ControlSlot* SControlsPanel::getOrCreateSlot(uint32_t index) +{ + ControlSlot* slot = getSlot(index); + if (slot) + return slot; + + if (index + 1 > slots_.size()) + slots_.resize(index + 1); + + slot = new ControlSlot; + slots_[index].reset(slot); + + CRect boxSize { 0.0, 0.0, 120.0, 90.0 }; + SharedPointer box = makeOwned(boxSize, listener_.get(), index); + slot->box = box; + slot->box->setCCLabelText(("CC " + std::to_string(index)).c_str()); + + syncSlotStyle(index); + + return slot; +} + +void SControlsPanel::setControlValue(uint32_t index, float value) +{ + ControlSlot* slot = getOrCreateSlot(index); + SKnobCCBox* box = slot->box; + float oldValue = box->getValue(); + box->setValue(value); + if (box->getValue() != oldValue) + box->invalid(); +} + +void SControlsPanel::setControlDefaultValue(uint32_t index, float value) +{ + ControlSlot* slot = getOrCreateSlot(index); + SKnobCCBox* box = slot->box; + box->setDefaultValue(value); +} + +void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text) +{ + ControlSlot* slot = getOrCreateSlot(index); + SKnobCCBox* box = slot->box; + if (text && text[0] != '\0') + box->setNameLabelText(text); + else + box->setNameLabelText(getDefaultLabelText(index).c_str()); + box->invalid(); +} + +void SControlsPanel::setNameLabelFont(CFontRef font) +{ + slots_[0]->box->setNameLabelFont(font); + slots_[0]->box->setValueEditFont(font); + syncAllSlotStyles(); +} + +void SControlsPanel::setNameLabelFontColor(CColor color) +{ + slots_[0]->box->setNameLabelFontColor(color); + slots_[0]->box->setValueEditFontColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setNameLabelBackColor(CColor color) +{ + slots_[0]->box->setNameLabelBackColor(color); + slots_[0]->box->setValueEditBackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setCCLabelFont(CFontRef font) +{ + slots_[0]->box->setCCLabelFont(font); + syncAllSlotStyles(); +} + +void SControlsPanel::setCCLabelBackColor(CColor color) +{ + slots_[0]->box->setCCLabelBackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setCCLabelFontColor(CColor color) +{ + slots_[0]->box->setCCLabelFontColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setValueEditBackColor(CColor color) +{ + slots_[0]->box->setValueEditBackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setShadingRectangleColor(CColor color) +{ + slots_[0]->box->setShadingRectangleColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setValueEditFontColor(CColor color) +{ + slots_[0]->box->setValueEditFontColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobActiveTrackColor(CColor color) +{ + slots_[0]->box->setKnobActiveTrackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobInactiveTrackColor(CColor color) +{ + slots_[0]->box->setKnobInactiveTrackColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobLineIndicatorColor(CColor color) +{ + slots_[0]->box->setKnobLineIndicatorColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobRotatorColor(CColor color) +{ + slots_[0]->box->setKnobRotatorColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobFont(CFontRef font) +{ + slots_[0]->box->setKnobFont(font); + syncAllSlotStyles(); +} + +void SControlsPanel::setKnobFontColor(CColor color) +{ + slots_[0]->box->setKnobFontColor(color); + syncAllSlotStyles(); +} + +void SControlsPanel::recalculateSubViews() +{ + CScrollView::recalculateSubViews(); + + // maybe the operation just created the scroll bar + if (CScrollbar* vsb = getVerticalScrollbar()) { + // update scrollbar style + vsb->setFrameColor(kColorTransparent); + vsb->setBackgroundColor(kColorTransparent); + vsb->setScrollerColor(kColorControlsScrollerTransparency); + } +} + +void SControlsPanel::updateLayout() +{ + removeAll(); + + const CRect viewBounds = getViewSize(); + + bool isFirstSlot = true; + CCoord itemWidth {}; + CCoord itemHeight {}; + CCoord itemOffsetX {}; + + int numColumns {}; + const CCoord horizontalPadding = 4.0; + CCoord verticalPadding = 4.0; + CCoord interRowPadding = {}; + CCoord interColumnPadding = 8.0; + + int currentRow = 0; + int currentColumn = 0; + int containerBottom = 0; + + uint32_t numSlots = static_cast(slots_.size()); + for (uint32_t i = 0; i < numSlots; ++i) { + ControlSlot* slot = slots_[i].get(); + if (!slot || !slot->used) + continue; + + CViewContainer* box = slot->box; + + if (isFirstSlot) { + itemWidth = box->getWidth(); + itemHeight = box->getHeight(); + isFirstSlot = false; + numColumns = int((viewBounds.getWidth() - horizontalPadding) / + (itemWidth + interColumnPadding)); + numColumns = std::max(1, numColumns); + itemOffsetX = (viewBounds.getWidth() - horizontalPadding - + numColumns * (itemWidth + interColumnPadding)) / 2.0; + + int maxRowsShown = int((viewBounds.getHeight() - 2 * verticalPadding) / itemHeight); + if (maxRowsShown > 1) + interRowPadding = (viewBounds.getHeight() - 2 * verticalPadding - itemHeight * maxRowsShown) / (maxRowsShown - 1); + } + + CRect itemBounds = box->getViewSize(); + itemBounds.moveTo( + itemOffsetX + horizontalPadding + currentColumn * (interColumnPadding + itemWidth), + verticalPadding + currentRow * (interRowPadding + itemHeight)); + + box->setViewSize(itemBounds); + + containerBottom = itemBounds.bottom; + + addView(box); + box->remember(); + + if (++currentColumn == numColumns) { + currentColumn = 0; + ++currentRow; + } + } + + setContainerSize(CRect(0.0, 0.0, viewBounds.getWidth(), containerBottom + verticalPadding)); + + invalid(); +} + +void SControlsPanel::syncAllSlotStyles() +{ + uint32_t count = static_cast(slots_.size()); + for (uint32_t index = 0; index < count; ++index) + syncSlotStyle(index); +} + +void SControlsPanel::syncSlotStyle(uint32_t index) +{ + if (index >= slots_.size()) + return; + + const SKnobCCBox* ref = slots_[0]->box; + SKnobCCBox* cur = slots_[index]->box; + + if (!cur) + return; + + if (cur != ref) { + cur->setNameLabelFont(ref->getNameLabelFont()); + cur->setNameLabelFontColor(ref->getNameLabelFontColor()); + cur->setNameLabelBackColor(ref->getNameLabelBackColor()); + + cur->setValueEditFont(ref->getValueEditFont()); + cur->setValueEditFontColor(ref->getValueEditFontColor()); + + cur->setShadingRectangleColor(ref->getShadingRectangleColor()); + + cur->setCCLabelFont(ref->getCCLabelFont()); + cur->setCCLabelFontColor(ref->getCCLabelFontColor()); + cur->setCCLabelBackColor(ref->getCCLabelBackColor()); + + cur->setKnobActiveTrackColor(ref->getKnobActiveTrackColor()); + cur->setKnobInactiveTrackColor(ref->getKnobInactiveTrackColor()); + cur->setKnobLineIndicatorColor(ref->getKnobLineIndicatorColor()); + cur->setKnobRotatorColor(ref->getKnobRotatorColor()); + cur->setKnobFont(ref->getKnobFont()); + cur->setKnobFontColor(ref->getKnobFontColor()); + } + + cur->invalid(); +} + +void SControlsPanel::ControlSlotListener::valueChanged(CControl* pControl) +{ + if (panel_->ValueChangeFunction) + panel_->ValueChangeFunction(pControl->getTag(), pControl->getValue()); +} + +void SControlsPanel::ControlSlotListener::controlBeginEdit(CControl* pControl) +{ + if (panel_->BeginEditFunction) + panel_->BeginEditFunction(pControl->getTag()); +} + +void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl) +{ + if (panel_->EndEditFunction) + panel_->EndEditFunction(pControl->getTag()); +} + +/// +SLevelMeter::SLevelMeter(const CRect& size) + : CView(size) +{ +} + +void SLevelMeter::setValue(float value) +{ + if (value_ == value) + return; + + value_ = value; + + // instantiate the timer lazily + if (!timer_) { + const uint32_t interval = 10; + timer_ = makeOwned( + [this](CVSTGUITimer* timer) { + timer->stop(); + timerArmed_ = false; + invalid(); + }, interval, false); + } + + // defer the update, but do not rearm the timer + if (!timerArmed_) { + timerArmed_ = true; + timer_->start(); + } +} + +void SLevelMeter::draw(CDrawContext* dc) +{ + float dbValue = 20.0f * std::log10(value_); + float fill = (dbValue - dbMin_) / (dbMax_ - dbMin_); + fill = (fill < 0.0f) ? 0.0f : fill; + fill = (fill > 1.0f) ? 1.0f : fill; + + CRect largeBounds = getViewSize(); + CRect fillBounds = largeBounds; + fillBounds.top = largeBounds.bottom - fill * largeBounds.getHeight(); + + const CColor safeColor = safeFillColor_; + const CColor dangerColor = dangerFillColor_; + + CColor fillColor; + if (safeColor == dangerColor) { + fillColor = safeColor; + } + else { + float thres = dangerThreshold_; + float mix = (fill - thres) / (1.0f - thres); + mix = (mix < 0.0f) ? 0.0f : mix; + + CCoord safeH, safeS, safeV, safeA; + CCoord dangerH, dangerS, dangerV, dangerA; + safeColor.toHSV(safeH, safeS, safeV); + dangerColor.toHSV(dangerH, dangerS, dangerV); + safeA = safeColor.alpha / 255.0; + dangerA = dangerColor.alpha / 255.0; + + CCoord H, S, V, A; + H = safeH + mix * (dangerH - safeH); + S = safeS + mix * (dangerS - safeS); + V = safeV + mix * (dangerV - safeV); + A = safeA + mix * (dangerA - safeA); + + fillColor.fromHSV(H, S, V); + fillColor.alpha = static_cast(A * 255.0); + } + + CCoord radius = radius_; + bool isRounded = radius > 0.0; + + dc->setDrawMode(isRounded ? kAntiAliasing : kAliasing); + + SharedPointer largeRoundRect; + SharedPointer fillRoundRect; + + if (isRounded) { + largeRoundRect = owned(dc->createRoundRectGraphicsPath(largeBounds, radius)); + fillRoundRect = owned(dc->createRoundRectGraphicsPath(fillBounds, radius)); + } + + if (backColor_.alpha > 0) { + dc->setFillColor(backColor_); + if (!isRounded) + dc->drawRect(largeBounds, kDrawFilled); + else + dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathFilled); + } + + dc->setFrameColor(frameColor_); + dc->setFillColor(fillColor); + + if (!isRounded) { + if (fill > 0) + dc->drawRect(fillBounds, kDrawFilled); + dc->drawRect(largeBounds); + } + else { + if (fill > 0 && fillBounds.getHeight() >= radius) + dc->drawGraphicsPath(fillRoundRect, CDrawContext::kPathFilled); + dc->drawGraphicsPath(largeRoundRect, CDrawContext::kPathStroked); + } +} + +/// +SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color) + : CView(size), color_(color) +{ +} + +void SPlaceHolder::draw(CDrawContext* dc) +{ + const CRect bounds = getViewSize(); + dc->setDrawMode(kAliasing); + dc->setFrameColor(color_); + dc->drawRect(bounds); + dc->drawLine(bounds.getTopLeft(), bounds.getBottomRight()); + dc->drawLine(bounds.getTopRight(), bounds.getBottomLeft()); +} diff --git a/plugins/editor/src/editor/GUIComponents.h b/plugins/editor/src/editor/GUIComponents.h new file mode 100644 index 0000000..a0ce854 --- /dev/null +++ b/plugins/editor/src/editor/GUIComponents.h @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "GUIDefs.h" +#include +#include +#include +#include + +#include "utility/vstgui_before.h" +#include "vstgui/lib/controls/cslider.h" +#include "vstgui/lib/controls/cknob.h" +#include "vstgui/lib/controls/ctextlabel.h" +#include "vstgui/lib/controls/ctextedit.h" +#include "vstgui/lib/controls/cbuttons.h" +#include "vstgui/lib/controls/coptionmenu.h" +#include "vstgui/lib/controls/cscrollbar.h" +#include "vstgui/lib/controls/icontrollistener.h" +#include "vstgui/lib/cviewcontainer.h" +#include "vstgui/lib/cscrollview.h" +#include "vstgui/lib/ccolor.h" +#include "vstgui/lib/dragging.h" +#include "utility/vstgui_after.h" + +using namespace VSTGUI; + +/// +class SBoxContainer : public CViewContainer { +public: + explicit SBoxContainer(const CRect& size); + virtual ~SBoxContainer() {} + void setCornerRadius(CCoord radius); + void setBackgroundColor(const CColor& color) override; + CColor getBackgroundColor() const override; + +protected: + void drawRect(CDrawContext* dc, const CRect& updateRect) override; + +protected: + CCoord cornerRadius_ = 0.0; + CColor backgroundColor_; +}; + +/// +class STitleContainer : public SBoxContainer { +public: + explicit STitleContainer(const CRect& size, UTF8StringPtr text = nullptr); + ~STitleContainer() {} + + void setTitleFont(CFontRef font); + CFontRef getTitleFont() { return titleFont_; } + + void setTitleFontColor(CColor color); + CColor getTitleFontColor() const { return titleFontColor_; } + void setTitleBackgroundColor(CColor color); + CColor getTitleBackgroundColor() const { return titleBackgroundColor_; } + +protected: + void drawRect(CDrawContext* dc, const CRect& updateRect) override; + +private: + std::string text_; + CColor titleFontColor_; + CColor titleBackgroundColor_; + SharedPointer titleFont_; +}; + +/// +class SFileDropTarget : public IDropTarget, + public NonAtomicReferenceCounted { +public: + typedef std::function FileDropFunction; + void setFileDropFunction(FileDropFunction f); + +protected: + DragOperation onDragEnter(DragEventData data) override; + DragOperation onDragMove(DragEventData data) override; + void onDragLeave(DragEventData data) override; + bool onDrop(DragEventData data) override; + +private: + static bool isFileDrop(IDataPackage* package); + +private: + DragOperation op_ = DragOperation::None; + FileDropFunction dropFunction_; +}; + +/// +class SValueMenu : public CParamDisplay { +public: + explicit SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag); + CColor getHoverColor() const { return hoverColor_; } + void setHoverColor(const CColor& color); + CMenuItem* addEntry(CMenuItem* item, float value, int32_t index = -1); + CMenuItem* addEntry(const UTF8String& title, float value, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags); + CMenuItem* addSeparator(int32_t index = -1); + int32_t getNbEntries() const; + +protected: + void draw(CDrawContext* dc) override; + CMouseEventResult onMouseEntered(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseExited(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; + bool onWheel(const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override; + +private: + CColor hoverColor_; + bool hovered_ = false; + + // + class MenuListener; + + // + void onItemClicked(int32_t index); + + // + CMenuItemList menuItems_; + std::vector menuItemValues_; + SharedPointer menuListener_; + + // + class MenuListener : public IControlListener, public NonAtomicReferenceCounted { + public: + explicit MenuListener(SValueMenu& menu) : menu_(menu) {} + void valueChanged(CControl* control) override + { + menu_.onItemClicked(static_cast(control->getValue())); + } + private: + SValueMenu& menu_; + }; +}; + +/// +class SActionMenu : public CParamDisplay { +public: + explicit SActionMenu(const CRect& bounds, IControlListener* listener); + std::string getTitle() const { return title_; } + void setTitle(std::string title); + CColor getHoverColor() const { return hoverColor_; } + void setHoverColor(const CColor& color); + CMenuItem* addEntry(CMenuItem* item, int32_t tag, int32_t index = -1); + CMenuItem* addEntry(const UTF8String& title, int32_t tag, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags); + CMenuItem* addSeparator(int32_t index = -1); + int32_t getNbEntries() const; + +protected: + void draw(CDrawContext* dc) override; + CMouseEventResult onMouseEntered(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseExited(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; + +private: + std::string title_; + CColor hoverColor_; + bool hovered_ = false; + + class MenuListener; + + // + void onItemClicked(int32_t index); + + // + CMenuItemList menuItems_; + std::vector menuItemTags_; + SharedPointer menuListener_; + + // + class MenuListener : public IControlListener, public NonAtomicReferenceCounted { + public: + explicit MenuListener(SActionMenu& menu) : menu_(menu) {} + void valueChanged(CControl* control) override + { + menu_.onItemClicked(static_cast(control->getValue())); + } + private: + SActionMenu& menu_; + }; +}; + +/// +class SHoverButton : public CKickButton { +public: + SHoverButton( + const CRect& size, + IControlListener* listener, + int32_t tag, + CBitmap* background, + const CPoint& offset = CPoint (0, 0)) + : CKickButton(size, listener, tag, background, offset) + {} + + CMouseEventResult onMouseEntered(CPoint&, const CButtonState&) override; + CMouseEventResult onMouseExited(CPoint&, const CButtonState&) override; + void draw(CDrawContext*) override; + + std::function OnHoverEnter; + std::function OnHoverLeave; + +private: + bool hovered_ { false }; +}; + +/// +class STextButton: public CTextButton { +public: + STextButton(const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr) + : CTextButton(size, listener, tag, title) {} + + CColor getHighlightColor() const { return highlightColor_; } + void setHighlightColor(const CColor& color); + CColor getInactiveColor() const { return inactiveColor_; } + void setInactiveColor(const CColor& color); + bool isInactive() const { return inactive_; } + void setInactive(bool b); + bool isHighlighted() const { return highlighted_; } + void setHighlighted(bool b); + CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override; + void draw(CDrawContext* context) override; + + std::function OnHoverEnter; + std::function OnHoverLeave; + +private: + CColor highlightColor_; + bool hovered_ { false }; + bool highlighted_ { false }; + CColor inactiveColor_; + bool inactive_ { false }; +}; + +/// +class SStyledKnob : public CKnobBase { +public: + SStyledKnob(const CRect& size, IControlListener* listener, int32_t tag); + + const CColor& getActiveTrackColor() const { return activeTrackColor_; } + void setActiveTrackColor(const CColor& color); + + const CColor& getInactiveTrackColor() const { return inactiveTrackColor_; } + void setInactiveTrackColor(const CColor& color); + + const CColor& getLineIndicatorColor() const { return lineIndicatorColor_; } + void setLineIndicatorColor(const CColor& color); + + const CColor& getRotatorColor() const { return rotatorColor_; } + void setRotatorColor(const CColor& color); + + void setFont(CFontRef font); + CFontRef getFont() const { return font_; } + + void setFontColor(CColor fontColor); + CColor getFontColor() const { return fontColor_; } + + using ValueToStringFunction = std::function; + void setValueToStringFunction(ValueToStringFunction func); + + void setHideValue(bool hide) { hideValue_ = hide; invalid(); } + bool getHideValue() const { return hideValue_; } + + CLASS_METHODS(SStyledKnob, CKnobBase) +protected: + void draw(CDrawContext* dc) override; + +private: + CColor activeTrackColor_; + CColor inactiveTrackColor_; + CColor lineIndicatorColor_; + CColor rotatorColor_ { gui::kColorTransparent }; + bool hideValue_ { false }; + + SharedPointer font_ = kNormalFont; + CColor fontColor_ { 0x00, 0x00, 0x00 }; + + ValueToStringFunction valueToStringFunction_; +}; + +class CFilledRect : public CView +{ +public: + explicit CFilledRect(const CRect& size) + : CView(size) {} + + void setRadius(CCoord radius) { radius_ = radius; invalid(); } + CCoord getRadius() const { return radius_; } + + void setColor(CColor color){ color_ = color; invalid(); } + CColor getColor() { return color_; } +protected: + void draw(CDrawContext* dc) override; +private: + CCoord radius_ { 5.0 }; + CColor color_ { 0, 0, 0, 70 }; +}; + +/// +class SKnobCCBox : public CViewContainer, ViewListenerAdapter { +public: + SKnobCCBox(const CRect& size, IControlListener* listener, int32_t tag); + ~SKnobCCBox(); + void setHue(float hue); + + float getValue() const { return knob_->getValue(); } + float getDefaultValue() const { return knob_->getDefaultValue(); } + void setValue(float value); + void setDefaultValue(float value); + + void setNameLabelText(const UTF8String& name) { label_->setText(name); label_->invalid(); } + void setCCLabelText(const UTF8String& name) { ccLabel_->setText(name); ccLabel_->invalid(); } + + void setNameLabelFont(CFontRef font); + CFontRef getNameLabelFont() const { return label_->getFont(); } + + void setNameLabelFontColor(CColor color) { label_->setFontColor(color); label_->invalid(); } + CColor getNameLabelFontColor() const { return label_->getFontColor(); } + + void setNameLabelBackColor(CColor color) { label_->setBackColor(color); label_->invalid(); } + CColor getNameLabelBackColor() const { return label_->getBackColor(); } + + void setValueEditFont(CFontRef font); + CFontRef getValueEditFont() const { return label_->getFont(); } + + void setValueEditFontColor(CColor color) { valueEdit_->setFontColor(color); valueEdit_->invalid(); } + CColor getValueEditFontColor() const { return valueEdit_->getFontColor(); } + + void setValueEditBackColor(CColor color) { valueEdit_->setBackColor(color); valueEdit_->invalid(); } + CColor getValueEditBackColor() const { return valueEdit_->getBackColor(); } + + void setShadingRectangleColor(CColor color) { shadingRectangle_->setColor(color); shadingRectangle_->invalid(); } + CColor getShadingRectangleColor() const { return shadingRectangle_->getColor(); } + + void setCCLabelFont(CFontRef font); + CFontRef getCCLabelFont() const { return ccLabel_->getFont(); } + + void setCCLabelFontColor(CColor color) { ccLabel_->setFontColor(color); ccLabel_->invalid(); } + CColor getCCLabelFontColor() const { return ccLabel_->getFontColor(); } + + void setCCLabelBackColor(CColor color) { ccLabel_->setBackColor(color); ccLabel_->invalid(); } + CColor getCCLabelBackColor() const { return ccLabel_->getBackColor(); } + + void setKnobActiveTrackColor(CColor color) { knob_->setActiveTrackColor(color); knob_->invalid(); } + CColor getKnobActiveTrackColor() const { return knob_->getActiveTrackColor(); } + + void setKnobInactiveTrackColor(CColor color) { knob_->setInactiveTrackColor(color); knob_->invalid(); } + CColor getKnobInactiveTrackColor() const { return knob_->getInactiveTrackColor(); } + + void setKnobLineIndicatorColor(CColor color) { knob_->setLineIndicatorColor(color); knob_->invalid(); } + CColor getKnobLineIndicatorColor() const { return knob_->getLineIndicatorColor(); } + + void setKnobRotatorColor(CColor color) { knob_->setRotatorColor(color); knob_->invalid(); } + CColor getKnobRotatorColor() const { return knob_->getRotatorColor(); } + + void setKnobFont(CFontRef font) { knob_->setFont(font); knob_->invalid(); } + CFontRef getKnobFont() const { return knob_->getFont(); } + + void setKnobFontColor(CColor color) { knob_->setFontColor(color); knob_->invalid(); } + CColor getKnobFontColor() const { return knob_->getFontColor(); } + + // Edit box listener + void viewLostFocus (CView* view) override; + + bool isHD() const noexcept { return hdMode_; } + void setHDMode(bool mode); + +protected: + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; + +private: + void updateViewSizes(); + void updateViewColors(); + SharedPointer label_; + SharedPointer valueEdit_; + SharedPointer knob_; + SharedPointer ccLabel_; + SharedPointer shadingRectangle_; + SharedPointer menuEntry_; + CRect nameLabelSize_; + CRect knobSize_; + CRect shadingRectangleSize_; + CRect ccLabelSize_; + CRect valueEditSize_; + CRect rectangleSize_; + float hue_ = 0.35; + + class MenuListener : public IControlListener, public NonAtomicReferenceCounted { + public: + explicit MenuListener(SKnobCCBox& box) : box_(box) {} + void valueChanged(CControl*) override + { + box_.setHDMode(!box_.isHD()); + } + private: + SKnobCCBox& box_; + }; + SharedPointer menuListener_; + bool hdMode_ { false }; +}; + +/// +class SControlsPanel : public CScrollView { +public: + explicit SControlsPanel(const CRect& size); + + void setControlUsed(uint32_t index, bool used); + void setControlValue(uint32_t index, float value); + void setControlDefaultValue(uint32_t index, float value); + void setControlLabelText(uint32_t index, UTF8StringPtr text); + + void setNameLabelFont(CFontRef font); + void setNameLabelFontColor(CColor color); + void setNameLabelBackColor(CColor color); + void setCCLabelFont(CFontRef font); + void setCCLabelBackColor(CColor color); + void setCCLabelFontColor(CColor color); + void setValueEditBackColor(CColor color); + void setValueEditFontColor(CColor color); + void setShadingRectangleColor(CColor color); + void setKnobActiveTrackColor(CColor color); + void setKnobInactiveTrackColor(CColor color); + void setKnobLineIndicatorColor(CColor color); + void setKnobRotatorColor(CColor color); + void setKnobFont(CFontRef font); + void setKnobFontColor(CColor color); + + std::function ValueChangeFunction; + std::function BeginEditFunction; + std::function EndEditFunction; + +protected: + void recalculateSubViews() override; + +private: + void updateLayout(); + void syncAllSlotStyles(); + void syncSlotStyle(uint32_t index); + static std::string getDefaultLabelText(uint32_t index); + + struct ControlSlot; + ControlSlot* getSlot(uint32_t index); + ControlSlot* getOrCreateSlot(uint32_t index); + +private: + struct ControlSlot { + bool used = false; + SharedPointer box; + }; + + class ControlSlotListener : public IControlListener { + public: + explicit ControlSlotListener(SControlsPanel* panel) : panel_(panel) {} + void valueChanged(CControl* pControl) override; + void controlBeginEdit(CControl* pControl) override; + void controlEndEdit(CControl* pControl) override; + + private: + SControlsPanel* panel_ = nullptr; + }; + + std::vector> slots_; + std::unique_ptr listener_; + SharedPointer relayoutTrigger_; +}; + +/// +class SLevelMeter : public CView { +public: + explicit SLevelMeter(const CRect& size); + + float getValue() const { return value_; } + void setValue(float value); + + float getDangerThreshold() const { return dangerThreshold_; } + void setDangerThreshold(float thres) { dangerThreshold_ = thres; invalid(); } + + CColor getFrameColor() const { return frameColor_; } + void setFrameColor(CColor color) { frameColor_ = color; invalid(); } + CColor getBackColor() const { return backColor_; } + void setBackColor(CColor color) { backColor_ = color; invalid(); } + + CColor getNormalFillColor() const { return safeFillColor_; } + void setNormalFillColor(CColor color) { safeFillColor_ = color; invalid(); } + CColor getDangerFillColor() const { return dangerFillColor_; } + void setDangerFillColor(CColor color) { dangerFillColor_ = color; invalid(); } + + CCoord getRoundRectRadius() const { return radius_; } + void setRoundRectRadius(CCoord radius) { radius_ = radius; invalid(); } + +protected: + void draw(CDrawContext* dc) override; + +private: + float value_ = 0; + float dangerThreshold_ = 0.5; + float dbMin_ = -40; + float dbMax_ = 0; + CColor frameColor_; + CColor safeFillColor_; + CColor dangerFillColor_; + CColor backColor_; + CCoord radius_ = 5.0; + SharedPointer timer_; + bool timerArmed_ = false; +}; + +/// +class SPlaceHolder : public CView { +public: + explicit SPlaceHolder(const CRect& size, const CColor& color = {0xff, 0x00, 0x00, 0xff}); + +protected: + void draw(CDrawContext* dc) override; + +private: + CColor color_; +}; diff --git a/plugins/editor/src/editor/GUIDefs.cpp b/plugins/editor/src/editor/GUIDefs.cpp new file mode 100644 index 0000000..176f322 --- /dev/null +++ b/plugins/editor/src/editor/GUIDefs.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "GUIDefs.h" + +#include "utility/vstgui_before.h" +#include "vstgui/lib/ccolor.h" +#include "utility/vstgui_after.h" + +namespace gui { + + const CColor kColorTransparent { CColor(0x00, 0x00, 0x00, 0x00) }; + const CColor kColorTransparentDark { CColor(0x00, 0x00, 0x00, 0xc0) }; + + const CColor kColorOrange { CColor(0xfd, 0x98, 0x00, 0xff) }; + + const CColor kColorControlsScrollerTransparency { CColor(0x00, 0x00, 0x00, 0x80) }; + const CColor kColorControlsTransparency { CColor(0x00, 0x00, 0x00, 0x80) }; + const CColor kColorInfoTransparency { CColor(0x00, 0x00, 0x00, 0x99) }; + const CColor kColorMeterDanger { CColor(0xaa, 0x00, 0x00) }; + const CColor kColorMeterNormal { CColor(0x00, 0xaa, 0x11) }; + const CColor kColorTooltipBackground { CColor(0xff, 0xff, 0xd2, 0xff) }; +} diff --git a/plugins/editor/src/editor/GUIDefs.h b/plugins/editor/src/editor/GUIDefs.h new file mode 100644 index 0000000..5238aba --- /dev/null +++ b/plugins/editor/src/editor/GUIDefs.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once + +#include "utility/vstgui_before.h" +#include "vstgui/lib/vstguifwd.h" +#include "utility/vstgui_after.h" + +using VSTGUI::CColor; + +namespace gui { + + extern const CColor kColorTransparent; + extern const CColor kColorTransparentDark; + + extern const CColor kColorOrange; + + extern const CColor kColorControlsScrollerTransparency; + extern const CColor kColorControlsTransparency; + extern const CColor kColorInfoTransparency; + extern const CColor kColorMeterDanger; + extern const CColor kColorMeterNormal; + extern const CColor kColorTooltipBackground; +} diff --git a/plugins/editor/src/editor/GUIHelpers.cpp b/plugins/editor/src/editor/GUIHelpers.cpp new file mode 100644 index 0000000..9faf7ef --- /dev/null +++ b/plugins/editor/src/editor/GUIHelpers.cpp @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "GUIHelpers.h" +#define VSTGUI_MORE_THAN_4_10 ((VSTGUI_VERSION_MAJOR > 4) || (VSTGUI_VERSION_MAJOR == 4 && VSTGUI_VERSION_MINOR > 10)) +#if VSTGUI_MORE_THAN_4_10 +#include "utility/vstgui_before.h" +#include +#include "utility/vstgui_after.h" +#endif + +class SFrameDisabler::KeyAndMouseHook : public CBaseObject, + public IKeyboardHook, + public IMouseObserver { +public: + void setEnabled(bool value) { enabled_ = value; } + +protected: + void onMouseEntered(CView*, CFrame*) override {} + void onMouseExited(CView*, CFrame*) override {} +#if VSTGUI_MORE_THAN_4_10 + void onKeyboardEvent(KeyboardEvent& event, CFrame* frame) override; + void onMouseEvent(MouseEvent& event, CFrame* frame) override; +#else + int32_t onKeyDown(const VstKeyCode&, CFrame*) override { return enabled_ ? -1 : 1; } + int32_t onKeyUp(const VstKeyCode&, CFrame*) override { return enabled_ ? -1 : 1; } + CMouseEventResult onMouseMoved(CFrame*, const CPoint&, const CButtonState&) override { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; } + CMouseEventResult onMouseDown(CFrame*, const CPoint&, const CButtonState&) override { return enabled_ ? kMouseEventNotHandled : kMouseEventHandled; } +#endif + +private: + bool enabled_ = true; +}; + +SFrameDisabler::SFrameDisabler(CFrame* frame) + : frame_(frame), hook_(makeOwned()) +{ + frame->registerKeyboardHook(hook_); + frame->registerMouseObserver(hook_); + + delayedEnabler_ = makeOwned( + [this](CVSTGUITimer* t) { hook_->setEnabled(true); t->stop(); }, + 1, false); +} + +SFrameDisabler::~SFrameDisabler() +{ + frame_->unregisterKeyboardHook(hook_); + frame_->unregisterMouseObserver(hook_); +} + +void SFrameDisabler::enable() +{ + delayedEnabler_->start(); +} + +void SFrameDisabler::disable() +{ + hook_->setEnabled(false); + delayedEnabler_->stop(); +} + +#if VSTGUI_MORE_THAN_4_10 +void SFrameDisabler::KeyAndMouseHook::onKeyboardEvent(KeyboardEvent& event, CFrame*) +{ + if (!enabled_) + event.consumed = true; +} + +void SFrameDisabler::KeyAndMouseHook::onMouseEvent(MouseEvent& event, CFrame*) +{ + if (!enabled_) + event.consumed = true; +} +#endif diff --git a/plugins/editor/src/editor/GUIHelpers.h b/plugins/editor/src/editor/GUIHelpers.h new file mode 100644 index 0000000..86f43df --- /dev/null +++ b/plugins/editor/src/editor/GUIHelpers.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "utility/vstgui_before.h" +#include +#include +#include "utility/vstgui_after.h" + +using namespace VSTGUI; + +class SFrameDisabler : public CBaseObject { +public: + explicit SFrameDisabler(CFrame* frame); + ~SFrameDisabler(); + + void enable(); + void disable(); + +private: + class KeyAndMouseHook; + +private: + CFrame* frame_ = nullptr; + SharedPointer hook_; + SharedPointer delayedEnabler_; +}; diff --git a/plugins/editor/src/editor/GUIPiano.cpp b/plugins/editor/src/editor/GUIPiano.cpp new file mode 100644 index 0000000..ce94dd3 --- /dev/null +++ b/plugins/editor/src/editor/GUIPiano.cpp @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "GUIPiano.h" +#include "ColorHelpers.h" +#include "utility/vstgui_before.h" +#include "vstgui/lib/cdrawcontext.h" +#include "vstgui/lib/cgraphicspath.h" +#include "utility/vstgui_after.h" +#include +#include + +static constexpr CCoord keyoffs[12] = {0, 0.6, 1, 1.8, 2, 3, + 3.55, 4, 4.7, 5, 5.85, 6}; +static constexpr bool black[12] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}; + +struct SPiano::Impl { + unsigned octs_ {}; + std::unique_ptr keyval_ { new float[128]{} }; + std::bitset<128> keyUsed_; + std::bitset<128> keyswitchUsed_; + unsigned mousePressedKey_ = ~0u; + + CCoord innerPaddingX_ = 4.0; + CCoord innerPaddingY_ = 4.0; + CCoord spacingY_ = 4.0; + + CColor backgroundFill_ { 0xca, 0xca, 0xca, 0xff }; +#if 0 + float backgroundRadius_ = 5.0; +#endif + float keyUsedHue_ = 0.55; + float keySwitchHue_ = 0.0; + float whiteKeyChroma_ = 0.9; + float blackKeyChroma_ = 0.75; + float whiteKeyLuma_ = 0.9; + float blackKeyLuma_ = 0.35; + float keyLumaPressDelta_ = 0.2; + + CColor outline_ { 0x00, 0x00, 0x00, 0xff }; + CColor shadeOutline_ { 0x80, 0x80, 0x80, 0xff }; + CColor labelStroke_ { 0x63, 0x63, 0x63, 0xff }; + + mutable Dimensions dim_; + SharedPointer font_; +}; + +SPiano::SPiano(CRect bounds) + : CView(bounds), impl_(new Impl) +{ + setNumOctaves(10); +} + +CFontRef SPiano::getFont() const +{ + const Impl& impl = *impl_; + return impl.font_; +} + +void SPiano::setFont(CFontRef font) +{ + Impl& impl = *impl_; + impl.font_ = font; + getDimensions(true); + invalid(); +} + +unsigned SPiano::getNumOctaves() const +{ + const Impl& impl = *impl_; + return impl.octs_; +} + +void SPiano::setNumOctaves(unsigned octs) +{ + Impl& impl = *impl_; + impl.octs_ = std::max(1u, octs); + getDimensions(true); + invalid(); +} + +void SPiano::setKeyUsed(unsigned key, bool used) +{ + Impl& impl = *impl_; + + if (key >= 128) + return; + + if (impl.keyUsed_.test(key) == used) + return; + + impl.keyUsed_.set(key, used); + invalid(); +} + +void SPiano::setKeyswitchUsed(unsigned key, bool used) +{ + Impl& impl = *impl_; + + if (key >= 128) + return; + + if (impl.keyswitchUsed_.test(key) == used) + return; + + impl.keyswitchUsed_.set(key, used); + invalid(); +} + +void SPiano::setKeyValue(unsigned key, float value) +{ + Impl& impl = *impl_; + + if (key >= 128) + return; + + value = std::max(0.0f, std::min(1.0f, value)); + + if (impl.keyval_[key] == value) + return; + + impl.keyval_[key] = value; + invalid(); +} + +SPiano::KeyRole SPiano::getKeyRole(unsigned key) +{ + Impl& impl = *impl_; + KeyRole role = KeyRole::Unused; + + if (key < 128) { + if (impl.keyswitchUsed_.test(key)) + role = KeyRole::Switch; + else if (impl.keyUsed_.test(key)) + role = KeyRole::Note; + } + + return role; +} + +void SPiano::setBackColor(const CColor &color) +{ + Impl& impl = *impl_; + if (impl.backgroundFill_ != color) { + impl.backgroundFill_ = color; + invalid(); + } +} + +void SPiano::setFontColor(const CColor &color) +{ + Impl& impl = *impl_; + if (impl.labelStroke_ != color) { + impl.labelStroke_ = color; + invalid(); + } +} + +void SPiano::draw(CDrawContext* dc) +{ + Impl& impl = *impl_; + const Dimensions dim = getDimensions(false); + const unsigned octs = impl.octs_; + const unsigned keyCount = octs * 12; + + dc->setDrawMode(kAntiAliasing); + + if (impl.backgroundFill_.alpha > 0) { + SharedPointer path; + path = owned(dc->createGraphicsPath()); +#if 0 + path->addRoundRect(dim.bounds, impl.backgroundRadius_); +#endif + dc->setFillColor(impl.backgroundFill_); + dc->drawGraphicsPath(path, CDrawContext::kPathFilled); + } + + for (unsigned key = 0; key < keyCount; ++key) { + if (!black[key % 12]) { + CRect rect = keyRect(key); + + SColorHCY hcy(0.0, impl.whiteKeyChroma_, impl.whiteKeyLuma_); + + switch (getKeyRole(key)) { + case KeyRole::Note: + hcy.h = impl.keyUsedHue_; + break; + case KeyRole::Switch: + hcy.h = impl.keySwitchHue_; + break; + default: + hcy.y = 1.0; + if (impl.keyval_[key]) + hcy.c = 0.0; + break; + } + + if (impl.keyval_[key]) + hcy.y = std::max(0.0f, hcy.y - impl.keyLumaPressDelta_); + + CColor keycolor = hcy.toColor(); + dc->setFillColor(keycolor); + dc->drawRect(rect, kDrawFilled); + } + } + + dc->setFrameColor(impl.outline_); + dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getBottomLeft()); + for (unsigned key = 0; key < keyCount; ++key) { + if (!black[key % 12]) { + CRect rect = keyRect(key); + dc->drawLine(rect.getTopRight(), rect.getBottomRight()); + } + } + + for (unsigned key = 0; key < keyCount; ++key) { + if (black[key % 12]) { + CRect rect = keyRect(key); + + SColorHCY hcy(0.0, impl.blackKeyChroma_, impl.blackKeyLuma_); + + switch (getKeyRole(key)) { + case KeyRole::Note: + hcy.h = impl.keyUsedHue_; + break; + case KeyRole::Switch: + hcy.h = impl.keySwitchHue_; + break; + default: + hcy.c = 0.0; + break; + } + + if (impl.keyval_[key]) + hcy.y = std::min(1.0f, hcy.y + impl.keyLumaPressDelta_); + + CColor keycolor = hcy.toColor(); + dc->setFillColor(keycolor); + dc->drawRect(rect, kDrawFilled); + dc->setFrameColor(impl.outline_); + dc->drawRect(rect); + } + } + + if (const CFontRef& font = impl.font_) { + for (unsigned o = 0; o < octs; ++o) { + CRect rect = keyRect(o * 12); + CRect textRect( + rect.left, dim.labelBounds.top, + rect.right, dim.labelBounds.bottom); + dc->setFont(font); + dc->setFontColor(impl.labelStroke_); + std::string text = std::to_string(static_cast(o) - 1); + dc->drawString(text.c_str(), textRect, kCenterText); + } + } + + { + dc->setFrameColor(impl.outline_); + dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getTopRight()); + dc->setFrameColor(impl.shadeOutline_); + dc->drawLine(dim.keyBounds.getBottomLeft(), dim.keyBounds.getBottomRight()); + } + + dc->setFrameColor(impl.outline_); +} + +CMouseEventResult SPiano::onMouseDown(CPoint& where, const CButtonState& buttons) +{ + Impl& impl = *impl_; + unsigned key = keyAtPos(where); + if (key != ~0u) { + impl.keyval_[key] = 1; + impl.mousePressedKey_ = key; + if (onKeyPressed) + onKeyPressed(key, mousePressVelocity(key, where.y)); + invalid(); + return kMouseEventHandled; + } + return CView::onMouseDown(where, buttons); +} + +CMouseEventResult SPiano::onMouseUp(CPoint& where, const CButtonState& buttons) +{ + Impl& impl = *impl_; + unsigned key = impl.mousePressedKey_; + if (key != ~0u) { + impl.keyval_[key] = 0; + if (onKeyReleased) + onKeyReleased(key, mousePressVelocity(key, where.y)); + impl.mousePressedKey_ = ~0u; + invalid(); + return kMouseEventHandled; + } + return CView::onMouseUp(where, buttons); +} + +CMouseEventResult SPiano::onMouseMoved(CPoint& where, const CButtonState& buttons) +{ + Impl& impl = *impl_; + if (impl.mousePressedKey_ != ~0u) { + unsigned key = keyAtPos(where); + if (impl.mousePressedKey_ != key) { + impl.keyval_[impl.mousePressedKey_] = 0; + if (onKeyReleased) + onKeyReleased(impl.mousePressedKey_, mousePressVelocity(key, where.y)); + // mousePressedKey_ = ~0u; + if (key != ~0u) { + impl.keyval_[key] = 1; + impl.mousePressedKey_ = key; + if (onKeyPressed) + onKeyPressed(key, mousePressVelocity(key, where.y)); + } + invalid(); + } + return kMouseEventHandled; + } + return CView::onMouseMoved(where, buttons); +} + +const SPiano::Dimensions& SPiano::getDimensions(bool forceUpdate) const +{ + const Impl& impl = *impl_; + + if (!forceUpdate && impl.dim_.bounds == getViewSize()) + return impl.dim_; + + Dimensions dim; + dim.bounds = getViewSize(); + dim.paddedBounds = CRect(dim.bounds) + .extend(-2 * impl.innerPaddingX_, -2 * impl.innerPaddingY_); + CCoord keyHeight = std::floor(dim.paddedBounds.getHeight()); + CCoord fontHeight = impl.font_ ? impl.font_->getSize() : 0.0; + keyHeight -= impl.spacingY_ + fontHeight; + dim.keyBounds = CRect(dim.paddedBounds) + .setHeight(keyHeight); + dim.keyWidth = static_cast( + dim.paddedBounds.getWidth() / impl.octs_ / 7.0); + dim.keyBounds.setWidth(dim.keyWidth * impl.octs_ * 7.0); + dim.keyBounds.offset( + std::floor(0.5 * (dim.paddedBounds.getWidth() - dim.keyBounds.getWidth())), 0.0); + + if (!impl.font_) + dim.labelBounds = CRect(); + else + dim.labelBounds = CRect( + dim.keyBounds.left, dim.keyBounds.bottom + impl.spacingY_, + dim.keyBounds.right, dim.keyBounds.bottom + impl.spacingY_ + fontHeight); + + impl.dim_ = dim; + return impl.dim_; +} + +CRect SPiano::keyRect(const Dimensions& dim, unsigned key) +{ + unsigned oct = key / 12; + unsigned note = key % 12; + unsigned keyw = dim.keyWidth; + unsigned keyh = static_cast(dim.keyBounds.getHeight()); + CCoord octwidth = (keyoffs[11] + 1.0) * keyw; + CCoord octx = octwidth * oct; + CCoord notex = octx + keyoffs[note] * keyw; + CCoord notew = black[note] ? (0.6 * keyw) : keyw; + CCoord noteh = black[note] ? (0.6 * keyh) : keyh; + return CRect(notex, 0.0, notex + notew, noteh).offset(dim.keyBounds.getTopLeft()); +} + +CRect SPiano::keyRect(unsigned key) const +{ + return keyRect(getDimensions(false), key); +} + +unsigned SPiano::keyAtPos(CPoint pos) const +{ + const Impl& impl = *impl_; + const unsigned octs = impl.octs_; + + for (unsigned key = 0; key < octs * 12; ++key) { + if (black[key % 12]) { + if (keyRect(key).pointInside(pos)) + return key; + } + } + + for (unsigned key = 0; key < octs * 12; ++key) { + if (!black[key % 12]) { + if (keyRect(key).pointInside(pos)) + return key; + } + } + + return ~0u; +} + +float SPiano::mousePressVelocity(unsigned key, CCoord posY) +{ + const CRect rect = keyRect(key); + CCoord value = (posY - rect.top) / rect.getHeight(); + return std::max(0.0f, std::min(1.0f, static_cast(value))); +} diff --git a/plugins/editor/src/editor/GUIPiano.h b/plugins/editor/src/editor/GUIPiano.h new file mode 100644 index 0000000..b20a6f8 --- /dev/null +++ b/plugins/editor/src/editor/GUIPiano.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "utility/vstgui_before.h" +#include "vstgui/lib/cview.h" +#include "vstgui/lib/ccolor.h" +#include "utility/vstgui_after.h" +#include +#include +#include + +using namespace VSTGUI; + +class SPiano : public CView { +public: + explicit SPiano(CRect bounds); + + CFontRef getFont() const; + void setFont(CFontRef font); + + unsigned getNumOctaves() const; + void setNumOctaves(unsigned octs); + + void setKeyUsed(unsigned key, bool used); + void setKeyswitchUsed(unsigned key, bool used); + void setKeyValue(unsigned key, float value); + + enum class KeyRole : int { + Unused = 0, + Note = 1 << 0, + Switch = 1 << 1, + }; + + KeyRole getKeyRole(unsigned key); + + std::function onKeyPressed; + std::function onKeyReleased; + + void setBackColor(const CColor&); + void setFontColor(const CColor&); + +protected: + void draw(CDrawContext* dc) override; + CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseUp(CPoint& where, const CButtonState& buttons) override; + CMouseEventResult onMouseMoved(CPoint& where, const CButtonState& buttons) override; + +private: + struct Dimensions { + CRect bounds {}; + CRect paddedBounds {}; + CRect keyBounds {}; + unsigned keyWidth {}; + CRect labelBounds {}; + }; + const Dimensions& getDimensions(bool forceUpdate) const; + + static CRect keyRect(const Dimensions& dim, unsigned key); + CRect keyRect(unsigned key) const; + unsigned keyAtPos(CPoint pos) const; + + float mousePressVelocity(unsigned key, CCoord posY); + +private: + struct Impl; + std::unique_ptr impl_; +}; diff --git a/plugins/editor/src/editor/ImageHelpers.cpp b/plugins/editor/src/editor/ImageHelpers.cpp new file mode 100644 index 0000000..c946088 --- /dev/null +++ b/plugins/editor/src/editor/ImageHelpers.cpp @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "ImageHelpers.h" +#include "EditorLibs.h" +#include + +using namespace VSTGUI; + +struct stbi_image_delete { + void operator()(unsigned char* x) const noexcept { stbi_image_free(x); } +}; +using stbi_image_u = std::unique_ptr; + +SharedPointer loadAnyFormatImage(const fs::path& filePath) +{ + stbi_image_u image; + int width, height, channels; + +#if defined(_WIN32) + FILE* file { _wfopen(filePath.wstring().c_str(), L"rb") }; +#else + FILE* file { fopen(filePath.c_str(), "rb") }; +#endif + if (file) { + image.reset(stbi_load_from_file(file, &width, &height, &channels, STBI_rgb_alpha)); + fclose(file); + } + + if (!image) + return nullptr; + + SharedPointer bitmap = makeOwned(width, height); + SharedPointer accessor = + owned(CBitmapPixelAccess::create(bitmap.get())); + + if (!accessor) + return nullptr; + + const unsigned char* pixel = image.get(); + do { + uint8_t r = pixel[0]; + uint8_t g = pixel[1]; + uint8_t b = pixel[2]; + uint8_t a = pixel[3]; + + // premultiply alpha + r = uint8_t((r * a) / 255); + g = uint8_t((g * a) / 255); + b = uint8_t((b * a) / 255); + + accessor->setColor(CColor(r, g, b, a)); + pixel += 4; + } while (++*accessor); + accessor = nullptr; + + return bitmap; +} + +void downscaleToWidthAndHeight(CBitmap* bitmap, CPoint frameSize) +{ + if (!bitmap) + return; + + PlatformBitmapPtr platformBitmap = bitmap->getPlatformBitmap(); + if (!platformBitmap) + return; + + CPoint bitmapSize = platformBitmap->getSize(); + + CCoord frameW = frameSize.x; + CCoord frameH = frameSize.y; + CCoord bitmapW = bitmapSize.x; + CCoord bitmapH = bitmapSize.y; + + CCoord scale = 1.0; + if (bitmapW > frameW || bitmapH > frameH) { + CCoord xScale = bitmapW / frameW; + CCoord yScale = bitmapH / frameH; + scale = (xScale > yScale) ? xScale : yScale; + } + + platformBitmap->setScaleFactor(scale); +} diff --git a/plugins/editor/src/editor/ImageHelpers.h b/plugins/editor/src/editor/ImageHelpers.h new file mode 100644 index 0000000..8c51612 --- /dev/null +++ b/plugins/editor/src/editor/ImageHelpers.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +#include "utility/vstgui_before.h" +#include "vstgui/vstgui.h" +#include "utility/vstgui_after.h" + +/** + * @brief Loads a bitmap from an image file, with a large support of formats + * through the stb_image library. + */ +VSTGUI::SharedPointer loadAnyFormatImage(const fs::path& filePath); + +/** + * @brief Adjust the scale factor of this bitmap, such that both its dimensions + * fit into a frame of the given size. + */ +void downscaleToWidthAndHeight(VSTGUI::CBitmap* bitmap, VSTGUI::CPoint frameSize); diff --git a/plugins/editor/src/editor/NativeHelpers.cpp b/plugins/editor/src/editor/NativeHelpers.cpp new file mode 100644 index 0000000..f089b25 --- /dev/null +++ b/plugins/editor/src/editor/NativeHelpers.cpp @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "NativeHelpers.h" +#include "plugin/NativeHelpers.h" + +#if defined(_WIN32) +#include "ghc/fs_std.hpp" +#include +#include + +bool openFileInExternalEditor(const char *filename) +{ + std::wstring path = stringToWideChar(filename); + + SHELLEXECUTEINFOW info; + memset(&info, 0, sizeof(info)); + + info.cbSize = sizeof(info); + info.fMask = SEE_MASK_CLASSNAME; + info.lpVerb = L"open"; + info.lpFile = path.c_str(); + info.lpClass = L"txtfile"; + info.nShow = SW_SHOW; + + return ShellExecuteExW(&info); +} + +bool openDirectoryInExplorer(const char *filename) +{ + std::wstring path = stringToWideChar(filename); + + SHELLEXECUTEINFOW info; + memset(&info, 0, sizeof(info)); + + info.cbSize = sizeof(info); + info.lpVerb = L"explore"; + info.lpFile = path.c_str(); + info.nShow = SW_SHOW; + + return ShellExecuteExW(&info); +} + +bool openURLWithExternalProgram(const char *url) +{ + std::wstring path = stringToWideChar(url); + + SHELLEXECUTEINFOW info; + memset(&info, 0, sizeof(info)); + + info.cbSize = sizeof(info); + info.lpVerb = L"open"; + info.lpFile = path.c_str(); + info.nShow = SW_SHOW; + + return ShellExecuteExW(&info); +} + +bool askQuestion(const char *text) +{ + int ret = MessageBoxW(nullptr, stringToWideChar(text), L"Question", MB_YESNO); + return ret == IDYES; +} + +std::string getOperatingSystemName() +{ + LSTATUS status; + HKEY key = nullptr; + const WCHAR keyPath[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion"; + const WCHAR valueName[] = L"ProductName"; + const char fallbackName[] = "Windows (unknown)"; + + status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, KEY_QUERY_VALUE, &key); + if (status != ERROR_SUCCESS) + return fallbackName; + + DWORD valueSize = 32768 * sizeof(WCHAR); + std::unique_ptr valueW(new WCHAR[(valueSize / sizeof(WCHAR)) + 1]()); + DWORD valueType; + status = RegQueryValueExW( + key, valueName, nullptr, + &valueType, reinterpret_cast(valueW.get()), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return fallbackName; + + std::unique_ptr valueUTF8(stringToUTF8(valueW.get())); + return valueUTF8.get(); +} + +std::string getProcessorName() +{ + LSTATUS status; + HKEY key = nullptr; + const WCHAR keyPath[] = L"Hardware\\Description\\System\\CentralProcessor\\0"; + const WCHAR valueName[] = L"ProcessorNameString"; + const char fallbackName[] = "Unknown"; + + status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyPath, 0, KEY_QUERY_VALUE, &key); + if (status != ERROR_SUCCESS) + return fallbackName; + + DWORD valueSize = 32768 * sizeof(WCHAR); + std::unique_ptr valueW(new WCHAR[(valueSize / sizeof(WCHAR)) + 1]()); + DWORD valueType; + status = RegQueryValueExW( + key, valueName, nullptr, + &valueType, reinterpret_cast(valueW.get()), &valueSize); + RegCloseKey(key); + if (status != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ)) + return fallbackName; + + std::unique_ptr valueUTF8(stringToUTF8(valueW.get())); + return valueUTF8.get(); +} + +std::string getCurrentProcessName() +{ + DWORD size = 32768; + std::unique_ptr buffer(new WCHAR[size]()); + + if (!GetModuleFileNameW(nullptr, buffer.get(), size)) + return {}; + + buffer[size - 1] = L'\0'; + const WCHAR* name = buffer.get(); + + if (const WCHAR* pos = wcsrchr(name, L'\\')) + name = pos + 1; + + std::unique_ptr nameUTF8(stringToUTF8(name)); + return nameUTF8.get(); +} +#elif defined(__APPLE__) + // implemented in NativeHelpers.mm +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { extern char **environ; } +extern "C" { extern char *__progname; } + +static bool openFileByMimeType(const char *filename, const char *mimetype) +{ + GAppInfo* appinfo = g_app_info_get_default_for_type(mimetype, FALSE); + if (!appinfo) + return 1; + + GList* files = nullptr; + GFile* file = g_file_new_for_path(filename); + files = g_list_append(files, file); + gboolean success = g_app_info_launch(appinfo, files, nullptr, nullptr); + g_object_unref(file); + g_list_free(files); + g_object_unref(appinfo); + return success == TRUE; +} + +bool openFileInExternalEditor(const char *filename) +{ + return openFileByMimeType(filename, "text/plain"); +} + +bool openDirectoryInExplorer(const char *filename) +{ + return openFileByMimeType(filename, "inode/directory"); +} + +bool openURLWithExternalProgram(const char *url) +{ + gboolean success = g_app_info_launch_default_for_uri(url, nullptr, nullptr); + return success == TRUE; +} + +static std::vector createForkEnviron() +{ + std::vector newEnv; + newEnv.reserve(256); + for (char **envp = environ; *envp; ++envp) { + // ensure the process will link with system libraries, + // and not these from the Ardour bundle. + if (strncmp(*envp, "LD_LIBRARY_PATH=", 16) == 0) + continue; + newEnv.push_back(*envp); + } + newEnv.push_back(nullptr); + return newEnv; +} + +static const std::string zenityPath = [] { + auto glibPath = g_find_program_in_path("zenity"); + if (glibPath) { + std::string s { glibPath }; + g_free(glibPath); + return s; + } else { + return std::string("/usr/bin/zenity"); + } +}(); + +bool askQuestion(const char *text) +{ + char *argv[] = { + const_cast(zenityPath.c_str()), + const_cast("--question"), + const_cast("--text"), + const_cast(text), + nullptr, + }; + + std::vector newEnv = createForkEnviron(); + char **envp = newEnv.data(); + + pid_t forkPid = vfork(); + if (forkPid == -1) + return false; + + if (forkPid == 0) { + execve(argv[0], argv, envp); + _exit(1); + } + + int wret; + int wstatus; + do { + wret = waitpid(forkPid, &wstatus, 0); + } while (wret == -1 && errno == EINTR); + + if (wret == -1 || !WIFEXITED(wstatus)) + return false; + + return WEXITSTATUS(wstatus) == 0; +} + +bool isZenityAvailable() +{ + return access(zenityPath.c_str(), X_OK) == 0; +} + +std::string getOperatingSystemName() +{ + std::string name; + + std::ifstream in("/etc/os-release", std::ios::binary); + if (!in) + in = std::ifstream("/usr/lib/os-release", std::ios::binary); + if (in) { + std::string line; + line.reserve(256); + for (bool found = false; !found && std::getline(in, line); ) { + const char prefix[] = "PRETTY_NAME="; + size_t length = sizeof(prefix) - 1; + found = line.size() >= length && !memcmp(line.data(), prefix, length); + if (found) { + if (char* value = g_shell_unquote(line.c_str() + length, nullptr)) { + name.assign(value); + g_free(value); + } + } + } + in.close(); + } + + if (name.empty()) { + utsname un {}; + int ret = uname(&un); + if (ret != -1 && un.sysname[0] != '\0') + name.append(un.sysname); + else { + name.append("Unknown"); + } + if (ret != -1 && un.release[0] != '\0') { + name.push_back(' '); + name.append(un.release); + } + } + + return name; +} + +std::string getProcessorName() +{ + std::string name; + std::string line; + std::ifstream in("/proc/cpuinfo", std::ios::binary); + + line.reserve(256); + + while (name.empty() && std::getline(in, line) && !line.empty()) { + size_t pos = line.find(':'); + if (pos == line.npos) + continue; + + absl::string_view left = absl::string_view(line).substr(0, pos); + absl::string_view right = absl::string_view(line).substr(pos + 1); + + left = absl::StripAsciiWhitespace(left); + right = absl::StripAsciiWhitespace(right); + + if (left == "model name") + name = std::string(right); + } + + if (name.empty()) + name = "Unknown"; + + return name; +} + +std::string getCurrentProcessName() +{ + std::string name; + + const std::string commPath = "/proc/" + std::to_string(getpid()) + "/comm"; + + if (std::ifstream in { commPath, std::ios::binary }) { + name.reserve(256); + for (int c; (c = in.get()) != std::char_traits::eof() && c != '\n'; ) + name.push_back(static_cast(c)); + } + + if (name.empty()) { + if (const char* progname = __progname) + name.assign(progname); + } + + return name; +} +#endif diff --git a/plugins/editor/src/editor/NativeHelpers.h b/plugins/editor/src/editor/NativeHelpers.h new file mode 100644 index 0000000..2741981 --- /dev/null +++ b/plugins/editor/src/editor/NativeHelpers.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +bool openFileInExternalEditor(const char *filename); +bool openDirectoryInExplorer(const char *filename); +bool openURLWithExternalProgram(const char *url); +bool askQuestion(const char *text); +std::string getOperatingSystemName(); +std::string getProcessorName(); +std::string getCurrentProcessName(); + +#if !defined(_WIN32) && !defined(__APPLE__) +bool isZenityAvailable(); +#endif diff --git a/plugins/editor/src/editor/NativeHelpers.mm b/plugins/editor/src/editor/NativeHelpers.mm new file mode 100644 index 0000000..e2d5399 --- /dev/null +++ b/plugins/editor/src/editor/NativeHelpers.mm @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "NativeHelpers.h" + +#if defined(__APPLE__) +#import +#import +#import +#include +#include +#include +#include + +static bool openFileWithApplication(const char *fileName, NSString *application) +{ + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; + NSString* fileNameNs = [NSString stringWithUTF8String:fileName]; + return [workspace openFile:fileNameNs withApplication:application] == YES; +} + +bool openFileInExternalEditor(const char *fileName) +{ + NSURL* appURL = nil; + NSURL* fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:fileName]]; + const LSRolesMask roles = kLSRolesEditor; + + NSArray* editorApps = (__bridge_transfer NSArray*)LSCopyApplicationURLsForURL( + (__bridge CFURLRef)fileURL, roles); + + for (NSUInteger i = 0, n = [editorApps count]; i < n && !appURL; ++i) { + NSURL* url = [editorApps objectAtIndex:i]; + if (url && [url isFileURL]) + appURL = url; + } + + if (!appURL) { + NSURL* url = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType( + kUTTypePlainText, roles, nil); + if (url && [url isFileURL]) + appURL = url; + } + + if (!appURL) + return false; + + return openFileWithApplication(fileName, [appURL path]); +} + +bool openDirectoryInExplorer(const char *fileName) +{ + return openFileWithApplication(fileName, @"Finder"); +} + +bool openURLWithExternalProgram(const char *url) +{ + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; + NSURL* urlNs = [NSURL URLWithString:[NSString stringWithUTF8String:url]]; + return [workspace openURL:urlNs] == YES; +} + +bool askQuestion(const char *text) +{ + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:[NSString stringWithUTF8String:text]]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + NSInteger button = [alert runModal]; + return button == NSAlertFirstButtonReturn; +} + +std::string getOperatingSystemName() +{ +#if TARGET_OS_IOS + NSString *osName = @"iOS"; +#elif TARGET_OS_MAC + NSString *osName = @"macOS"; +#endif + NSString *osVersion = [[NSProcessInfo processInfo] operatingSystemVersionString]; + return [[NSString stringWithFormat:@"%@ %@", osName, osVersion] UTF8String]; +} + +std::string getProcessorName() +{ + char nameBuf[256]; + size_t size = sizeof(nameBuf); + const char* fallbackName = "Unknown"; + + if (sysctlbyname("machdep.cpu.brand_string", nameBuf, &size, nullptr, 0) == -1) + return fallbackName; + + size = strnlen(nameBuf, sizeof(nameBuf)); + if (size == 0) + return fallbackName; + + return std::string(nameBuf, size); +} + +std::string getCurrentProcessName() +{ + kinfo_proc proc {}; + size_t size = sizeof(kinfo_proc); + int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, int(getpid()) }; + if (sysctl(name, 4, &proc, &size, nullptr, 0) == -1) + return {}; + return proc.kp_proc.p_comm; +} +#endif diff --git a/plugins/editor/src/editor/Theme.cpp b/plugins/editor/src/editor/Theme.cpp new file mode 100644 index 0000000..351e314 --- /dev/null +++ b/plugins/editor/src/editor/Theme.cpp @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "Theme.h" +#include "ColorHelpers.h" +#include "VSTGUIHelpers.h" +#include "plugin/SfizzSettings.h" +#include "sfizz/utility/StringViewHelpers.h" +#include +#include +#include +#include + +void Theme::clear() { + frameBackground = {}; + normalPalette = {}; + invertedPalette = {}; +} + +void Theme::load(const std::string& name) +{ + fs::path resPath = getResourceBasePath(); + fs::path themePath = resPath / "Themes" / fs::u8path(name) / "theme.xml"; + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(themePath.c_str()); + if (!result) { + std::cerr << "[sfizz] cannot load theme from " << resPath << '\n'; + return; + } + + loadDocument(doc); +} + +void Theme::loadDocument(const pugi::xml_document& doc) +{ + pugi::xml_node rootNode(doc.child("sfizz-theme")); + if (!rootNode) { + std::cerr << "[sfizz] trying to load an invalid theme\n"; + return; + } + + /// + clear(); + + /// + auto loadChildColorNodes = [this](pugi::xml_node topNode, bool inverted) { + for (pugi::xml_node colorNode : topNode.children("color")) { + absl::string_view name = colorNode.attribute("name").as_string(); + CColor* slot = getColorFromName(name, inverted); + if (!slot) { + std::cerr << "[sfizz] color not recognized: " << name << "\n"; + continue; + } + + *slot = {}; + + absl::string_view colorText = colorNode.text().as_string(); + if (!colorFromHex(colorText, *slot)) + std::cerr << "[sfizz] invalid color value: " << colorText << "\n"; + } + }; + + /// + loadChildColorNodes(rootNode, false); + + for (pugi::xml_node paletteNode : rootNode.children("palette")) { + absl::string_view paletteName = paletteNode.attribute("name").as_string(); + + bool inverted; + if (paletteName == "normal") + inverted = false; + else if (paletteName == "inverted") + inverted = true; + else { + std::cerr << "[sfizz] palette not recognized: " << paletteName << "\n"; + continue; + } + + loadChildColorNodes(paletteNode, inverted); + } + + /// + invokeChangeListener(); +} + +void Theme::storeCurrentName(absl::string_view name) +{ + SfizzSettings settings; + settings.store("current_theme", name); +} + +std::string Theme::loadCurrentName() +{ + SfizzSettings settings; + return settings.load_or("current_theme", "Default"); +} + +const std::vector& Theme::getAvailableNames() +{ + static const std::vector names = extractAvailableNames(); + return names; +} + +std::vector Theme::extractAvailableNames() +{ + fs::path themesPath = getResourceBasePath() / "Themes"; + + std::error_code ec; + fs::directory_iterator it(themesPath, ec); + if (ec) { + std::cerr << "[sfizz] error reading the theme directory: " << ec.message() << '\n'; + return {}; + } + + std::vector names; + for (; !ec && it != fs::directory_iterator(); it.increment(ec)) { + const fs::directory_entry& entry = *it; + if (entry.is_directory()) + names.emplace_back(entry.path().filename().u8string()); + } + + std::sort( + names.begin(), names.end(), + [](const std::string& a, const std::string& b) -> bool { + return (a == "Default") ? true : (b == "Default") ? false : a < b; + }); + + return names; +} + +CColor* Theme::getColorFromName(absl::string_view name, bool fromInvertedPalette) +{ + CColor* c = nullptr; + switch (hash(name)) { + #define COLOR_CASE(X) \ + case hash(#X): \ + c = &X; \ + break + #define PALETTE_COLOR_CASE(X) \ + case hash(#X): \ + c = fromInvertedPalette ? \ + &invertedPalette.X : &normalPalette.X; \ + break + + COLOR_CASE(frameBackground); + PALETTE_COLOR_CASE(boxBackground); + PALETTE_COLOR_CASE(highlightedText); + PALETTE_COLOR_CASE(icon); + PALETTE_COLOR_CASE(iconHighlight); + PALETTE_COLOR_CASE(inactiveText); + PALETTE_COLOR_CASE(knobActiveTrack); + PALETTE_COLOR_CASE(knobInactiveTrack); + PALETTE_COLOR_CASE(knobLabelText); + PALETTE_COLOR_CASE(knobLineIndicator); + PALETTE_COLOR_CASE(knobText); + PALETTE_COLOR_CASE(knobLabelBackground); + PALETTE_COLOR_CASE(text); + PALETTE_COLOR_CASE(titleBoxBackground); + PALETTE_COLOR_CASE(titleBoxText); + PALETTE_COLOR_CASE(valueBackground); + PALETTE_COLOR_CASE(valueText); + + #undef COLOR_CASE + #undef PALETTE_COLOR_CASE + + default: break; + } + return c; +} diff --git a/plugins/editor/src/editor/Theme.h b/plugins/editor/src/editor/Theme.h new file mode 100644 index 0000000..9e5d87a --- /dev/null +++ b/plugins/editor/src/editor/Theme.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "utility/vstgui_before.h" +#include "vstgui/vstgui.h" +#include "utility/vstgui_after.h" +#include +#include +#include +namespace pugi { class xml_document; } + +using namespace VSTGUI; + +struct Palette { + CColor boxBackground; + CColor text; + CColor inactiveText; + CColor highlightedText; + CColor titleBoxText; + CColor titleBoxBackground; + CColor icon; + CColor iconHighlight; + CColor valueText; + CColor valueBackground; + CColor knobActiveTrack; + CColor knobInactiveTrack; + CColor knobLineIndicator; + CColor knobText; + CColor knobLabelText; + CColor knobLabelBackground; +}; + +struct Theme { + Theme() = default; + + Theme(const Theme&) = delete; + Theme& operator=(const Theme&) = delete; + + CColor frameBackground; + Palette normalPalette {}; + Palette invertedPalette {}; + + struct ChangeListener { + virtual ~ChangeListener() {} + virtual void onThemeChanged() = 0; + }; + + ChangeListener* listener = nullptr; + + void clear(); + void load(const std::string& name); + void loadDocument(const pugi::xml_document& doc); + + void invokeChangeListener() { if (listener) listener->onThemeChanged(); } + + static void storeCurrentName(absl::string_view name); + static std::string loadCurrentName(); + static const std::vector& getAvailableNames(); + + CColor* getColorFromName(absl::string_view name, bool fromInvertedPalette = false); + +private: + static std::vector extractAvailableNames(); +}; diff --git a/plugins/editor/src/editor/VSTGUIHelpers.cpp b/plugins/editor/src/editor/VSTGUIHelpers.cpp new file mode 100644 index 0000000..2e7d41d --- /dev/null +++ b/plugins/editor/src/editor/VSTGUIHelpers.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "VSTGUIHelpers.h" +#include "utility/vstgui_before.h" +#include "vstgui/lib/platform/platformfactory.h" +#if defined(_WIN32) +# include "vstgui/lib/platform/win32/win32factory.h" +#elif defined(__APPLE__) +#else +# include "vstgui/lib/platform/linux/linuxfactory.h" +#endif +#include "utility/vstgui_after.h" + +using namespace VSTGUI; + +#if defined(_WIN32) +fs::path getResourceBasePath() +{ + Optional optionalPath = getPlatformFactory().asWin32Factory()->getResourceBasePath(); + if (!optionalPath) + return fs::path(); + return fs::u8path(optionalPath->getString()); +} +#elif defined(__APPLE__) + // implemented in VSTGUIHelpers.mm +#else +fs::path getResourceBasePath() +{ + return fs::u8path(getPlatformFactory().asLinuxFactory()->getResourcePath()); +} +#endif diff --git a/plugins/editor/src/editor/VSTGUIHelpers.h b/plugins/editor/src/editor/VSTGUIHelpers.h new file mode 100644 index 0000000..01c8ef5 --- /dev/null +++ b/plugins/editor/src/editor/VSTGUIHelpers.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include + +fs::path getResourceBasePath(); diff --git a/plugins/editor/src/editor/VSTGUIHelpers.mm b/plugins/editor/src/editor/VSTGUIHelpers.mm new file mode 100644 index 0000000..7f753a2 --- /dev/null +++ b/plugins/editor/src/editor/VSTGUIHelpers.mm @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "VSTGUIHelpers.h" + +#if defined(__APPLE__) +#include "vstgui/lib/platform/mac/macfactory.h" +#import +#import + +fs::path getResourceBasePath() +{ + CFBundleRef bundle = VSTGUI::getPlatformFactory().asMacFactory()->getBundle(); + if (!bundle) + return fs::path(); + + NSURL* url = (__bridge_transfer NSURL*)CFBundleCopyResourcesDirectoryURL(bundle); + if (!url || ![url isFileURL]) + return fs::path(); + + return fs::u8path([[url path] UTF8String]); +} +#endif diff --git a/plugins/editor/src/editor/layout/about.hpp b/plugins/editor/src/editor/layout/about.hpp new file mode 100644 index 0000000..f4b5608 --- /dev/null +++ b/plugins/editor/src/editor/layout/about.hpp @@ -0,0 +1,28 @@ +/* This file is generated by the layout maker tool. */ +auto* const view__0 = createaboutView(CRect(0, 0, 800, 475), -1, "About sfizz", kCenterText, 14); +aboutView = view__0; +auto* const view__1 = createLogo(CRect(200, 0, 600, 180), -1, "", kCenterText, 14); +view__0->addView(view__1); +auto* const view__2 = createInfoBox(CRect(260, 180, 540, 260), -1, "", kCenterText, 14); +view__0->addView(view__2); +auto* const view__3 = createDescriptionBox(CRect(200, 260, 600, 360), -1, "", kCenterText, 14); +view__0->addView(view__3); +auto* const view__4 = createHoverBox(CRect(460, 435, 775, 460), -1, "", kCenterText, 12); +lblHover_ = view__4; +view__0->addView(view__4); +view__4->setVisible(false); +auto* const view__5 = createButtonSfztools(CRect(460, 405, 500, 445), kTagButtonSfztools, "", kCenterText, 30); +view__0->addView(view__5); +auto* const view__6 = createButtonGithub(CRect(530, 405, 570, 445), kTagButtonGithub, "", kCenterText, 30); +view__0->addView(view__6); +auto* const view__7 = createButtonDiscord(CRect(600, 405, 640, 445), kTagButtonDiscord, "", kCenterText, 30); +view__0->addView(view__7); +auto* const view__8 = createButtonOpencollective(CRect(670, 405, 710, 445), kTagButtonOpencollective, "", kCenterText, 30); +view__0->addView(view__8); +auto* const view__9 = createButtonSfzformat(CRect(740, 405, 780, 445), kTagButtonSfzformat, "", kCenterText, 30); +view__0->addView(view__9); +auto* const view__10 = createSysInfoKeyBox(CRect(15, 375, 105, 465), -1, "Format\nHost\nProcessor\nAddress\nSystem", kLeftText, 14); +view__0->addView(view__10); +auto* const view__11 = createSysInfoValueBox(CRect(105, 375, 450, 465), -1, "%PluginFormat%\n%HostProgram%\n%HostCPU%\n%HostBits% bits\n%HostOS%", kLeftText, 14); +lblSysInfoValue_ = view__11; +view__0->addView(view__11); diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp new file mode 100644 index 0000000..2faf625 --- /dev/null +++ b/plugins/editor/src/editor/layout/main.hpp @@ -0,0 +1,247 @@ +/* This file is generated by the layout maker tool. */ +auto* const view__0 = createLogicalGroup(CRect(0, 0, 775, 515), -1, "", kCenterText, 14); +mainView = view__0; +auto* const view__1 = createBackground(CRect(0, 110, 775, 445), -1, "", kCenterText, 14); +imageContainer_ = view__1; +view__0->addView(view__1); +enterPalette(invertedPalette); +auto* const view__2 = createLogicalGroup(CRect(0, 0, 775, 110), -1, "", kCenterText, 14); +view__0->addView(view__2); +auto* const view__3 = createRoundedGroup(CRect(5, 4, 180, 105), -1, "", kCenterText, 14); +view__2->addView(view__3); +auto* const view__4 = createAboutButton(CRect(27, 5, 147, 65), kTagAbout, "", kCenterText, 14); +view__3->addView(view__4); +auto* const view__5 = createInfoButton(CRect(51, 69, 83, 101), kTagFirstChangePanel+kPanelInfo, "", kCenterText, 30); +panelButtons_[kPanelInfo] = view__5; +view__3->addView(view__5); +auto* const view__6 = createCCButton(CRect(92, 69, 124, 101), kTagFirstChangePanel+kPanelControls, "", kCenterText, 30); +panelButtons_[kPanelControls] = view__6; +view__3->addView(view__6); +auto* const view__7 = createSettingsButton(CRect(132, 69, 164, 101), kTagFirstChangePanel+kPanelSettings, "", kCenterText, 30); +panelButtons_[kPanelSettings] = view__7; +view__3->addView(view__7); +auto* const view__8 = createHomeButton(CRect(11, 69, 43, 101), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 30); +panelButtons_[kPanelGeneral] = view__8; +view__3->addView(view__8); +auto* const view__9 = createRoundedGroup(CRect(185, 5, 550, 105), -1, "", kCenterText, 14); +view__2->addView(view__9); +auto* const view__10 = createHLine(CRect(10, 35, 355, 40), -1, "", kCenterText, 14); +view__9->addView(view__10); +auto* const view__11 = createHLine(CRect(10, 70, 355, 75), -1, "", kCenterText, 14); +view__9->addView(view__11); +auto* const view__12 = createClickableLabel(CRect(10, 5, 280, 35), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); +sfzFileLabel_ = view__12; +view__9->addView(view__12); +auto* const view__13 = createLabel(CRect(70, 40, 355, 70), -1, "", kLeftText, 20); +keyswitchLabel_ = view__13; +view__9->addView(view__13); +auto* const view__14 = createBadge(CRect(10, 42, 70, 68), -1, "", kCenterText, 20); +keyswitchBadge_ = view__14; +view__9->addView(view__14); +auto* const view__15 = createInactiveLabel(CRect(10, 40, 355, 70), -1, "No key switch", kLeftText, 20); +keyswitchInactiveLabel_ = view__15; +view__9->addView(view__15); +view__15->setVisible(false); +auto* const view__16 = createLabel(CRect(10, 73, 70, 98), -1, "Voices:", kRightText, 14); +view__9->addView(view__16); +auto* const view__17 = createPreviousFileButton(CRect(280, 10, 305, 35), kTagPreviousSfzFile, "", kCenterText, 24); +view__9->addView(view__17); +auto* const view__18 = createNextFileButton(CRect(305, 10, 330, 35), kTagNextSfzFile, "", kCenterText, 24); +view__9->addView(view__18); +auto* const view__19 = createChevronDropDown(CRect(330, 10, 355, 35), kTagFileOperations, "", kCenterText, 24); +fileOperationsMenu_ = view__19; +view__9->addView(view__19); +auto* const view__20 = createLabel(CRect(70, 73, 110, 98), -1, "", kCenterText, 14); +infoVoicesLabel_ = view__20; +view__9->addView(view__20); +auto* const view__21 = createLabel(CRect(130, 73, 170, 98), -1, "Max:", kRightText, 14); +view__9->addView(view__21); +auto* const view__22 = createLabel(CRect(170, 73, 205, 98), -1, "", kCenterText, 14); +numVoicesLabel_ = view__22; +view__9->addView(view__22); +auto* const view__23 = createLabel(CRect(245, 73, 305, 98), -1, "Memory:", kRightText, 14); +view__9->addView(view__23); +auto* const view__24 = createLabel(CRect(305, 73, 355, 98), -1, "", kCenterText, 14); +memoryLabel_ = view__24; +view__9->addView(view__24); +auto* const view__25 = createChevronValueDropDown(CRect(205, 77, 225, 97), kTagSetNumVoices, "", kCenterText, 16); +numVoicesSlider_ = view__25; +view__9->addView(view__25); +auto* const view__26 = createRoundedGroup(CRect(555, 5, 770, 105), -1, "", kCenterText, 14); +view__2->addView(view__26); +auto* const view__27 = createKnob48(CRect(32, 15, 80, 63), -1, "", kCenterText, 14); +view__26->addView(view__27); +view__27->setVisible(false); +auto* const view__28 = createValueLabel(CRect(27, 65, 87, 70), -1, "Center", kCenterText, 14); +view__26->addView(view__28); +view__28->setVisible(false); +auto* const view__29 = createKnobCCBox(CRect(5, 5, 75, 95), kTagSetCCVolume, "Volume", kCenterText, 14); +volumeCCKnob_ = view__29; +view__26->addView(view__29); +auto* const view__30 = createKnobCCBox(CRect(80, 5, 150, 95), kTagSetCCPan, "Pan", kCenterText, 14); +panCCKnob_ = view__30; +view__26->addView(view__30); +auto* const view__31 = createVMeter(CRect(155, 5, 178, 95), -1, "", kCenterText, 14); +meters_[0] = view__31; +view__26->addView(view__31); +auto* const view__32 = createVMeter(CRect(185, 5, 208, 95), -1, "", kCenterText, 14); +meters_[1] = view__32; +view__26->addView(view__32); +enterPalette(defaultPalette); +auto* const view__33 = createLogicalGroup(CRect(0, 110, 775, 445), -1, "", kCenterText, 14); +subPanels_[kPanelInfo] = view__33; +view__0->addView(view__33); +view__33->setVisible(false); +auto* const view__34 = createSquaredTransparentGroup(CRect(0, 0, 775, 335), -1, "", kCenterText, 14); +view__33->addView(view__34); +auto* const view__35 = createInfoLabel(CRect(20, 10, 80, 35), -1, "Curves:", kLeftText, 14); +view__34->addView(view__35); +auto* const view__36 = createInfoLabel(CRect(20, 35, 80, 60), -1, "Masters:", kLeftText, 14); +view__34->addView(view__36); +auto* const view__37 = createInfoLabel(CRect(20, 60, 80, 85), -1, "Groups:", kLeftText, 14); +view__34->addView(view__37); +auto* const view__38 = createInfoLabel(CRect(20, 85, 80, 110), -1, "Regions:", kLeftText, 14); +view__34->addView(view__38); +auto* const view__39 = createInfoLabel(CRect(20, 110, 80, 135), -1, "Samples:", kLeftText, 14); +view__34->addView(view__39); +auto* const view__40 = createInfoLabel(CRect(120, 10, 160, 35), -1, "0", kCenterText, 14); +infoCurvesLabel_ = view__40; +view__34->addView(view__40); +auto* const view__41 = createInfoLabel(CRect(120, 35, 160, 60), -1, "0", kCenterText, 14); +infoMastersLabel_ = view__41; +view__34->addView(view__41); +auto* const view__42 = createInfoLabel(CRect(120, 60, 160, 85), -1, "0", kCenterText, 14); +infoGroupsLabel_ = view__42; +view__34->addView(view__42); +auto* const view__43 = createInfoLabel(CRect(120, 85, 160, 110), -1, "0", kCenterText, 14); +infoRegionsLabel_ = view__43; +view__34->addView(view__43); +auto* const view__44 = createInfoLabel(CRect(120, 110, 160, 135), -1, "0", kCenterText, 14); +infoSamplesLabel_ = view__44; +view__34->addView(view__44); +auto* const view__45 = createLogicalGroup(CRect(0, 110, 775, 445), -1, "", kCenterText, 14); +subPanels_[kPanelControls] = view__45; +view__0->addView(view__45); +view__45->setVisible(false); +auto* const view__46 = createControlsPanel(CRect(0, 0, 775, 335), -1, "", kCenterText, 14); +controlsPanel_ = view__46; +view__45->addView(view__46); +auto* const view__47 = createLogicalGroup(CRect(0, 110, 775, 445), -1, "", kCenterText, 14); +subPanels_[kPanelSettings] = view__47; +view__0->addView(view__47); +auto* const view__48 = createSquaredGroup(CRect(0, 0, 775, 335), -1, "", kCenterText, 14); +view__47->addView(view__48); +auto* const view__49 = createValueMenu(CRect(180, 85, 250, 110), kTagSetOversampling, "", kCenterText, 14); +oversamplingSlider_ = view__49; +view__48->addView(view__49); +auto* const view__50 = createLabel(CRect(10, 85, 95, 110), -1, "Oversampling", kLeftText, 14); +view__48->addView(view__50); +auto* const view__51 = createLabel(CRect(10, 60, 85, 85), -1, "Preload size", kLeftText, 14); +view__48->addView(view__51); +auto* const view__52 = createValueMenu(CRect(180, 60, 250, 85), kTagSetPreloadSize, "", kCenterText, 14); +preloadSizeSlider_ = view__52; +view__48->addView(view__52); +auto* const view__53 = createValueMenu(CRect(430, 35, 510, 60), kTagSetSampleQuality, "", kCenterText, 14); +sampleQualitySlider_ = view__53; +view__48->addView(view__53); +auto* const view__54 = createLabel(CRect(270, 35, 320, 60), -1, "Sample", kLeftText, 14); +view__48->addView(view__54); +auto* const view__55 = createLabel(CRect(270, 85, 330, 110), -1, "Oscillator", kLeftText, 14); +view__48->addView(view__55); +auto* const view__56 = createValueMenu(CRect(430, 85, 510, 110), kTagSetOscillatorQuality, "", kCenterText, 14); +oscillatorQualitySlider_ = view__56; +view__48->addView(view__56); +auto* const view__57 = createOptionMenu(CRect(665, 35, 765, 60), kTagThemeMenu, "", kCenterText, 14); +themeMenu_ = view__57; +view__48->addView(view__57); +auto* const view__58 = createLabel(CRect(525, 35, 570, 60), -1, "Theme", kLeftText, 14); +view__48->addView(view__58); +auto* const view__59 = createLabel(CRect(525, 60, 620, 85), -1, "User SFZ folder", kLeftText, 14); +view__48->addView(view__59); +auto* const view__60 = createValueButton(CRect(665, 60, 765, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 14); +userFilesDirButton_ = view__60; +view__48->addView(view__60); +auto* const view__61 = createLabel(CRect(525, 200, 585, 225), -1, "Root key", kLeftText, 14); +view__48->addView(view__61); +auto* const view__62 = createLabel(CRect(525, 225, 595, 250), -1, "Frequency", kLeftText, 14); +view__48->addView(view__62); +auto* const view__63 = createStyledKnob(CRect(700, 250, 748, 298), kTagSetStretchedTuning, "", kCenterText, 14); +stretchedTuningSlider_ = view__63; +view__48->addView(view__63); +auto* const view__64 = createLabel(CRect(525, 260, 575, 285), -1, "Stretch", kLeftText, 14); +view__48->addView(view__64); +auto* const view__65 = createLabel(CRect(525, 175, 585, 200), -1, "Scala file", kLeftText, 14); +view__48->addView(view__65); +auto* const view__66 = createValueButton(CRect(624, 175, 724, 200), kTagLoadScalaFile, "DefaultScale", kCenterText, 14); +scalaFileButton_ = view__66; +view__48->addView(view__66); +auto* const view__67 = createValueMenu(CRect(685, 200, 720, 225), kTagSetScalaRootKey, "", kCenterText, 14); +scalaRootKeySlider_ = view__67; +view__48->addView(view__67); +auto* const view__68 = createValueMenu(CRect(720, 200, 750, 225), kTagSetScalaRootKey, "", kCenterText, 14); +scalaRootOctaveSlider_ = view__68; +view__48->addView(view__68); +auto* const view__69 = createResetSomethingButton(CRect(725, 175, 750, 200), kTagResetScalaFile, "", kCenterText, 14); +scalaResetButton_ = view__69; +view__48->addView(view__69); +auto* const view__70 = createLabel(CRect(270, 60, 415, 85), -1, "when freewheeling", kLeftText, 14); +view__48->addView(view__70); +auto* const view__71 = createValueMenu(CRect(430, 60, 510, 85), kTagSetFreewheelingSampleQuality, "", kCenterText, 14); +freewheelingSampleQualitySlider_ = view__71; +view__48->addView(view__71); +auto* const view__72 = createLabel(CRect(10, 110, 155, 135), -1, "Sustain cancels release", kLeftText, 14); +view__48->addView(view__72); +auto* const view__73 = createCheckbox(CRect(180, 110, 205, 135), kTagSetSustainCancelsRelease, "", kCenterText, 14); +sustainCancelsReleaseCheckbox_ = view__73; +view__48->addView(view__73); +auto* const view__74 = createLabel(CRect(10, 35, 235, 55), -1, "SFIZZ_VERSION", kCenterText, 14); +sfizzVersionLabel_ = view__74; +view__48->addView(view__74); +auto* const view__75 = createLabel(CRect(270, 5, 380, 30), -1, "Rendering quality", kLeftText, 14); +view__48->addView(view__75); +auto* const view__76 = createHLine(CRect(10, 25, 250, 30), -1, "", kCenterText, 14); +view__48->addView(view__76); +auto* const view__77 = createLabel(CRect(10, 5, 105, 30), -1, "Engine options", kLeftText, 14); +view__48->addView(view__77); +auto* const view__78 = createLabel(CRect(525, 5, 565, 30), -1, "Other", kLeftText, 14); +view__48->addView(view__78); +auto* const view__79 = createLabel(CRect(525, 145, 750, 170), -1, "Tuning", kLeftText, 14); +view__48->addView(view__79); +auto* const view__80 = createHLine(CRect(270, 25, 509, 30), -1, "", kCenterText, 14); +view__48->addView(view__80); +auto* const view__81 = createHLine(CRect(525, 25, 765, 30), -1, "", kCenterText, 14); +view__48->addView(view__81); +auto* const view__82 = createHLine(CRect(525, 165, 750, 170), -1, "", kCenterText, 14); +view__48->addView(view__82); +auto* const view__83 = createChevronValueDropDown(CRect(725, 225, 750, 250), kTagSetTuningFrequency, "", kCenterText, 24); +tuningFrequencyDropdown_ = view__83; +view__48->addView(view__83); +auto* const view__84 = createTextEdit(CRect(665, 225, 725, 250), kTagSetTuningFrequency, "", kCenterText, 14); +tuningFrequencyEdit_ = view__84; +view__48->addView(view__84); +auto* const view__85 = createLabel(CRect(270, 110, 415, 135), -1, "when freewheeling", kLeftText, 14); +view__48->addView(view__85); +auto* const view__86 = createValueMenu(CRect(430, 110, 510, 135), kTagSetFreewheelingOscillatorQuality, "", kCenterText, 14); +freewheelingOscillatorQualitySlider_ = view__86; +view__48->addView(view__86); +auto* const view__87 = createLabel(CRect(525, 85, 620, 110), -1, "Zoom", kLeftText, 14); +view__48->addView(view__87); +auto* const view__88 = createOptionMenu(CRect(665, 85, 765, 110), kTagZoomMenu, "", kCenterText, 14); +zoomMenu_ = view__88; +view__48->addView(view__88); +auto* const view__89 = createValueButton(CRect(665, 110, 765, 135), kTagSetDefaultZoom, "Set Default", kCenterText, 14); +defaultZoomButton_ = view__89; +view__48->addView(view__89); +auto* const view__90 = createPiano(CRect(0, 445, 775, 515), -1, "", kCenterText, 16); +piano_ = view__90; +view__0->addView(view__90); +auto* const view__91 = createLogicalGroup(CRect(0, 110, 775, 445), -1, "", kCenterText, 14); +subPanels_[kPanelGeneral] = view__91; +view__0->addView(view__91); +view__91->setVisible(false); +enterPalette(invertedPalette); +auto* const view__92 = createHoverBox(CRect(5, 105, 175, 130), -1, "", kCenterText, 14); +lblHover_ = view__92; +view__0->addView(view__92); +view__92->setVisible(false); +enterPalette(defaultPalette); diff --git a/plugins/editor/src/editor/utility/vstgui_after.h b/plugins/editor/src/editor/utility/vstgui_after.h new file mode 100644 index 0000000..69e3b4d --- /dev/null +++ b/plugins/editor/src/editor/utility/vstgui_after.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/plugins/editor/src/editor/utility/vstgui_before.h b/plugins/editor/src/editor/utility/vstgui_before.h new file mode 100644 index 0000000..8293e82 --- /dev/null +++ b/plugins/editor/src/editor/utility/vstgui_before.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wdeprecated-copy" +#pragma GCC diagnostic ignored "-Wmultichar" +#pragma GCC diagnostic ignored "-Wextra" +#endif diff --git a/plugins/editor/tools/layout-maker/LICENSE b/plugins/editor/tools/layout-maker/LICENSE new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/plugins/editor/tools/layout-maker/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/plugins/editor/tools/layout-maker/README b/plugins/editor/tools/layout-maker/README new file mode 100644 index 0000000..e8402cc --- /dev/null +++ b/plugins/editor/tools/layout-maker/README @@ -0,0 +1,2 @@ +This purpose of this tool is to accept UI designs make with Fluid, the FLTK +design editor, and convert these designs to music plugin interfaces. diff --git a/plugins/editor/tools/layout-maker/sources/layout.h b/plugins/editor/tools/layout-maker/sources/layout.h new file mode 100644 index 0000000..d68627f --- /dev/null +++ b/plugins/editor/tools/layout-maker/sources/layout.h @@ -0,0 +1,50 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.0 +// +// Copyright Jean Pierre Cimalando 2019-2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once +#include +#include + +struct LayoutImage { + std::string filepath; + int x = 0; + int y = 0; + int w = 0; + int h = 0; +}; + +struct LayoutItem { + std::string id; + std::string classname; + std::string label; + int x = 0; + int y = 0; + int w = 0; + int h = 0; + std::string box; + std::string down_box; + int labelfont = 0; + int labelsize = 14; + std::string labeltype; + int textsize = 14; + int align = 0; + double value = 0; + double minimum = 0; + double maximum = 0; + double step = 0; + std::string type; + std::string callback; + LayoutImage image; + bool hidden = false; + std::string comment; + std::vector items; +}; + +struct Layout { + std::vector items; +}; diff --git a/plugins/editor/tools/layout-maker/sources/main.cpp b/plugins/editor/tools/layout-maker/sources/main.cpp new file mode 100644 index 0000000..a456ace --- /dev/null +++ b/plugins/editor/tools/layout-maker/sources/main.cpp @@ -0,0 +1,190 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.0 +// +// Copyright Jean Pierre Cimalando 2019-2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include "layout.h" +#include "reader.h" +#include +#include +#include +#include + +/// +typedef std::unordered_map Metadata; + +static Metadata metadata_from_comment(absl::string_view comment) +{ + Metadata md; + + while (!comment.empty()) { + absl::string_view line; + + size_t pos = comment.find_first_of("\r\n"); + if (pos != comment.npos) { + line = comment.substr(0, pos); + comment.remove_prefix(pos + 1); + } + else { + line = comment; + comment = {}; + } + + line = absl::StripAsciiWhitespace(line); + if (line.empty() || line[0] == '#') + continue; + + std::string key, value; + pos = line.find_first_of('='); + if (pos != comment.npos) { + key = std::string(line.substr(0, pos)); + value = std::string(line.substr(pos + 1)); + } + else + key = std::string(line); + + md.emplace(std::move(key), std::move(value)); + } + + return md; +} + +/// +static std::string cstrQuote(absl::string_view text) +{ + std::string cstr; + cstr.reserve(2 * text.size() + 2); + + cstr.push_back('"'); + + for (char c : text) { + switch (c) { + case '\a' : + cstr.append("\\a"); + break; + case '\b' : + cstr.append("\\b"); + break; + case '\t' : + cstr.append("\\t"); + break; + case '\n' : + cstr.append("\\n"); + break; + case '\v' : + cstr.append("\\v"); + break; + case '\f' : + cstr.append("\\f"); + break; + case '\r' : + cstr.append("\\r"); + break; + case '"': + case '\\': + cstr.push_back('\\'); + cstr.push_back(c); + break; + default: + cstr.push_back(c); + break; + } + } + + cstr.push_back('"'); + return cstr; +} + +/// +static void codegen_item(int& idCounter, int parentId, int parentX, int parentY, const LayoutItem& item, absl::string_view oldPalette) +{ + const Metadata md = metadata_from_comment(item.comment); + + absl::string_view tag = "-1"; + absl::string_view newPalette; + + Metadata::const_iterator it; + it = md.find("tag"); + if (it != md.end()) + tag = it->second; + it = md.find("palette"); + if (it != md.end()) + newPalette = it->second; + + absl::string_view currentPalette = newPalette.empty() ? oldPalette : newPalette; + + int id = idCounter++; + int myX = item.x; + int myY = item.y; + if (parentId == -1) { + myX = 0; + myY = 0; + } + int relX = myX - parentX; + int relY = myY - parentY; + + //std::cout << "// Begin " << id << " " << item.classname << " {" << item.label << "}" << "\n"; + + if (!newPalette.empty()) + std::cout << "enterPalette(" << newPalette << ");\n"; + + absl::string_view label; + if (!item.label.empty() && item.labeltype != "NO_LABEL") + label = item.label; + + absl::string_view align = "kCenterText"; + if (item.align & 4) + align = "kLeftText"; + else if (item.align & 8) + align = "kRightText"; + + std::cout << "auto"/*item.classname*/ << "* const view__" << id << " = create" << item.classname << "(CRect(" << relX << ", " << relY << ", " << (relX + item.w) << ", " << (relY + item.h) << "), " << tag << ", " << cstrQuote(label) << ", " << align << ", " << item.labelsize << ");\n"; + + if (!item.id.empty()) + std::cout << item.id << " = view__" << id << ";\n"; + + if (parentId != -1) + std::cout << "view__" << parentId << "->addView(view__" << id << ");\n"; + + if (item.hidden) + std::cout << "view__" << id << "->setVisible(false);\n"; + + for (const LayoutItem& subItem : item.items) + codegen_item(idCounter, id, myX, myY, subItem, currentPalette); + + if (!newPalette.empty()) + std::cout << "enterPalette(" << oldPalette << ");\n"; + + //std::cout << "// End " << id << " " << item.classname << " {" << item.label << "}" << "\n"; +} + +static void codegen_layout(const LayoutItem& item) +{ + int idCounter = 0; + codegen_item(idCounter, -1, 0, 0, item, "defaultPalette"); +} + +/// +int main(int argc, char *argv[]) +{ + if (argc != 2) { + std::cerr << "Please indicate a fluid design file.\n"; + return 1; + } + + Layout layout = read_file_layout(argv[1]); + + if (layout.items.size() != 1) { + std::cerr << "There must be exactly 1 top level component."; + return 1; + } + + std::cout << "/* This file is generated by the layout maker tool. */\n"; + + codegen_layout(layout.items[0]); + + return 0; +} diff --git a/plugins/editor/tools/layout-maker/sources/reader.cpp b/plugins/editor/tools/layout-maker/sources/reader.cpp new file mode 100644 index 0000000..ab2fbdf --- /dev/null +++ b/plugins/editor/tools/layout-maker/sources/reader.cpp @@ -0,0 +1,316 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.0 +// +// Copyright Jean Pierre Cimalando 2019-2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#include "reader.h" +#include +#include +#include + +/// +class tokenizer { +public: + tokenizer( + absl::string_view text, + absl::string_view dropped_delims, + absl::string_view kept_delims); + + absl::string_view next(bool consume); + std::string string_until(char closing, bool consume); + bool at_end() const; + +private: + bool is_dropped(char c) const { + return dropped_delims_.find(c) != dropped_delims_.npos; + } + bool is_kept(char c) const { + return kept_delims_.find(c) != kept_delims_.npos; + } + bool is_delim(char c) const { + return dropped_delims_.find(c) != dropped_delims_.npos || + kept_delims_.find(c) != kept_delims_.npos; + } + +private: + absl::string_view text_; + absl::string_view dropped_delims_; + absl::string_view kept_delims_; +}; + +tokenizer::tokenizer( + absl::string_view text, + absl::string_view dropped_delims, + absl::string_view kept_delims) + : text_(text), dropped_delims_(dropped_delims), kept_delims_(kept_delims) +{ +} + +absl::string_view tokenizer::next(bool consume) +{ + absl::string_view text = text_; + + while (!text.empty() && is_dropped(text[0])) + text.remove_prefix(1); + + if (text.empty()) + return {}; + + size_t pos; + { + auto it = std::find_if(text.begin(), text.end(), [this](char c) -> bool { return is_delim(c); }); + if (it == text.end()) + pos = text.size(); + else { + pos = std::distance(text.begin(), it); + pos += is_kept(text[0]); + } + } + + absl::string_view token = text.substr(0, pos); + if (consume) + text_ = text.substr(pos); + return token; +} + +std::string tokenizer::string_until(char closing, bool consume) +{ + absl::string_view text = text_; + std::string result; + + result.reserve(256); + + for (char c; !text.empty() && + ((c = text[0]), text.remove_prefix(1), c != closing); ) { + if (c != '\\') + result.push_back(c); + else { + if (!text.empty()) { + c = text[0]; + text.remove_prefix(1); + result.push_back(c); + } + } + } + + if (consume) + text_ = text; + return result; +} + +bool tokenizer::at_end() const +{ + absl::string_view text = text_; + + while (!text.empty() && is_dropped(text[0])) + text.remove_prefix(1); + + return text.empty(); +} + +/// +typedef std::vector TokenList; +static bool read_file_lines(const char *filename, std::string &text); +static Layout read_tokens_layout(tokenizer &tkzr); + +Layout read_file_layout(const char *filename) +{ + std::string text; + if (!read_file_lines(filename, text)) + throw std::runtime_error("Cannot read fluid design file."); + + tokenizer tok(text, " \t\r\n", "{}"); + return read_tokens_layout(tok); +} + +static std::string consume_next_token(tokenizer &tkzr) +{ + if (tkzr.at_end()) + throw file_format_error("Premature end of tokens"); + return std::string(tkzr.next(true)); +} + +static bool try_consume_next_token(const char *text, tokenizer &tkzr) +{ + if (tkzr.at_end()) + return false; + + if (tkzr.next(false) != text) + return false; + + tkzr.next(true); + return true; +} + +static void ensure_next_token(const char *text, tokenizer &tkzr) +{ + std::string tok = consume_next_token(tkzr); + if (tok != text) + throw file_format_error("Unexpected token: " + tok); +} + +static std::string consume_any_string(tokenizer &tkzr) +{ + if (!tkzr.at_end() && tkzr.next(false) == "{") { + tkzr.next(true); + return tkzr.string_until('}', true); + } + else + return consume_next_token(tkzr); +} + +static int consume_int_token(tokenizer &tkzr) +{ + std::string text = consume_next_token(tkzr); + return std::stoi(text); +} + +static int consume_real_token(tokenizer &tkzr) +{ + std::string text = consume_next_token(tkzr); + return std::stod(text); +} + +static void consume_layout_item_properties(LayoutItem &item, tokenizer &tkzr) +{ + ensure_next_token("{", tkzr); + for (bool have = true; have;) { + if (try_consume_next_token("open", tkzr)) + ; // skip + else if (try_consume_next_token("selected", tkzr)) + ; // skip + else if (try_consume_next_token("label", tkzr)) + item.label = consume_any_string(tkzr); + else if (try_consume_next_token("xywh", tkzr)) { + ensure_next_token("{", tkzr); + item.x = consume_int_token(tkzr); + item.y = consume_int_token(tkzr); + item.w = consume_int_token(tkzr); + item.h = consume_int_token(tkzr); + ensure_next_token("}", tkzr); + } + else if (try_consume_next_token("box", tkzr)) + item.box = consume_next_token(tkzr); + else if (try_consume_next_token("down_box", tkzr)) + item.down_box = consume_next_token(tkzr); + else if (try_consume_next_token("labelfont", tkzr)) + item.labelfont = consume_int_token(tkzr); + else if (try_consume_next_token("labelsize", tkzr)) + item.labelsize = consume_int_token(tkzr); + else if (try_consume_next_token("labeltype", tkzr)) + item.labeltype = consume_any_string(tkzr); + else if (try_consume_next_token("textsize", tkzr)) + item.textsize = consume_int_token(tkzr); + else if (try_consume_next_token("align", tkzr)) + item.align = consume_int_token(tkzr); + else if (try_consume_next_token("type", tkzr)) + item.type = consume_any_string(tkzr); + else if (try_consume_next_token("callback", tkzr)) + item.callback = consume_any_string(tkzr); + else if (try_consume_next_token("class", tkzr)) + item.classname = consume_any_string(tkzr); + else if (try_consume_next_token("value", tkzr)) + item.value = consume_real_token(tkzr); + else if (try_consume_next_token("minimum", tkzr)) + item.minimum = consume_real_token(tkzr); + else if (try_consume_next_token("maximum", tkzr)) + item.maximum = consume_real_token(tkzr); + else if (try_consume_next_token("step", tkzr)) + item.step = consume_real_token(tkzr); + else if (try_consume_next_token("image", tkzr)) + item.image.filepath = consume_any_string(tkzr); + else if (try_consume_next_token("hide", tkzr)) + item.hidden = true; + else if (try_consume_next_token("visible", tkzr)) + /* skip */; + else if (try_consume_next_token("comment", tkzr)) + item.comment = consume_any_string(tkzr); + else + have = false; + } + ensure_next_token("}", tkzr); +} + +static LayoutItem consume_layout_item(const std::string &classname, tokenizer &tkzr, bool anonymous = false) +{ + LayoutItem item; + item.classname = classname; + if (!anonymous) + item.id = consume_any_string(tkzr); + consume_layout_item_properties(item, tkzr); + if (!tkzr.at_end() && tkzr.next(false) == "{") { + consume_next_token(tkzr); + for (std::string text; (text = consume_next_token(tkzr)) != "}";) { + if (text == "decl") { + consume_any_string(tkzr); + consume_any_string(tkzr); + } + else if (text == "Function") { + consume_any_string(tkzr); + consume_any_string(tkzr); + consume_any_string(tkzr); + } + else + item.items.push_back(consume_layout_item(text, tkzr)); + } + } + return item; +} + +static Layout read_tokens_layout(tokenizer &tkzr) +{ + Layout layout; + + std::string version_name; + std::string header_name; + std::string code_name; + + while (!tkzr.at_end()) { + std::string key = consume_next_token(tkzr); + + if (key == "version") + version_name = consume_next_token(tkzr); + else if (key == "header_name") { + ensure_next_token("{", tkzr); + header_name = consume_next_token(tkzr); + ensure_next_token("}", tkzr); + } + else if (key == "code_name") { + ensure_next_token("{", tkzr); + code_name = consume_next_token(tkzr); + ensure_next_token("}", tkzr); + } + else if (key == "decl") { + consume_any_string(tkzr); + consume_any_string(tkzr); + } + else if (key == "widget_class") { + key = consume_next_token(tkzr); + layout.items.push_back(consume_layout_item(key, tkzr, true)); + layout.items.back().id = key; + } + else + layout.items.push_back(consume_layout_item(key, tkzr)); + } + + return layout; +} + +/// +static bool read_file_lines(const char *filename, std::string &text) +{ + std::ifstream stream(filename); + std::string line; + + while (std::getline(stream, line)) { + if (!line.empty() && line[0] != '#') { + text.append(line); + text.push_back('\n'); + } + } + + return !stream.bad(); +} diff --git a/plugins/editor/tools/layout-maker/sources/reader.h b/plugins/editor/tools/layout-maker/sources/reader.h new file mode 100644 index 0000000..3dc6b4b --- /dev/null +++ b/plugins/editor/tools/layout-maker/sources/reader.h @@ -0,0 +1,21 @@ +// -*- C++ -*- +// SPDX-License-Identifier: BSL-1.0 +// +// Copyright Jean Pierre Cimalando 2019-2020. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once +#include "layout.h" +#include +#include + +Layout read_file_layout(const char *filename); + +/// +struct file_format_error : public std::runtime_error { +public: + explicit file_format_error(const std::string &reason = "Format error") + : runtime_error(reason) {} +}; diff --git a/plugins/lv2/CMakeLists.txt b/plugins/lv2/CMakeLists.txt new file mode 100644 index 0000000..13dfe3e --- /dev/null +++ b/plugins/lv2/CMakeLists.txt @@ -0,0 +1,145 @@ +set(LV2PLUGIN_PRJ_NAME "${PROJECT_NAME}_lv2") + +# Set the build directory as /lv2/.lv2/ +set(PROJECT_BINARY_DIR "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.lv2") + +# LV2 plugin specific settings +include(LV2Config) + +# Keep non build turtle files in IDE +set(LV2PLUGIN_TTL_SRC_FILES + manifest.ttl.in + ${PROJECT_NAME}.ttl.in +) +if(SFIZZ_LV2_UI) + list(APPEND LV2PLUGIN_TTL_SRC_FILES + ${PROJECT_NAME}_ui.ttl.in) +endif() +source_group("Turtle Files" FILES + ${LV2PLUGIN_TTL_SRC_FILES} +) +add_library(${LV2PLUGIN_PRJ_NAME} MODULE + ${PROJECT_NAME}.cpp + ${PROJECT_NAME}_lv2_common.cpp + ${LV2PLUGIN_TTL_SRC_FILES}) +target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE sfizz::sfizz sfizz::import sfizz::plugins-common) + +if(SFIZZ_LV2_UI) + add_library(${LV2PLUGIN_PRJ_NAME}_ui MODULE + ${PROJECT_NAME}_ui.cpp + ${PROJECT_NAME}_lv2_common.cpp + vstgui_helpers.h + vstgui_helpers.cpp) + target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE sfizz::editor sfizz::vstgui sfizz::plugins-common) +endif() + +if(SFIZZ_LV2_PSA) + target_compile_definitions(${LV2PLUGIN_PRJ_NAME} PRIVATE "SFIZZ_LV2_PSA=1") + if(SFIZZ_LV2_UI) + target_compile_definitions(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "SFIZZ_LV2_PSA=1") + endif() +endif() + +# Explicitely strip all symbols on Linux but lv2_descriptor() +# MacOS linker does not support this apparently https://bugs.webkit.org/show_bug.cgi?id=144555 +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/lv2.version") + # target_link_libraries(${LV2PLUGIN_PRJ_NAME} "-Wl,-u,lv2_descriptor") + if(SFIZZ_LV2_UI) + target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/lv2ui.version") + # target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui "-Wl,-u,lv2ui_descriptor") + endif() +endif() + +target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE . external/ardour) +if(SFIZZ_USE_SYSTEM_LV2) + find_path(LV2_INCLUDE_DIR "lv2.h") + if(NOT LV2_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find lv2 include directory") + else() + message(STATUS "Found system lv2") + endif() +else() + target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE vendor) +endif() +sfizz_enable_lto_if_needed(${LV2PLUGIN_PRJ_NAME}) +if(MINGW) + set_target_properties(${LV2PLUGIN_PRJ_NAME} PROPERTIES LINK_FLAGS "-static") +endif() + +if(SFIZZ_LV2_UI) + target_include_directories(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE . external/ardour) + if(NOT SFIZZ_USE_SYSTEM_LV2) + target_include_directories(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE vendor) + endif() + sfizz_enable_lto_if_needed(${LV2PLUGIN_PRJ_NAME}_ui) + if(MINGW) + set_target_properties(${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LINK_FLAGS "-static") + endif() +endif() + +# Define a preprocessor variable to indicate a build with UI enabled +if(SFIZZ_LV2_UI) + target_compile_definitions(${LV2PLUGIN_PRJ_NAME} PRIVATE "SFIZZ_LV2_UI=1") + target_compile_definitions(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "SFIZZ_LV2_UI=1") +endif() + +# Remove the "lib" prefix, rename the target name and build it in the .lv build dir +# /lv2/_lv2. to +# /lv2/.lv2/. +set_target_properties(${LV2PLUGIN_PRJ_NAME} PROPERTIES PREFIX "") +set_target_properties(${LV2PLUGIN_PRJ_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}") +set_target_properties(${LV2PLUGIN_PRJ_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary/$<0:>") + +if(SFIZZ_LV2_UI) + set_target_properties(${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES PREFIX "") + set_target_properties(${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES OUTPUT_NAME "${PROJECT_NAME}_ui") + set_target_properties(${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary/$<0:>") +endif() + +# Generate *.ttl files from *.in sources, +# create the destination directory if it doesn't exists and copy needed files +file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}) +configure_file(manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl) +configure_file(${PROJECT_NAME}.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ttl) +if(SFIZZ_LV2_UI) + configure_file(${PROJECT_NAME}_ui.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_ui.ttl) +endif() +configure_file(LICENSE.md.in ${PROJECT_BINARY_DIR}/LICENSE.md) + +# Generate controllers.ttl +sfizz_lv2_generate_controllers_ttl("${PROJECT_BINARY_DIR}/controllers.ttl") + +# Copy resource files into the bundle +set(LV2_RESOURCES + DefaultInstrument.sfz + DefaultScale.scl) +execute_process( + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources") +foreach(res ${LV2_RESOURCES}) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${res}" + DESTINATION "${PROJECT_BINARY_DIR}/Contents/Resources") +endforeach() + +# Copy editor resources +if(SFIZZ_LV2_UI) + execute_process( + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources") + copy_editor_resources( + ${LV2PLUGIN_PRJ_NAME}_ui + "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources" + "${PROJECT_BINARY_DIR}/Contents/Resources") +endif() + +# Installation +if(NOT MSVC) + install(DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR} + COMPONENT "lv2" + USE_SOURCE_PERMISSIONS) + bundle_dylibs(lv2 + "${LV2PLUGIN_INSTALL_DIR}/${PROJECT_NAME}.lv2/Contents/Binary/sfizz.so" + COMPONENT "lv2") + bundle_dylibs(lv2-ui + "${LV2PLUGIN_INSTALL_DIR}/${PROJECT_NAME}.lv2/Contents/Binary/sfizz_ui.so" + COMPONENT "lv2") +endif() diff --git a/plugins/lv2/LICENSE.md.in b/plugins/lv2/LICENSE.md.in new file mode 100644 index 0000000..1d0b134 --- /dev/null +++ b/plugins/lv2/LICENSE.md.in @@ -0,0 +1,12 @@ +Most of the code in the plug comes from various examples in the LV2 distribution and +example plugins. As such, the source code of the LV2 plugin version of `sfizz` is +distributed under the same terms as the LV2 specification, which is the ISC license. +Check the LICENSE.md at (@LV2PLUGIN_REPOSITORY@) file for more +information about the license of the source code of the `sfizz` library and its +contributors. + +The `sfizz` LV2 plugin can be optionally statically built against `libsndfile` +from Erik de Castro Lopo (http://www.mega-nerd.com/libsndfile). `libsndfile` +is distributed under the terms of the LGPL v2 or v3. If you distribute a +statically-linked LV2 version of `sfizz` built against `libsndfile`, you need +to respect the terms of the LGPL v2 or v3 license. diff --git a/plugins/lv2/external/ardour/ardour/lv2_extensions.h b/plugins/lv2/external/ardour/ardour/lv2_extensions.h new file mode 100644 index 0000000..fde2934 --- /dev/null +++ b/plugins/lv2/external/ardour/ardour/lv2_extensions.h @@ -0,0 +1,282 @@ +/* + Copyright 2016 Robin Gareus + + 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 _ardour_lv2_extensions_h_ +#define _ardour_lv2_extensions_h_ + +#include + +/** + @defgroup lv2inlinedisplay Inline-Display + + Support for displaying a miniaturized generic view + directly in the host's Mixer Window. + + @{ +*/ + +#define LV2_INLINEDISPLAY_URI "http://harrisonconsoles.com/lv2/inlinedisplay" +#define LV2_INLINEDISPLAY_PREFIX LV2_INLINEDISPLAY_URI "#" +#define LV2_INLINEDISPLAY__interface LV2_INLINEDISPLAY_PREFIX "interface" +#define LV2_INLINEDISPLAY__queue_draw LV2_INLINEDISPLAY_PREFIX "queue_draw" +#define LV2_INLINEDISPLAY__in_gui LV2_INLINEDISPLAY_PREFIX "in_gui" + +/** Opaque handle for LV2_Inline_Display::queue_draw() */ +typedef void* LV2_Inline_Display_Handle; + +/** raw image pixmap format is ARGB32, + * the data pointer is owned by the plugin and must be valid + * from the first call to render until cleanup. + */ +typedef struct { + unsigned char *data; + int width; + int height; + int stride; +} LV2_Inline_Display_Image_Surface; + +/** a LV2 Feature provided by the Host to the plugin */ +typedef struct { + /** Opaque host data */ + LV2_Inline_Display_Handle handle; + /** Request from run() that the host should call render() at a later time + * to update the inline display */ + void (*queue_draw)(LV2_Inline_Display_Handle handle); +} LV2_Inline_Display; + +/** + * Plugin Inline-Display Interface. + */ +typedef struct { + /** + * The render method. This is called by the host in a non-realtime context, + * usually the main GUI thread. + * The data pointer is owned by the plugin and must be valid + * from the first call to render until cleanup. + * + * @param instance The LV2 instance + * @param w the max available width + * @param h the max available height + * @return pointer to a LV2_Inline_Display_Image_Surface or NULL + */ + LV2_Inline_Display_Image_Surface* (*render)(LV2_Handle instance, uint32_t w, uint32_t h); +} LV2_Inline_Display_Interface; + +/** + @} +*/ + +/** + @defgroup lv2automate Self-Automation + + Support for plugins to write automation data via Atom Events + + @{ +*/ + +#define LV2_AUTOMATE_URI "http://ardour.org/lv2/automate" +#define LV2_AUTOMATE_URI_PREFIX LV2_AUTOMATE_URI "#" +/** an lv2:optionalFeature */ +#define LV2_AUTOMATE_URI__can_write LV2_AUTOMATE_URI_PREFIX "canWriteAutomatation" +/** atom:supports */ +#define LV2_AUTOMATE_URI__control LV2_AUTOMATE_URI_PREFIX "automationControl" +/** lv2:portProperty */ +#define LV2_AUTOMATE_URI__controlled LV2_AUTOMATE_URI_PREFIX "automationControlled" +#define LV2_AUTOMATE_URI__controller LV2_AUTOMATE_URI_PREFIX "automationController" + +/** atom messages */ +#define LV2_AUTOMATE_URI__event LV2_AUTOMATE_URI_PREFIX "event" +#define LV2_AUTOMATE_URI__setup LV2_AUTOMATE_URI_PREFIX "setup" +#define LV2_AUTOMATE_URI__finalize LV2_AUTOMATE_URI_PREFIX "finalize" +#define LV2_AUTOMATE_URI__start LV2_AUTOMATE_URI_PREFIX "start" +#define LV2_AUTOMATE_URI__end LV2_AUTOMATE_URI_PREFIX "end" +#define LV2_AUTOMATE_URI__parameter LV2_AUTOMATE_URI_PREFIX "parameter" +#define LV2_AUTOMATE_URI__value LV2_AUTOMATE_URI_PREFIX "value" + +/** + @} +*/ + +/** + @defgroup lv2license License-Report + + Allow for commercial LV2 to report their + licensing status. + + @{ +*/ + +#define LV2_PLUGINLICENSE_URI "http://harrisonconsoles.com/lv2/license" +#define LV2_PLUGINLICENSE_PREFIX LV2_PLUGINLICENSE_URI "#" +#define LV2_PLUGINLICENSE__interface LV2_PLUGINLICENSE_PREFIX "interface" +#define LV2_PLUGINLICENSE__interface2 LV2_PLUGINLICENSE_PREFIX "interface2" + + +typedef struct _LV2_License_Interface { + /* @return -1 if no license is needed; 0 if unlicensed, 1 if licensed */ + int (*is_licensed)(LV2_Handle instance); + /* @return a string copy of the licensee name if licensed, or NULL, the caller needs to free this */ + char* (*licensee)(LV2_Handle instance); + /* @return a URI identifying the plugin-bundle or plugin for which a given license is valid */ + const char* (*product_uri)(LV2_Handle instance); + /* @return human readable product name for the URI */ + const char* (*product_name)(LV2_Handle instance); + /* @return link to website or webstore */ + const char* (*store_url)(LV2_Handle instance); + /* interface2 ext: preferred location to install the license file, the caller needs to free this */ + char* (*preferred_license_file_path)(LV2_Handle instance); + /* interface2 ext: currently used license file (if any, may be NULL), the caller needs to free this */ + char* (*current_license_file_path)(LV2_Handle instance); + /* interface2 ext: free() allocated strings (licensee, license_file_paths) */ + void (*free)(char*); +} LV2_License_Interface; + +/** + @} +*/ + +/** + @defgroup lv2bypass Plugin-provided bypass + + A port with the designation "processing#enable" must + control a plugin's internal bypass mode. + + If the port value is larger than zero the plugin processes + normally. + + If the port value is zero, the plugin is expected to bypass + all signals unmodified. + + The plugin is responsible for providing a click-free transition + between the states. + + (values less than zero are reserved for future use: + e.g click-free insert/removal of latent plugins. + Generally values <= 0 are to be treated as bypassed.) + + lv2:designation ; + + @{ +*/ + +#define LV2_PROCESSING_URI "http://ardour.org/lv2/processing" +#define LV2_PROCESSING_URI_PREFIX LV2_PROCESSING_URI "#" +#define LV2_PROCESSING_URI__enable LV2_PROCESSING_URI_PREFIX "enable" + +/** + @} +*/ + + +/** + @defgroup lv2routing plugin port/routing control + + This is a "feature" to simplify per port meta-data of + http://lv2plug.in/ns/ext/port-groups/port-groups.html#source + + Plugins using this feature provide a strong hint that the host + should always connect all audio output-ports. + + This allows mono->stereo plugins to override strict_io rules. + + @{ +*/ + +#define LV2_ROUTING_URI "http://harrisonconsoles.com/lv2/routing" +#define LV2_ROUTING_PREFIX LV2_ROUTING_URI "#" +#define LV2_ROUTING__connectAllOutputs LV2_ROUTING_PREFIX "connectAllOutputs" + +/** + @} +*/ + +/** + @defgroup lv2midnam MIDI Naming + + @{ +*/ + + +#define LV2_MIDNAM_URI "http://ardour.org/lv2/midnam" +#define LV2_MIDNAM_PREFIX LV2_MIDNAM_URI "#" +#define LV2_MIDNAM__interface LV2_MIDNAM_PREFIX "interface" +#define LV2_MIDNAM__update LV2_MIDNAM_PREFIX "update" + +typedef void* LV2_Midnam_Handle; + +/** a LV2 Feature provided by the Host to the plugin */ +typedef struct { + /** Opaque host data */ + LV2_Midnam_Handle handle; + /** Request from run() that the host should re-read the midnam */ + void (*update)(LV2_Midnam_Handle handle); +} LV2_Midnam; + +typedef struct { + /** Query midnam document. The plugin + * is expected to return a null-terminated XML + * text which is a valid midnam desciption + * (or NULL in case of error). + * + * The midnam \ must be unique and + * specific for the given plugin-instance. + */ + char* (*midnam)(LV2_Handle instance); + + /** The unique model id used ith the midnam, + * (or NULL). + */ + char* (*model)(LV2_Handle instance); + + /** free allocated strings. The host + * calls this for every value returned by + * \ref midnam and \ref model. + */ + void (*free)(char*); +} LV2_Midnam_Interface; + +/** + @} +*/ + +/** + @defgroup lv2bankpatch MIDI Bank/Patch Notifications + + LV2 extension to allow a synth to inform a host about the + currentl used MIDI bank/program. + + @{ +*/ + + +#define LV2_BANKPATCH_URI "http://ardour.org/lv2/bankpatch" +#define LV2_BANKPATCH_PREFIX LV2_BANKPATCH_URI "#" +#define LV2_BANKPATCH__notify LV2_BANKPATCH_PREFIX "notify" + +typedef void* LV2_BankPatch_Handle; + +/** a LV2 Feature provided by the Host to the plugin */ +typedef struct { + /** Opaque host data */ + LV2_BankPatch_Handle handle; + /** Info from plugin's run(), notify host that bank/program changed */ + void (*notify)(LV2_BankPatch_Handle handle, uint8_t channel, uint32_t bank, uint8_t pgm); +} LV2_BankPatch; + +/** + @} +*/ +#endif diff --git a/plugins/lv2/lv2.version b/plugins/lv2/lv2.version new file mode 100644 index 0000000..5ac5364 --- /dev/null +++ b/plugins/lv2/lv2.version @@ -0,0 +1,4 @@ +LV2ABI_1.0 { + global: *lv2_descriptor*; + local: *; +}; diff --git a/plugins/lv2/lv2ui.version b/plugins/lv2/lv2ui.version new file mode 100644 index 0000000..95c55e1 --- /dev/null +++ b/plugins/lv2/lv2ui.version @@ -0,0 +1,4 @@ +LV2UIABI_1.0 { + global: *lv2ui_descriptor*; + local: *; +}; diff --git a/plugins/lv2/manifest.ttl.in b/plugins/lv2/manifest.ttl.in new file mode 100644 index 0000000..37305af --- /dev/null +++ b/plugins/lv2/manifest.ttl.in @@ -0,0 +1,18 @@ +@prefix lv2: . +@prefix rdfs: . +@prefix ui: . + +<@LV2PLUGIN_URI@> + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso <@PROJECT_NAME@.ttl>, . + +<@LV2PLUGIN_URI@-multi> + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso <@PROJECT_NAME@.ttl>, . + +@LV2PLUGIN_IF_ENABLE_UI@<@LV2PLUGIN_URI@#ui> +@LV2PLUGIN_IF_ENABLE_UI@ a ui:@LV2_UI_TYPE@ ; +@LV2PLUGIN_IF_ENABLE_UI@ ui:binary ; +@LV2PLUGIN_IF_ENABLE_UI@ rdfs:seeAlso <@PROJECT_NAME@_ui.ttl> . diff --git a/plugins/lv2/resources/DefaultInstrument.sfz b/plugins/lv2/resources/DefaultInstrument.sfz new file mode 100644 index 0000000..5f82f9c --- /dev/null +++ b/plugins/lv2/resources/DefaultInstrument.sfz @@ -0,0 +1,3 @@ + +sample=*sine +ampeg_attack=0.02 ampeg_release=0.1 diff --git a/plugins/lv2/resources/DefaultScale.scl b/plugins/lv2/resources/DefaultScale.scl new file mode 100644 index 0000000..081422f --- /dev/null +++ b/plugins/lv2/resources/DefaultScale.scl @@ -0,0 +1,14 @@ +Equal temperament +12 +100.0 +200.0 +300.0 +400.0 +500.0 +600.0 +700.0 +800.0 +900.0 +1000.0 +1100.0 +1200.0 diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp new file mode 100644 index 0000000..ea7bbae --- /dev/null +++ b/plugins/lv2/sfizz.cpp @@ -0,0 +1,1978 @@ +/* + SPDX-License-Identifier: ISC + + Sfizz LV2 plugin + + Copyright 2019-2020, Paul Ferrand + + This file was based on skeleton and example code from the LV2 plugin + distribution available at http://lv2plug.in/ + + The LV2 sample plugins have the following copyright and notice, which are + extended to the current work: + Copyright 2011-2016 David Robillard + Copyright 2011 Gabriel M. Beddingfield + Copyright 2011 James Morris + + 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. + + Compiling this plugin statically against libsndfile implies distributing it + under the terms of the LGPL v3 license. See the LICENSE.md file for more + information. If you did not receive a LICENSE.md file, inform the current + maintainer. + + 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. +*/ + +#include "sfizz_lv2.h" +#include "sfizz_lv2_plugin.h" + +#include "sfizz/import/sfizz_import.h" +#include "plugin/InstrumentDescription.h" + +#include +#include +#include + +#include + +#define CHANNEL_MASK 0x0F +#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK) +#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK) +#define PITCH_BUILD_AND_CENTER(first_byte, last_byte) (int)(((unsigned int)last_byte << 7) + (unsigned int)first_byte) - 8192 +#define MAX_BLOCK_SIZE 8192 +#define MAX_VOICES 256 +#define DEFAULT_VOICES 64 +#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1 +#define DEFAULT_PRELOAD 8192 +#define LOG_SAMPLE_COUNT 48000 +#define UNUSED(x) (void)(x) + +#ifndef NDEBUG +#define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__) +#else +#define LV2_DEBUG(...) +#endif + +enum +{ + SFIZZ_TIMEINFO_POSITION = 1 << 0, + SFIZZ_TIMEINFO_SIGNATURE = 1 << 1, + SFIZZ_TIMEINFO_TEMPO = 1 << 2, + SFIZZ_TIMEINFO_SPEED = 1 << 3, +}; + +/// +static bool +sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path); + +static bool +sfizz_lv2_load_scala_file(sfizz_plugin_t *self, const char *file_path); + +/// +static void +sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle, + char *path) +{ + (void)handle; + free(path); +} + +static LV2_State_Free_Path sfizz_State_Free_Path = +{ + NULL, + &sfizz_lv2_state_free_path, +}; + +static void +sfizz_lv2_map_required_uris(sfizz_plugin_t *self) +{ + LV2_URID_Map *map = self->map; + self->midi_event_uri = map->map(map->handle, LV2_MIDI__MidiEvent); + self->max_block_length_uri = map->map(map->handle, LV2_BUF_SIZE__maxBlockLength); + self->nominal_block_length_uri = map->map(map->handle, LV2_BUF_SIZE__nominalBlockLength); + self->sample_rate_uri = map->map(map->handle, LV2_PARAMETERS__sampleRate); + self->atom_float_uri = map->map(map->handle, LV2_ATOM__Float); + self->atom_double_uri = map->map(map->handle, LV2_ATOM__Double); + self->atom_int_uri = map->map(map->handle, LV2_ATOM__Int); + self->atom_long_uri = map->map(map->handle, LV2_ATOM__Long); + self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path); + self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID); + self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object); + self->atom_blank_uri = map->map(map->handle, LV2_ATOM__Blank); + self->patch_set_uri = map->map(map->handle, LV2_PATCH__Set); + self->patch_get_uri = map->map(map->handle, LV2_PATCH__Get); + self->patch_put_uri = map->map(map->handle, LV2_PATCH__Put); + self->patch_body_uri = map->map(map->handle, LV2_PATCH__body); + self->patch_property_uri = map->map(map->handle, LV2_PATCH__property); + self->patch_value_uri = map->map(map->handle, LV2_PATCH__value); + self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged); + self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); + self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); + self->sfizz_description_uri = map->map(map->handle, SFIZZ__description); + self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices); + self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize); + self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); + self->sfizz_last_keyswitch_uri = map->map(map->handle, SFIZZ__lastKeyswitch); + self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); + self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); + self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify); + self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); + self->time_position_uri = map->map(map->handle, LV2_TIME__Position); + self->time_bar_uri = map->map(map->handle, LV2_TIME__bar); + self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat); + self->time_beat_unit_uri = map->map(map->handle, LV2_TIME__beatUnit); + self->time_beats_per_bar_uri = map->map(map->handle, LV2_TIME__beatsPerBar); + self->time_beats_per_minute_uri = map->map(map->handle, LV2_TIME__beatsPerMinute); + self->time_speed_uri = map->map(map->handle, LV2_TIME__speed); +} + +static bool +sfizz_atom_extract_real(sfizz_plugin_t *self, const LV2_Atom *atom, double *real) +{ + if (!atom) + return false; + + const LV2_URID type = atom->type; + + if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) { + *real = ((const LV2_Atom_Int *)atom)->body; + return true; + } + if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) { + *real = ((const LV2_Atom_Long *)atom)->body; + return true; + } + if (type == self->atom_float_uri && atom->size >= sizeof(float)) { + *real = ((const LV2_Atom_Float *)atom)->body; + return true; + } + if (type == self->atom_double_uri && atom->size >= sizeof(double)) { + *real = ((const LV2_Atom_Double *)atom)->body; + return true; + } + + return false; +} + +static bool +sfizz_atom_extract_integer(sfizz_plugin_t *self, const LV2_Atom *atom, int64_t *integer) +{ + if (!atom) + return false; + + const LV2_URID type = atom->type; + + if (type == self->atom_int_uri && atom->size >= sizeof(int32_t)) { + *integer = ((const LV2_Atom_Int *)atom)->body; + return true; + } + if (type == self->atom_long_uri && atom->size >= sizeof(int64_t)) { + *integer = ((const LV2_Atom_Long *)atom)->body; + return true; + } + if (type == self->atom_float_uri && atom->size >= sizeof(float)) { + *integer = (int64_t)((const LV2_Atom_Float *)atom)->body; + return true; + } + if (type == self->atom_double_uri && atom->size >= sizeof(double)) { + *integer = (int64_t)((const LV2_Atom_Double *)atom)->body; + return true; + } + + return false; +} + +static void +connect_port_stereo(LV2_Handle instance, + uint32_t port, + void *data) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + switch (port) + { + case SFIZZ_CONTROL: + self->control_port = (const LV2_Atom_Sequence *)data; + break; + case SFIZZ_AUTOMATE: + self->automate_port = (LV2_Atom_Sequence *)data; + break; + case SFIZZ_LEFT: + self->output_buffers[0] = (float *)data; + break; + case SFIZZ_RIGHT: + self->output_buffers[1] = (float *)data; + break; + case SFIZZ_VOLUME: + self->volume_port = (const float *)data; + break; + case SFIZZ_POLYPHONY: + self->polyphony_port = (const float *)data; + break; + case SFIZZ_OVERSAMPLING: + self->oversampling_port = (const float *)data; + break; + case SFIZZ_PRELOAD: + self->preload_port = (const float *)data; + break; + case SFIZZ_FREEWHEELING: + self->freewheel_port = (const float *)data; + break; + case SFIZZ_SCALA_ROOT_KEY: + self->scala_root_key_port = (const float *)data; + break; + case SFIZZ_TUNING_FREQUENCY: + self->tuning_frequency_port = (const float *)data; + break; + case SFIZZ_STRETCH_TUNING: + self->stretch_tuning_port = (const float *)data; + break; + case SFIZZ_SAMPLE_QUALITY: + self->sample_quality_port = (const float *)data; + break; + case SFIZZ_OSCILLATOR_QUALITY: + self->oscillator_quality_port = (const float *)data; + break; + case SFIZZ_ACTIVE_VOICES: + self->active_voices_port = (float *)data; + break; + case SFIZZ_NUM_CURVES: + self->num_curves_port = (float *)data; + break; + case SFIZZ_NUM_MASTERS: + self->num_masters_port = (float *)data; + break; + case SFIZZ_NUM_GROUPS: + self->num_groups_port = (float *)data; + break; + case SFIZZ_NUM_REGIONS: + self->num_regions_port = (float *)data; + break; + case SFIZZ_NUM_SAMPLES: + self->num_samples_port = (float *)data; + break; + case SFIZZ_FREEWHEELING_SAMPLE_QUALITY: + self->freewheeling_sample_quality_port = (const float *)data; + break; + case SFIZZ_FREEWHEELING_OSCILLATOR_QUALITY: + self->freewheeling_oscillator_quality_port = (const float *)data; + break; + case SFIZZ_SUSTAIN_CANCELS_RELEASE: + self->sustain_cancels_release_port = (const float *)data; + break; + default: + break; + } +} + +static void +connect_port_multi(LV2_Handle instance, + uint32_t port, + void *data) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + switch (port) + { + case SFIZZ_MULTI_CONTROL: + self->control_port = (const LV2_Atom_Sequence *)data; + break; + case SFIZZ_MULTI_AUTOMATE: + self->automate_port = (LV2_Atom_Sequence *)data; + break; + case SFIZZ_MULTI_OUT1L: + self->output_buffers[0] = (float *)data; + break; + case SFIZZ_MULTI_OUT1R: + self->output_buffers[1] = (float *)data; + break; + case SFIZZ_MULTI_OUT2L: + self->output_buffers[2] = (float *)data; + break; + case SFIZZ_MULTI_OUT2R: + self->output_buffers[3] = (float *)data; + break; + case SFIZZ_MULTI_OUT3L: + self->output_buffers[4] = (float *)data; + break; + case SFIZZ_MULTI_OUT3R: + self->output_buffers[5] = (float *)data; + break; + case SFIZZ_MULTI_OUT4L: + self->output_buffers[6] = (float *)data; + break; + case SFIZZ_MULTI_OUT4R: + self->output_buffers[7] = (float *)data; + break; + case SFIZZ_MULTI_OUT5L: + self->output_buffers[8] = (float *)data; + break; + case SFIZZ_MULTI_OUT5R: + self->output_buffers[9] = (float *)data; + break; + case SFIZZ_MULTI_OUT6L: + self->output_buffers[10] = (float *)data; + break; + case SFIZZ_MULTI_OUT6R: + self->output_buffers[11] = (float *)data; + break; + case SFIZZ_MULTI_OUT7L: + self->output_buffers[12] = (float *)data; + break; + case SFIZZ_MULTI_OUT7R: + self->output_buffers[13] = (float *)data; + break; + case SFIZZ_MULTI_OUT8L: + self->output_buffers[14] = (float *)data; + break; + case SFIZZ_MULTI_OUT8R: + self->output_buffers[15] = (float *)data; + break; + case SFIZZ_MULTI_VOLUME: + self->volume_port = (const float *)data; + break; + case SFIZZ_MULTI_POLYPHONY: + self->polyphony_port = (const float *)data; + break; + case SFIZZ_MULTI_OVERSAMPLING: + self->oversampling_port = (const float *)data; + break; + case SFIZZ_MULTI_PRELOAD: + self->preload_port = (const float *)data; + break; + case SFIZZ_MULTI_FREEWHEELING: + self->freewheel_port = (const float *)data; + break; + case SFIZZ_MULTI_SCALA_ROOT_KEY: + self->scala_root_key_port = (const float *)data; + break; + case SFIZZ_MULTI_TUNING_FREQUENCY: + self->tuning_frequency_port = (const float *)data; + break; + case SFIZZ_MULTI_STRETCH_TUNING: + self->stretch_tuning_port = (const float *)data; + break; + case SFIZZ_MULTI_SAMPLE_QUALITY: + self->sample_quality_port = (const float *)data; + break; + case SFIZZ_MULTI_OSCILLATOR_QUALITY: + self->oscillator_quality_port = (const float *)data; + break; + case SFIZZ_MULTI_ACTIVE_VOICES: + self->active_voices_port = (float *)data; + break; + case SFIZZ_MULTI_NUM_CURVES: + self->num_curves_port = (float *)data; + break; + case SFIZZ_MULTI_NUM_MASTERS: + self->num_masters_port = (float *)data; + break; + case SFIZZ_MULTI_NUM_GROUPS: + self->num_groups_port = (float *)data; + break; + case SFIZZ_MULTI_NUM_REGIONS: + self->num_regions_port = (float *)data; + break; + case SFIZZ_MULTI_NUM_SAMPLES: + self->num_samples_port = (float *)data; + break; + case SFIZZ_MULTI_FREEWHEELING_SAMPLE_QUALITY: + self->freewheeling_sample_quality_port = (const float *)data; + break; + case SFIZZ_MULTI_FREEWHEELING_OSCILLATOR_QUALITY: + self->freewheeling_oscillator_quality_port = (const float *)data; + break; + case SFIZZ_MULTI_SUSTAIN_CANCELS_RELEASE: + self->sustain_cancels_release_port = (const float *)data; + break; + default: + break; + } +} + +static void +connect_port(LV2_Handle instance, + uint32_t port, + void *data) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + if (self->multi_out) + connect_port_multi(instance, port, data); + else + connect_port_stereo(instance, port, data); +} + +// This function sets the sample rate in the self parameter but does not update it. +static void +sfizz_lv2_parse_sample_rate(sfizz_plugin_t* self, const LV2_Options_Option* opt) +{ + if (opt->type == self->atom_float_uri) + { + // self->sample_rate = *(float*)opt->value; + LV2_DEBUG("Attempted to change the sample rate to %.2f (original was %.2f); ignored", + *(float*)opt->value, + self->sample_rate); + } + else if (opt->type == self->atom_int_uri) + { + // self->sample_rate = *(int*)opt->value; + LV2_DEBUG("Attempted to change the sample rate to %d (original was %.2f); ignored", + *(int*)opt->value, + self->sample_rate); + } + else + { + lv2_log_warning(&self->logger, "[sfizz] Got a sample rate but could not resolve the type of the atom\n"); + if (self->unmap) + lv2_log_warning(&self->logger, + "[sfizz] Atom URI: %s\n", + self->unmap->unmap(self->unmap->handle, opt->type)); + } +} + +static void +sfizz_lv2_get_default_sfz_path(sfizz_plugin_t *self, char *path, size_t size) +{ + snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SFZ_FILE); +} + +static void +sfizz_lv2_get_default_scala_path(sfizz_plugin_t *self, char *path, size_t size) +{ + snprintf(path, size, "%s/%s", self->bundle_path, DEFAULT_SCALA_FILE); +} + +static void +sfizz_lv2_update_timeinfo(sfizz_plugin_t *self, int delay, int updates) +{ + if (updates & SFIZZ_TIMEINFO_POSITION) + sfizz_send_time_position(self->synth, delay, self->bar, self->bar_beat); + if (updates & SFIZZ_TIMEINFO_SIGNATURE) + sfizz_send_time_signature(self->synth, delay, self->beats_per_bar, self->beat_unit); + if (updates & SFIZZ_TIMEINFO_TEMPO) + sfizz_send_bpm_tempo(self->synth, delay, self->bpm_tempo); + if (updates & SFIZZ_TIMEINFO_SPEED) + sfizz_send_playback_state(self->synth, delay, self->speed > 0); +} + +static void +sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args) +{ + (void)delay; + + sfizz_plugin_t *self = (sfizz_plugin_t *)data; + + if (!strcmp(path, "/sw/last/current") && sig) + { + if (sig[0] == 'i') + self->last_keyswitch = args[0].i; + else if (sig[0] == 'N') + self->last_keyswitch = -1; + } + + // transmit to UI as OSC blob + uint8_t *osc_temp = self->osc_temp; + uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args); + if (osc_size > OSC_TEMP_SIZE) + return; + + LV2_Atom_Forge* forge = &self->forge_automate; + bool write_ok = + lv2_atom_forge_frame_time(forge, 0) && + lv2_atom_forge_atom(forge, osc_size, self->sfizz_osc_blob_uri) && + lv2_atom_forge_raw(forge, osc_temp, osc_size); + if (write_ok) + lv2_atom_forge_pad(forge, osc_size); + + (void)write_ok; +} + +static LV2_Handle +instantiate(const LV2_Descriptor *descriptor, + double rate, + const char *bundle_path, + const LV2_Feature *const *features) +{ + UNUSED(descriptor); + LV2_Options_Option *options = NULL; + bool supports_bounded_block_size = false; + bool options_has_block_size = false; + bool supports_fixed_block_size = false; + + // Allocate and initialise instance structure. + sfizz_plugin_t *self = new sfizz_plugin_t; + if (!self) + return NULL; + + strncpy(self->bundle_path, bundle_path, MAX_BUNDLE_PATH_SIZE); + self->bundle_path[MAX_BUNDLE_PATH_SIZE - 1] = '\0'; + + // Set defaults + self->multi_out = !strcmp(descriptor->URI, SFIZZ_MULTI_URI); + self->max_block_size = MAX_BLOCK_SIZE; + self->sample_rate = (float)rate; + self->expect_nominal_block_length = false; + self->sfz_file_path[0] = '\0'; + self->scala_file_path[0] = '\0'; + self->num_voices = DEFAULT_VOICES; + self->oversampling = DEFAULT_OVERSAMPLING; + self->preload_size = DEFAULT_PRELOAD; + self->stretch_tuning = 0.0f; + self->check_modification = false; + self->sample_counter = 0; + + // Initial timing + self->bar = 0; + self->bar_beat = 0; + self->beats_per_bar = 4; + self->beat_unit = 4; + self->bpm_tempo = 120; + self->speed = 1; + + // Get the features from the host and populate the structure + for (const LV2_Feature *const *f = features; *f; f++) + { + const char *uri = (**f).URI; + void *data = (**f).data; + + // lv2_log_note(&self->logger, "Feature URI: %s\n", (**f).URI); + + if (!strcmp(uri, LV2_URID__map)) + self->map = (LV2_URID_Map *)data; + + if (!strcmp(uri, LV2_URID__unmap)) + self->unmap = (LV2_URID_Unmap *)data; + + if (!strcmp(uri, LV2_BUF_SIZE__boundedBlockLength)) + supports_bounded_block_size = true; + + if (!strcmp(uri, LV2_BUF_SIZE__fixedBlockLength)) + supports_fixed_block_size = true; + + if (!strcmp(uri, LV2_OPTIONS__options)) + options = (LV2_Options_Option *)data; + + if (!strcmp(uri, LV2_WORKER__schedule)) + self->worker = (LV2_Worker_Schedule *)data; + + if (!strcmp(uri, LV2_LOG__log)) + self->log = (LV2_Log_Log *)data; + + if (!strcmp(uri, LV2_MIDNAM__update)) + self->midnam = (LV2_Midnam *)data; + } + + // Setup the loggers + lv2_log_logger_init(&self->logger, self->map, self->log); + + // The map feature is required + if (!self->map) + { + lv2_log_error(&self->logger, "Map feature not found, aborting..\n"); + delete self; + return NULL; + } + + // The worker feature is required + if (!self->worker) + { + lv2_log_error(&self->logger, "Worker feature not found, aborting..\n"); + delete self; + return NULL; + } + + // Map the URIs we will need + sfizz_lv2_map_required_uris(self); + + // Initialize the forge + lv2_atom_forge_init(&self->forge_automate, self->map); + lv2_atom_forge_init(&self->forge_secondary, self->map); + + // Check the options for the block size and sample rate parameters + if (options) + { + for (const LV2_Options_Option *opt = options; opt->key || opt->value; ++opt) + { + if (opt->key == self->sample_rate_uri) + { + sfizz_lv2_parse_sample_rate(self, opt); + } + else if (!self->expect_nominal_block_length && opt->key == self->max_block_length_uri) + { + if (opt->type != self->atom_int_uri) + { + lv2_log_warning(&self->logger, "Got a max block size but the type was wrong\n"); + continue; + } + self->max_block_size = *(int *)opt->value; + options_has_block_size = true; + } + else if (opt->key == self->nominal_block_length_uri) + { + if (opt->type != self->atom_int_uri) + { + lv2_log_warning(&self->logger, "Got a nominal block size but the type was wrong\n"); + continue; + } + self->max_block_size = *(int *)opt->value; + self->expect_nominal_block_length = true; + options_has_block_size = true; + } + } + } + else + { + lv2_log_warning(&self->logger, + "No option array was given upon instantiation; will use default values.\n"); + } + + // We need _some_ information on the block size + if (!supports_bounded_block_size && !supports_fixed_block_size && !options_has_block_size) + { + lv2_log_error(&self->logger, + "Bounded block size not supported and options gave no block size, aborting..\n"); + delete self; + return NULL; + } + + self->ccmap = sfizz_lv2_ccmap_create(self->map); + self->cc_current = new float[sfz::config::numCCs](); + + self->synth = sfizz_create_synth(); + self->client = sfizz_create_client(self); + self->synth_mutex = spin_mutex_create(); + sfizz_set_broadcast_callback(self->synth, &sfizz_lv2_receive_message, self); + sfizz_set_receive_callback(self->client, &sfizz_lv2_receive_message); + + self->sfz_blob_mutex = new std::mutex; + + sfizz_lv2_load_file(self, self->sfz_file_path); + sfizz_lv2_load_scala_file(self, self->scala_file_path); + + sfizz_lv2_update_timeinfo(self, 0, ~0); + + self->text_description[0] = 0; + self->resend_description = false; + +#if defined(SFIZZ_LV2_UI) + if (self->multi_out) + self->rms_follower.setNumOutputs(MULTI_OUTPUT_COUNT); + else + self->rms_follower.setNumOutputs(2); +#endif + + return (LV2_Handle)self; +} + +static void +cleanup(LV2_Handle instance) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + delete[] self->sfz_blob_data; + delete self->sfz_blob_mutex; + spin_mutex_destroy(self->synth_mutex); + sfizz_delete_client(self->client); + sfizz_free(self->synth); + delete[] self->cc_current; + sfizz_lv2_ccmap_free(self->ccmap); + delete self; +} + +static void +activate(LV2_Handle instance) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + sfizz_set_samples_per_block(self->synth, self->max_block_size); + sfizz_set_sample_rate(self->synth, self->sample_rate); + self->must_update_midnam.store(1); +#if defined(SFIZZ_LV2_UI) + self->rms_follower.init(self->sample_rate); +#endif +} + +static void +deactivate(LV2_Handle instance) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + sfizz_all_sound_off(self->synth); +} + +static void +sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID verb_uri, LV2_URID urid, const char *path) +{ + LV2_Atom_Forge_Frame frame; + + LV2_Atom_Forge* forge = &self->forge_automate; + bool write_ok = + lv2_atom_forge_frame_time(forge, 0) && + lv2_atom_forge_object(forge, &frame, 0, verb_uri) && + lv2_atom_forge_key(forge, self->patch_property_uri) && + lv2_atom_forge_urid(forge, urid) && + lv2_atom_forge_key(forge, self->patch_value_uri) && + lv2_atom_forge_path(forge, path, (uint32_t)strlen(path)); + + if (write_ok) + lv2_atom_forge_pop(forge, &frame); +} + +static void +sfizz_lv2_send_instrument_description(sfizz_plugin_t *self) +{ + LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge* forge = &self->forge_automate; + + bool write_ok = + lv2_atom_forge_frame_time(forge, 0) && + lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) && + lv2_atom_forge_key(forge, self->patch_property_uri) && + lv2_atom_forge_urid(forge, self->sfizz_description_uri) && + lv2_atom_forge_key(forge, self->patch_value_uri) && + lv2_atom_forge_string(forge, self->text_description, + (uint32_t)strnlen(self->text_description, sizeof(self->text_description))); + + if (write_ok) + lv2_atom_forge_pop(forge, &frame); +} + +static void +sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_URID verb_uri, unsigned cc, float value) +{ + LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); + LV2_Atom_Forge_Frame frame; + + LV2_Atom_Forge* forge = &self->forge_automate; + bool write_ok = + lv2_atom_forge_frame_time(forge, 0) && + lv2_atom_forge_object(forge, &frame, 0, verb_uri) && + lv2_atom_forge_key(forge, self->patch_property_uri) && + lv2_atom_forge_urid(forge, urid) && + lv2_atom_forge_key(forge, self->patch_value_uri) && + lv2_atom_forge_float(forge, value); + + if (write_ok) + lv2_atom_forge_pop(forge, &frame); +} + +#if defined(SFIZZ_LV2_UI) +static void +sfizz_lv2_send_levels(sfizz_plugin_t *self, const float* levels, uint32_t num_levels) +{ + LV2_Atom_Forge* forge = &self->forge_automate; + bool write_ok = lv2_atom_forge_frame_time(forge, 0); + + LV2_Atom_Vector *vector = nullptr; + if (write_ok) { + LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector(forge, sizeof(float), self->atom_float_uri, num_levels, levels); + write_ok = ref; + if (write_ok) + vector = (LV2_Atom_Vector *)lv2_atom_forge_deref(forge, ref); + } + + if (write_ok) + vector->atom.type = self->sfizz_audio_level_uri; + + (void)write_ok; +} +#endif + +static void +sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj) +{ + const LV2_Atom *property = NULL; + lv2_atom_object_get(obj, self->patch_property_uri, &property, 0); + if (!property) + { + lv2_log_error(&self->logger, + "[sfizz] Could not get the property from the patch object, aborting\n"); + return; + } + + if (property->type != self->atom_urid_uri) + { + lv2_log_error(&self->logger, + "[sfizz] Atom type was not a URID, aborting\n"); + return; + } + + const uint32_t key = ((const LV2_Atom_URID *)property)->body; + const LV2_Atom *atom = NULL; + lv2_atom_object_get(obj, self->patch_value_uri, &atom, 0); + if (!atom) + { + lv2_log_error(&self->logger, "[sfizz] Error retrieving the atom, aborting\n"); + if (self->unmap) + lv2_log_warning(&self->logger, + "Atom URI: %s\n", + self->unmap->unmap(self->unmap->handle, key)); + return; + } + + typedef struct + { + LV2_Atom atom; + char body[MAX_PATH_SIZE]; + } sfizz_path_atom_buffer_t; + + int cc = sfizz_lv2_ccmap_unmap(self->ccmap, key); + if (cc != -1) { + if (atom->type == self->atom_float_uri && atom->size == sizeof(float)) { + float value = *(const float *)LV2_ATOM_BODY_CONST(atom); + sfizz_automate_hdcc(self->synth, delay, cc, value); + self->cc_current[cc] = value; + } + } + else if (key == self->sfizz_sfz_file_uri) + { + LV2_Atom_Forge *forge = &self->forge_secondary; + sfizz_path_atom_buffer_t buffer; + lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer)); + const char *body = (const char *)LV2_ATOM_BODY_CONST(atom); + uint32_t size = (uint32_t)strnlen(body, atom->size); + if (lv2_atom_forge_typed_string(forge, self->sfizz_sfz_file_uri, body, size)) + self->worker->schedule_work(self->worker->handle, lv2_atom_total_size(&buffer.atom), &buffer.atom); + self->check_modification = false; + } + else if (key == self->sfizz_scala_file_uri) + { + LV2_Atom_Forge *forge = &self->forge_secondary; + sfizz_path_atom_buffer_t buffer; + lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer)); + const char *body = (const char *)LV2_ATOM_BODY_CONST(atom); + uint32_t size = (uint32_t)strnlen(body, atom->size); + if (lv2_atom_forge_typed_string(forge, self->sfizz_scala_file_uri, body, size)) + self->worker->schedule_work(self->worker->handle, lv2_atom_total_size(&buffer.atom), &buffer.atom); + self->check_modification = false; + } + else + { + lv2_log_warning(&self->logger, "[sfizz] Unknown or unsupported object\n"); + if (self->unmap) + lv2_log_warning(&self->logger, + "Object URI: %s\n", + self->unmap->unmap(self->unmap->handle, key)); + return; + } +} + +static void +sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) +{ + const uint8_t *const msg = (const uint8_t *)(ev + 1); + switch (lv2_midi_message_type(msg)) + { + case LV2_MIDI_MSG_NOTE_ON: + if (msg[2] == 0) + goto noteoff; // 0 velocity note-ons should be forbidden but just in case... + + sfizz_send_note_on(self->synth, + (int)ev->time.frames, + (int)msg[1], + msg[2]); + break; + case LV2_MIDI_MSG_NOTE_OFF: noteoff: + sfizz_send_note_off(self->synth, + (int)ev->time.frames, + (int)msg[1], + msg[2]); + break; + case LV2_MIDI_MSG_CONTROLLER: + { + unsigned cc = msg[1]; + float value = float(msg[2]) * (1.0f / 127.0f); + +// Note(jpc) CC must be mapped by host, not handled here. +// See LV2 midi:binding. +#if defined(SFIZZ_LV2_PSA) + switch (cc) + { + default: + { + sfizz_automate_hdcc(self->synth, + (int)ev->time.frames, + (int)cc, + value); + self->cc_current[cc] = value; + } + break; + case LV2_MIDI_CTL_ALL_NOTES_OFF: + // TODO implemented as all-sound-off + sfizz_all_sound_off(self->synth); + break; + case LV2_MIDI_CTL_ALL_SOUNDS_OFF: + sfizz_all_sound_off(self->synth); + break; + } +#endif + } + break; + case LV2_MIDI_MSG_CHANNEL_PRESSURE: + sfizz_send_channel_aftertouch(self->synth, + (int)ev->time.frames, + msg[1]); + break; + case LV2_MIDI_MSG_NOTE_PRESSURE: + sfizz_send_poly_aftertouch(self->synth, + (int)ev->time.frames, + (int)msg[1], + msg[2]); + break; + case LV2_MIDI_MSG_BENDER: + sfizz_send_pitch_wheel(self->synth, + (int)ev->time.frames, + PITCH_BUILD_AND_CENTER(msg[1], msg[2])); + break; + case LV2_MIDI_MSG_PGM_CHANGE: + sfizz_send_program_change(self->synth, + (int)ev->time.frames, + msg[1]); + break; + default: + break; + } +} + +static void +sfizz_lv2_status_log(sfizz_plugin_t *self) +{ + UNUSED(self); + // lv2_log_note(&self->logger, "[sfizz] Allocated buffers: %d\n", sfizz_get_num_buffers(self->synth)); + // lv2_log_note(&self->logger, "[sfizz] Allocated bytes: %d bytes\n", sfizz_get_num_bytes(self->synth)); + // lv2_log_note(&self->logger, "[sfizz] Active voices: %d\n", sfizz_get_num_active_voices(self->synth)); +} + +static int next_pow_2(int v) +{ + if (v < 1) + return 1; + + // Bit twiddling hack + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static void +sfizz_lv2_check_oversampling(sfizz_plugin_t* self) +{ + int port_value = next_pow_2((int)*self->oversampling_port); + if (port_value == (int)self->oversampling) + return; + + self->oversampling = (sfizz_oversampling_factor_t)port_value; + + LV2_Atom_Int atom; + atom.atom.type = self->sfizz_oversampling_uri; + atom.atom.size = sizeof(int); + atom.body = self->oversampling; + if (self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom *)&atom), + &atom) != LV2_WORKER_SUCCESS) + { + lv2_log_error(&self->logger, "[sfizz] There was an issue changing the oversampling factor\n"); + } +} + +static void +sfizz_lv2_check_preload_size(sfizz_plugin_t* self) +{ + unsigned int preload_size = (int)*self->preload_port; + if (preload_size != self->preload_size) + { + LV2_Atom_Int atom; + atom.atom.type = self->sfizz_preload_size_uri; + atom.atom.size = sizeof(int); + atom.body = preload_size; + if (self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom *)&atom), + &atom) != LV2_WORKER_SUCCESS) + { + lv2_log_error(&self->logger, "[sfizz] There was an issue changing the preload size\n"); + } + self->preload_size = preload_size; + } +} + +static void +sfizz_lv2_check_num_voices(sfizz_plugin_t* self) +{ + int num_voices = (int)*self->polyphony_port; + if (num_voices != self->num_voices) + { + LV2_Atom_Int atom; + atom.atom.type = self->sfizz_num_voices_uri; + atom.atom.size = sizeof(int); + atom.body = num_voices; + if (self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom *)&atom), + &atom) != LV2_WORKER_SUCCESS) + { + lv2_log_error(&self->logger, "[sfizz] There was an issue changing the number of voices\n"); + } + self->num_voices = num_voices; + } +} + +static void +sfizz_lv2_check_freewheeling(sfizz_plugin_t* self) +{ + if (*(self->freewheel_port) > 0) + { + sfizz_enable_freewheeling(self->synth); + } + else + { + sfizz_disable_freewheeling(self->synth); + } +} + +static void +sfizz_lv2_check_stretch_tuning(sfizz_plugin_t* self) +{ + float stretch_tuning = (float)*self->stretch_tuning_port; + if (stretch_tuning != self->stretch_tuning) + { + sfizz_load_stretch_tuning_by_ratio(self->synth, stretch_tuning); + self->stretch_tuning = stretch_tuning; + } +} + +static void +run(LV2_Handle instance, uint32_t sample_count) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + assert(self->control_port && self->automate_port); + + if (!spin_mutex_trylock(self->synth_mutex)) + { + for (int channel = 0; channel < 2; ++channel) + memset(self->output_buffers[channel], 0, sample_count * sizeof(float)); + return; + } + + // Set up dedicated forges to write on their respective ports. + const size_t automate_capacity = self->automate_port->atom.size; + lv2_atom_forge_set_buffer(&self->forge_automate, (uint8_t *)self->automate_port, automate_capacity); + + // Start sequences in the respective output ports. + LV2_Atom_Forge_Frame automate_frame; + if (!lv2_atom_forge_sequence_head(&self->forge_automate, &automate_frame, 0)) + assert(false); + + LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) + { + const int delay = (int)ev->time.frames; + + // If the received atom is an object/patch message + if (ev->body.type == self->atom_object_uri || ev->body.type == self->atom_blank_uri) + { + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; + + if (obj->body.otype == self->patch_set_uri) + { + sfizz_lv2_handle_atom_object(self, delay, obj); + } + else if (obj->body.otype == self->patch_get_uri) + { + const LV2_Atom_URID *property = NULL; + lv2_atom_object_get(obj, self->patch_property_uri, &property, 0); + if (!property) // Send the full state + { + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path); + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path); + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) + sfizz_lv2_send_controller(self, self->sfizz_notify_uri, cc, self->cc_current[cc]); + + sfizz_lv2_send_instrument_description(self); + } + else if (property->body == self->sfizz_sfz_file_uri) + { + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_sfz_file_uri, self->sfz_file_path); + } + else if (property->body == self->sfizz_scala_file_uri) + { + sfizz_lv2_send_file_path(self, self->sfizz_notify_uri, self->sfizz_scala_file_uri, self->scala_file_path); + } + else if (property->body == self->sfizz_description_uri) + { + sfizz_lv2_send_instrument_description(self); + } + else + { + int cc = sfizz_lv2_ccmap_unmap(self->ccmap, property->body); + if (cc != -1) + sfizz_lv2_send_controller(self, self->sfizz_notify_uri, unsigned(cc), self->cc_current[cc]); + } + } + else if (obj->body.otype == self->time_position_uri) + { + const LV2_Atom *bar_atom = NULL; + const LV2_Atom *bar_beat_atom = NULL; + const LV2_Atom *beat_unit_atom = NULL; + const LV2_Atom *beats_per_bar_atom = NULL; + const LV2_Atom *beats_per_minute_atom = NULL; + const LV2_Atom *speed_atom = NULL; + + lv2_atom_object_get( + obj, + self->time_bar_uri, &bar_atom, + self->time_bar_beat_uri, &bar_beat_atom, + self->time_beats_per_bar_uri, &beats_per_bar_atom, + self->time_beats_per_minute_uri, &beats_per_minute_atom, + self->time_beat_unit_uri, &beat_unit_atom, + self->time_speed_uri, &speed_atom, + 0); + + int updates = 0; + + int64_t bar; + double bar_beat; + if (sfizz_atom_extract_integer(self, bar_atom, &bar)) { + self->bar = (int)bar; + updates |= SFIZZ_TIMEINFO_POSITION; + } + if (sfizz_atom_extract_real(self, bar_beat_atom, &bar_beat)) { + self->bar_beat = (float)bar_beat; + updates |= SFIZZ_TIMEINFO_POSITION; + } + + double beats_per_bar; + int64_t beat_unit; + if (sfizz_atom_extract_real(self, beats_per_bar_atom, &beats_per_bar)) { + self->beats_per_bar = (int)beats_per_bar; + updates |= SFIZZ_TIMEINFO_SIGNATURE; + } + if (sfizz_atom_extract_integer(self, beat_unit_atom, &beat_unit)) { + self->beat_unit = (int)beat_unit; + updates |= SFIZZ_TIMEINFO_SIGNATURE; + } + + double tempo; + if (sfizz_atom_extract_real(self, beats_per_minute_atom, &tempo)) { + self->bpm_tempo = (float)tempo; + updates |= SFIZZ_TIMEINFO_TEMPO; + } + + double speed; + if (sfizz_atom_extract_real(self, speed_atom, &speed)) { + self->speed = (float)speed; + updates |= SFIZZ_TIMEINFO_SPEED; + } + + sfizz_lv2_update_timeinfo(self, delay, updates); + } + else + { + lv2_log_warning(&self->logger, "[sfizz] Got an Object atom but it was not supported\n"); + if (self->unmap) + lv2_log_warning(&self->logger, + "Object URI: %s\n", + self->unmap->unmap(self->unmap->handle, obj->body.otype)); + continue; + } + } + else if (ev->body.type == self->midi_event_uri) + { + // Got an atom that is a MIDI event + sfizz_lv2_process_midi_event(self, ev); + } + else if (ev->body.type == self->sfizz_osc_blob_uri) + { + // Got an atom that is a OSC event + const char *path; + const char *sig; + const sfizz_arg_t *args; + uint8_t buffer[1024]; + if (sfizz_extract_message(LV2_ATOM_BODY_CONST(&ev->body), ev->body.size, buffer, sizeof(buffer), &path, &sig, &args) > 0) + sfizz_send_message(self->synth, self->client, (int)ev->time.frames, path, sig, args); + } + } + + + // Check and update parameters if needed + sfizz_lv2_check_freewheeling(self); + sfizz_set_volume(self->synth, *(self->volume_port)); + sfizz_set_scala_root_key(self->synth, *(self->scala_root_key_port)); + sfizz_set_tuning_frequency(self->synth, *(self->tuning_frequency_port)); + sfizz_set_sample_quality(self->synth, SFIZZ_PROCESS_LIVE, (int)(*self->sample_quality_port)); + sfizz_set_oscillator_quality(self->synth, SFIZZ_PROCESS_LIVE, (int)(*self->oscillator_quality_port)); + sfizz_set_sample_quality(self->synth, SFIZZ_PROCESS_FREEWHEELING, (int)(*self->freewheeling_sample_quality_port)); + sfizz_set_oscillator_quality(self->synth, SFIZZ_PROCESS_FREEWHEELING, (int)(*self->freewheeling_oscillator_quality_port)); + sfizz_set_sustain_cancels_release(self->synth, (*self->sustain_cancels_release_port > 0.0f)); + sfizz_lv2_check_stretch_tuning(self); + sfizz_lv2_check_preload_size(self); + sfizz_lv2_check_oversampling(self); + sfizz_lv2_check_num_voices(self); + *(self->active_voices_port) = sfizz_get_num_active_voices(self->synth); + *(self->num_curves_port) = sfizz_get_num_curves(self->synth); + *(self->num_masters_port) = sfizz_get_num_masters(self->synth); + *(self->num_groups_port) = sfizz_get_num_groups(self->synth); + *(self->num_regions_port) = sfizz_get_num_regions(self->synth); + *(self->num_samples_port) = sfizz_get_num_preloaded_samples(self->synth); + if (self->resend_description) { + sfizz_lv2_send_instrument_description(self); + self->resend_description = false; + } + + // Log the buffer usage + self->sample_counter += (int)sample_count; + if (self->sample_counter > LOG_SAMPLE_COUNT && self->check_modification) + { + LV2_Atom atom; + atom.size = 0; +#ifndef NDEBUG + atom.type = self->sfizz_log_status_uri; + if (!(self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom *)&atom), + &atom) == LV2_WORKER_SUCCESS)) + { + lv2_log_error(&self->logger, "[sfizz] There was an issue sending a logging message to the background worker\n"); + } +#endif + atom.type = self->sfizz_check_modification_uri; + if ((self->worker->schedule_work(self->worker->handle, + lv2_atom_total_size((LV2_Atom *)&atom), + &atom) == LV2_WORKER_SUCCESS)) { + self->check_modification = false; + } else { + lv2_log_error(&self->logger, "[sfizz] There was an issue sending a notice to check the modification of the SFZ file to the background worker\n"); + } + self->sample_counter = 0; + } + + // Render the block + if (self->multi_out) + sfizz_render_block(self->synth, self->output_buffers, MULTI_OUTPUT_COUNT, (int)sample_count); + else + sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count); + + // Request OSC updates + sfizz_send_message(self->synth, self->client, 0, "/sw/last/current", "", nullptr); + + if (self->midnam && self->must_update_midnam.exchange(0)) + { + self->midnam->update(self->midnam->handle); + } + + if (self->resync_cc) { + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) + sfizz_lv2_send_controller(self, self->patch_set_uri, cc, self->cc_current[cc]); + + self->resync_cc = false; + } + + spin_mutex_unlock(self->synth_mutex); + +#if defined(SFIZZ_LV2_UI) + if (self->ui_active) + { + if (self->multi_out) { + self->rms_follower.process((const float**)self->output_buffers, + sample_count, MULTI_OUTPUT_COUNT); + float levels[MULTI_OUTPUT_COUNT]; + self->rms_follower.getRMS(levels, MULTI_OUTPUT_COUNT); + sfizz_lv2_send_levels(self, levels, MULTI_OUTPUT_COUNT); + } else { + self->rms_follower.process((const float**)self->output_buffers, + sample_count, 2); + float levels[2]; + self->rms_follower.getRMS(levels, 2); + sfizz_lv2_send_levels(self, levels, 2); + } + } + else + { + self->rms_follower.clear(); + } +#endif + + lv2_atom_forge_pop(&self->forge_automate, &automate_frame); +} + +static uint32_t +lv2_get_options(LV2_Handle instance, LV2_Options_Option *options) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + LV2_DEBUG("[DEBUG] get_options called\n"); + for (LV2_Options_Option *opt = options; opt->key || opt->value; ++opt) + { + if (self->unmap) { + LV2_DEBUG("[DEBUG] Called for an option with key (subject): %s (%s) \n", + self->unmap->unmap(self->unmap->handle, opt->key), + self->unmap->unmap(self->unmap->handle, opt->subject)); + } + + if (opt->key == self->sample_rate_uri) + { + opt->type = self->atom_float_uri; + opt->size = sizeof(float); + opt->value = (void*)&self->sample_rate; + return LV2_OPTIONS_SUCCESS; + } + + if (opt->key == self->max_block_length_uri || opt->key == self->nominal_block_length_uri) + { + opt->type = self->atom_int_uri; + opt->size = sizeof(int); + opt->value = (void*)&self->max_block_size; + return LV2_OPTIONS_SUCCESS; + } + } + return LV2_OPTIONS_ERR_UNKNOWN; +} + +static uint32_t +lv2_set_options(LV2_Handle instance, const LV2_Options_Option *options) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + + // Update the block size and sample rate as needed + for (const LV2_Options_Option *opt = options; opt->key || opt->value; ++opt) + { + if (opt->key == self->sample_rate_uri) + { + sfizz_lv2_parse_sample_rate(self, opt); + spin_mutex_lock(self->synth_mutex); + sfizz_set_sample_rate(self->synth, self->sample_rate); + spin_mutex_unlock(self->synth_mutex); + } + else if (!self->expect_nominal_block_length && opt->key == self->max_block_length_uri) + { + if (opt->type != self->atom_int_uri) + { + lv2_log_warning(&self->logger, "[sfizz] Got a max block size but the type was wrong\n"); + continue; + } + self->max_block_size = *(int *)opt->value; + spin_mutex_lock(self->synth_mutex); + sfizz_set_samples_per_block(self->synth, self->max_block_size); + spin_mutex_unlock(self->synth_mutex); + } + else if (opt->key == self->nominal_block_length_uri) + { + if (opt->type != self->atom_int_uri) + { + lv2_log_warning(&self->logger, "[sfizz] Got a nominal block size but the type was wrong\n"); + continue; + } + self->max_block_size = *(int *)opt->value; + spin_mutex_lock(self->synth_mutex); + sfizz_set_samples_per_block(self->synth, self->max_block_size); + spin_mutex_unlock(self->synth_mutex); + } + } + return LV2_OPTIONS_SUCCESS; +} + +static void +sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char *file_path) +{ + if (file_path != self->sfz_file_path) + strcpy(self->sfz_file_path, file_path); + + lv2_log_note(&self->logger, "[sfizz] File changed to: %s\n", file_path); + + char *unknown_opcodes = sfizz_get_unknown_opcodes(self->synth); + if (unknown_opcodes) + { + lv2_log_note(&self->logger, "[sfizz] Unknown opcodes: %s\n", unknown_opcodes); + free(unknown_opcodes); + } + lv2_log_note(&self->logger, "[sfizz] Number of masters: %d\n", sfizz_get_num_masters(self->synth)); + lv2_log_note(&self->logger, "[sfizz] Number of groups: %d\n", sfizz_get_num_groups(self->synth)); + lv2_log_note(&self->logger, "[sfizz] Number of regions: %d\n", sfizz_get_num_regions(self->synth)); + + self->must_update_midnam.store(1); +} + +static void +sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) +{ + const std::string blob = getDescriptionBlob(self->synth); + + // Update description blob that UI can fetch, thread-safely + uint32_t size = uint32_t(blob.size()); + uint8_t *data = new uint8_t[size]; + memcpy(data, blob.data(), size); + + self->sfz_blob_mutex->lock(); + self->sfz_blob_serial += 1; + const uint8_t *old_data = self->sfz_blob_data; + self->sfz_blob_data = data; + self->sfz_blob_size = size; + self->sfz_blob_mutex->unlock(); + + delete[] old_data; + + // + const InstrumentDescription desc = parseDescriptionBlob(blob); + char *cursor = self->text_description; + char *end = self->text_description + sizeof(self->text_description); + cursor += snprintf(cursor, end - cursor, "CC used:\n"); + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { + // Update the current CCs + self->cc_current[cc] = desc.ccValue[cc]; + self->resync_cc = true; + + if (desc.ccLabel[cc].empty()) + cursor += snprintf(cursor, end - cursor, "- %d\n", cc); + else + cursor += snprintf(cursor, end - cursor, "- %d: %s\n", + cc, desc.ccLabel[cc].c_str()); + } + } +} + +static bool +sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path) +{ + char buf[MAX_PATH_SIZE]; + if (file_path[0] == '\0') + { + sfizz_lv2_get_default_sfz_path(self, buf, MAX_PATH_SIZE); + file_path = buf; + } + + /// + bool status = sfizz_load_or_import_file(self->synth, file_path, nullptr); + sfizz_lv2_update_sfz_info(self); + sfizz_lv2_update_file_info(self, file_path); + return status; +} + +static bool +sfizz_lv2_load_scala_file(sfizz_plugin_t *self, const char *file_path) +{ + char buf[MAX_PATH_SIZE]; + if (file_path[0] == '\0') + { + sfizz_lv2_get_default_scala_path(self, buf, MAX_PATH_SIZE); + file_path = buf; + } + + bool status = sfizz_load_scala_file(self->synth, file_path); + if (file_path != self->scala_file_path) + strcpy(self->scala_file_path, file_path); + return status; +} + +static LV2_State_Status +restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const *features) +{ + UNUSED(flags); + LV2_State_Status status = LV2_STATE_SUCCESS; + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + + LV2_State_Map_Path *map_path = NULL; + LV2_State_Free_Path *free_path = &sfizz_State_Free_Path; + for (const LV2_Feature *const *f = features; *f; ++f) + { + if (!strcmp((*f)->URI, LV2_STATE__mapPath)) + map_path = (LV2_State_Map_Path *)(**f).data; + else if (!strcmp((*f)->URI, LV2_STATE__freePath)) + free_path = (LV2_State_Free_Path *)(**f).data; + } + + // Set default values + self->last_keyswitch = -1; + sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE); + sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE); + + // Fetch back the saved file path, if any + size_t size; + uint32_t type; + uint32_t val_flags; + const void *value; + value = retrieve(handle, self->sfizz_sfz_file_uri, &size, &type, &val_flags); + if (value) + { + const char *path = (const char *)value; + if (map_path) + { + path = map_path->absolute_path(map_path->handle, path); + if (!path) + status = LV2_STATE_ERR_UNKNOWN; + } + + if (path) + { + strncpy(self->sfz_file_path, path, MAX_PATH_SIZE - 1); + self->sfz_file_path[MAX_PATH_SIZE - 1] = '\0'; + + if (map_path) + free_path->free_path(free_path->handle, (char *)path); + } + } + + value = retrieve(handle, self->sfizz_scala_file_uri, &size, &type, &val_flags); + if (value) + { + const char *path = (const char *)value; + if (map_path) + { + path = map_path->absolute_path(map_path->handle, path); + if (!path) + status = LV2_STATE_ERR_UNKNOWN; + } + + if (path) + { + strncpy(self->scala_file_path, path, MAX_PATH_SIZE - 1); + self->scala_file_path[MAX_PATH_SIZE - 1] = '\0'; + + if (map_path) + free_path->free_path(free_path->handle, (char *)path); + } + } + + value = retrieve(handle, self->sfizz_last_keyswitch_uri, &size, &type, &val_flags); + if (value) + { + int last_keyswitch = *(const int*)value; + self->last_keyswitch = last_keyswitch; + } + + // Collect all CC values present in the state + std::unique_ptr[]> cc_values( + new absl::optional[sfz::config::numCCs]); + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); + value = retrieve(handle, urid, &size, &type, &val_flags); + if (value && type == self->atom_float_uri) + cc_values[cc] = *(const float *)value; + } + + // Sync the parameters to the synth + spin_mutex_lock(self->synth_mutex); + + // Load an empty file to remove the default sine, and then the new file. + sfizz_load_string(self->synth, "empty.sfz", ""); + self->check_modification = false; + if (sfizz_lv2_load_file(self, self->sfz_file_path)) + { + lv2_log_note(&self->logger, + "[sfizz] Restoring the file %s\n", self->sfz_file_path); + self->check_modification = true; + } + else + { + lv2_log_error(&self->logger, + "[sfizz] Error while restoring the file %s\n", self->sfz_file_path); + } + + if (sfizz_lv2_load_scala_file(self, self->scala_file_path)) + { + lv2_log_note(&self->logger, + "[sfizz] Restoring the scale %s\n", self->scala_file_path); + } + else + { + lv2_log_error(&self->logger, + "[sfizz] Error while restoring the scale %s\n", self->scala_file_path); + } + + // Override default automation values with these from the state file + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + absl::optional value = cc_values[cc]; + if (value) { + // Set CC in the synth + sfizz_automate_hdcc(self->synth, 0, int(cc), *value); + // Update the current CCs + self->cc_current[cc] = *value; + } + } + + if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) { + sfizz_send_hd_note_on(self->synth, 0, self->last_keyswitch, 1.0f); + sfizz_send_hd_note_off(self->synth, 1, self->last_keyswitch, 0.0f); + } + + spin_mutex_unlock(self->synth_mutex); + + return status; +} + +static LV2_State_Status +save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const *features) +{ + UNUSED(flags); + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + + LV2_State_Map_Path *map_path = NULL; + LV2_State_Free_Path *free_path = &sfizz_State_Free_Path; + for (const LV2_Feature *const *f = features; *f; ++f) + { + if (!strcmp((*f)->URI, LV2_STATE__mapPath)) + map_path = (LV2_State_Map_Path *)(**f).data; + else if (!strcmp((*f)->URI, LV2_STATE__freePath)) + free_path = (LV2_State_Free_Path *)(**f).data; + } + + const char *path; + + // Save the file path + path = self->sfz_file_path; + if (map_path) + { + path = map_path->abstract_path(map_path->handle, path); + if (!path) + return LV2_STATE_ERR_UNKNOWN; + } + store(handle, + self->sfizz_sfz_file_uri, + path, + strlen(path) + 1, + self->atom_path_uri, + LV2_STATE_IS_POD); + if (map_path) + free_path->free_path(free_path->handle, (char *)path); + + // Save the scala file path + path = self->scala_file_path; + if (map_path) + { + path = map_path->abstract_path(map_path->handle, path); + if (!path) + return LV2_STATE_ERR_UNKNOWN; + } + if (!path) + return LV2_STATE_ERR_UNKNOWN; + store(handle, + self->sfizz_scala_file_uri, + path, + strlen(path) + 1, + self->atom_path_uri, + LV2_STATE_IS_POD); + if (map_path) + free_path->free_path(free_path->handle, (char *)path); + + // Save the CCs (used only) + self->sfz_blob_mutex->lock(); + const InstrumentDescription desc = parseDescriptionBlob( + absl::string_view((const char*)self->sfz_blob_data, self->sfz_blob_size)); + self->sfz_blob_mutex->unlock(); + + if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) { + store(handle, + self->sfizz_last_keyswitch_uri, + &self->last_keyswitch, + sizeof(int), + self->atom_int_uri, + LV2_STATE_IS_POD); + } + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { + LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); + store(handle, + urid, + &self->cc_current[cc], + sizeof(float), + self->atom_float_uri, + LV2_STATE_IS_POD); + } + } + + return LV2_STATE_SUCCESS; +} + +static void +sfizz_lv2_respond_with_simple_atom( + LV2_Worker_Respond_Function respond, + LV2_Worker_Respond_Handle handle, + uint32_t type_uri) +{ + LV2_Atom atom; + atom.size = 0; + atom.type = type_uri; + respond(handle, lv2_atom_total_size(&atom), &atom); +} + +// This runs in a lower priority thread +static LV2_Worker_Status +work(LV2_Handle instance, + LV2_Worker_Respond_Function respond, + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void *data) +{ + UNUSED(size); + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + if (!data) { + lv2_log_error(&self->logger, "[sfizz] Ignoring empty data in the worker thread\n"); + return LV2_WORKER_ERR_UNKNOWN; + } + + const LV2_Atom *atom = (const LV2_Atom *)data; + if (atom->type == self->sfizz_sfz_file_uri) + { + const char *sfz_file_path = (const char *)LV2_ATOM_BODY_CONST(atom); + + spin_mutex_lock(self->synth_mutex); + bool success = sfizz_lv2_load_file(self, sfz_file_path); + spin_mutex_unlock(self->synth_mutex); + + if (!success) { + lv2_log_error(&self->logger, + "[sfizz] Error with %s; no file should be loaded\n", sfz_file_path); + } + + // Reactivate checking for file changes + sfizz_lv2_respond_with_simple_atom(respond, handle, self->sfizz_check_modification_uri); + sfizz_lv2_respond_with_simple_atom(respond, handle, self->sfizz_description_uri); + } + else if (atom->type == self->sfizz_scala_file_uri) + { + const char *scala_file_path = (const char *)LV2_ATOM_BODY_CONST(atom); + + spin_mutex_lock(self->synth_mutex); + bool success = sfizz_lv2_load_scala_file(self, scala_file_path); + spin_mutex_unlock(self->synth_mutex); + + if (success) { + lv2_log_note(&self->logger, "[sfizz] Scala file loaded: %s\n", scala_file_path); + } else { + lv2_log_error(&self->logger, + "[sfizz] Error with %s; no new scala file should be loaded\n", scala_file_path); + } + + // Reactivate checking for file changes + sfizz_lv2_respond_with_simple_atom(respond, handle, self->sfizz_check_modification_uri); + } + else if (atom->type == self->sfizz_num_voices_uri) + { + const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom); + + spin_mutex_lock(self->synth_mutex); + sfizz_set_num_voices(self->synth, num_voices); + spin_mutex_unlock(self->synth_mutex); + + if (sfizz_get_num_voices(self->synth) == num_voices) { + lv2_log_note(&self->logger, "[sfizz] Number of voices changed to: %d\n", num_voices); + } else { + lv2_log_error(&self->logger, "[sfizz] Error changing the number of voices\n"); + } + } + else if (atom->type == self->sfizz_preload_size_uri) + { + const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom); + + spin_mutex_lock(self->synth_mutex); + sfizz_set_preload_size(self->synth, preload_size); + spin_mutex_unlock(self->synth_mutex); + + if (sfizz_get_preload_size(self->synth) == preload_size) { + lv2_log_note(&self->logger, "[sfizz] Preload size changed to: %d\n", preload_size); + } else { + lv2_log_error(&self->logger, "[sfizz] Error changing the preload size\n"); + } + } + else if (atom->type == self->sfizz_oversampling_uri) + { + const sfizz_oversampling_factor_t oversampling = + *(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom); + + spin_mutex_lock(self->synth_mutex); + sfizz_set_oversampling_factor(self->synth, oversampling); + spin_mutex_unlock(self->synth_mutex); + + if (sfizz_get_oversampling_factor(self->synth) == oversampling) { + lv2_log_note(&self->logger, "[sfizz] Oversampling changed to: %d\n", oversampling); + } else { + lv2_log_error(&self->logger, "[sfizz] Error changing the oversampling\n"); + } + } + else if (atom->type == self->sfizz_log_status_uri) + { + sfizz_lv2_status_log(self); + } + else if (atom->type == self->sfizz_check_modification_uri) + { + if (sfizz_should_reload_file(self->synth)) + { + lv2_log_note(&self->logger, + "[sfizz] File %s seems to have been updated, reloading\n", + self->sfz_file_path); + + spin_mutex_lock(self->synth_mutex); + bool success = sfizz_lv2_load_file(self, self->sfz_file_path); + spin_mutex_unlock(self->synth_mutex); + + if (!success) { + lv2_log_error(&self->logger, + "[sfizz] Error with %s; no file should be loaded\n", self->sfz_file_path); + } + + sfizz_lv2_respond_with_simple_atom(respond, handle, self->sfizz_description_uri); + } + + if (sfizz_should_reload_scala(self->synth)) + { + lv2_log_note(&self->logger, + "[sfizz] Scala file %s seems to have been updated, reloading\n", + self->scala_file_path); + + spin_mutex_lock(self->synth_mutex); + bool success = sfizz_lv2_load_scala_file(self, self->scala_file_path); + spin_mutex_unlock(self->synth_mutex); + + if (success) { + lv2_log_note(&self->logger, "[sfizz] Scala file loaded: %s\n", self->scala_file_path); + } else { + lv2_log_error(&self->logger, + "[sfizz] Error with %s; no new scala file should be loaded\n", self->scala_file_path); + } + } + + // Reactivate checking for file changes + sfizz_lv2_respond_with_simple_atom(respond, handle, self->sfizz_check_modification_uri); + } + else + { + lv2_log_error(&self->logger, "[sfizz] Got an unknown atom in work\n"); + if (self->unmap) + lv2_log_error(&self->logger, + "URI: %s\n", + self->unmap->unmap(self->unmap->handle, atom->type)); + return LV2_WORKER_ERR_UNKNOWN; + } + return LV2_WORKER_SUCCESS; +} + +// This runs in the audio thread +static LV2_Worker_Status +work_response(LV2_Handle instance, + uint32_t size, + const void *data) +{ + UNUSED(size); + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + + if (!data) + return LV2_WORKER_ERR_UNKNOWN; + + const LV2_Atom *atom = (const LV2_Atom *)data; + if (atom->type == self->sfizz_check_modification_uri) { + self->check_modification = true; // check changes + } else if (atom->type == self->sfizz_description_uri) { + self->resend_description = true; + } else { + lv2_log_error(&self->logger, "[sfizz] Got an unexpected atom in work response\n"); + if (self->unmap) + lv2_log_error(&self->logger, + "URI: %s\n", + self->unmap->unmap(self->unmap->handle, atom->type)); + return LV2_WORKER_ERR_UNKNOWN; + } + + return LV2_WORKER_SUCCESS; +} + +static char * +midnam_model(LV2_Handle instance) +{ + char *model = (char *)malloc(64); + if (!model) + return NULL; + + sprintf(model, "Sfizz LV2:%p", instance); + return model; +} + +static char * +midnam_export(LV2_Handle instance) +{ + sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + + char *model = midnam_model(instance); + if (!model) + return NULL; + + char *xml = sfizz_export_midnam(self->synth, model); + free(model); + return xml; +} + +static void +midnam_free(char *string) +{ + sfizz_free_memory(string); +} + +static const void * +extension_data(const char *uri) +{ + static const LV2_Options_Interface options = {lv2_get_options, lv2_set_options}; + static const LV2_State_Interface state = {save, restore}; + static const LV2_Worker_Interface worker = {work, work_response, NULL}; + static const LV2_Midnam_Interface midnam = {midnam_export, midnam_model, midnam_free}; + + // Advertise the extensions we support + if (!strcmp(uri, LV2_OPTIONS__interface)) + return &options; + else if (!strcmp(uri, LV2_STATE__interface)) + return &state; + else if (!strcmp(uri, LV2_WORKER__interface)) + return &worker; + else if (!strcmp(uri, LV2_MIDNAM__interface)) + return &midnam; + + return NULL; +} + +static const LV2_Descriptor descriptor = { + SFIZZ_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data}; + +static const LV2_Descriptor descriptor_multi = { + SFIZZ_MULTI_URI, + instantiate, + connect_port, + activate, + run, + deactivate, + cleanup, + extension_data}; + +LV2_SYMBOL_EXPORT +const LV2_Descriptor * +lv2_descriptor(uint32_t index) +{ + switch (index) + { + case 0: + return &descriptor; + case 1: + return &descriptor_multi; + default: + return NULL; + } +} diff --git a/plugins/lv2/sfizz.ttl.in b/plugins/lv2/sfizz.ttl.in new file mode 100644 index 0000000..e4c6d8b --- /dev/null +++ b/plugins/lv2/sfizz.ttl.in @@ -0,0 +1,917 @@ +@prefix atom: . +@prefix bufsize: . +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix midi: . +@prefix opts: . +@prefix param: . +@prefix patch: . +@prefix pg: . +@prefix pprop: . +@prefix rdf: . +@prefix rdfs: . +@prefix rsz: . +@prefix state: . +@prefix time: . +@prefix ui: . +@prefix units: . +@prefix urid: . +@prefix work: . +@prefix midnam: . + +midnam:interface a lv2:ExtensionData . +midnam:update a lv2:Feature . + +<@LV2PLUGIN_URI@#stereo_output> + a pg:StereoGroup, pg:OutputGroup ; + lv2:symbol "stereo_output" ; + lv2:name "Stereo output", + "Sortie stéréo"@fr , + "Uscita stereo"@it . + +<@LV2PLUGIN_URI@#config> + a pg:Group ; + lv2:symbol "config" ; + lv2:name "Configuration", + "Configuration"@fr , + "Impostazioni"@it . + +<@LV2PLUGIN_URI@#tuning> + a pg:Group ; + lv2:symbol "tuning" ; + lv2:name "Tuning", + "Accordage"@fr , + "Accordatura"@it . + +<@LV2PLUGIN_URI@#status> + a pg:Group ; + lv2:symbol "status" ; + lv2:name "status", + "Statut"@fr . + +<@LV2PLUGIN_URI@:sfzfile> + a lv2:Parameter ; + rdfs:label "SFZ file", + "Fichier SFZ"@fr , + "File SFZ"@it ; + rdfs:range atom:Path . + +<@LV2PLUGIN_URI@:tuningfile> + a lv2:Parameter ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + rdfs:label "Scala file", + "Fichier Scala"@fr , + "File Scala"@it ; + rdfs:range atom:Path . + +<@LV2PLUGIN_URI@:description> + a lv2:Parameter ; + rdfs:label "Instrument description" ; + rdfs:range atom:String . + +<@LV2PLUGIN_URI@> + a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ; + + doap:name "@LV2PLUGIN_NAME@" ; + doap:license ; + doap:maintainer [ + foaf:name "@LV2PLUGIN_AUTHOR@" ; + foaf:homepage <@LV2PLUGIN_URI@> ; + foaf:mbox ; + ] ; + rdfs:comment "@LV2PLUGIN_COMMENT@", + "Échantillonneur SFZ"@fr , + "Campionatore SFZ"@it ; + + lv2:minorVersion @LV2PLUGIN_VERSION_MINOR@ ; + lv2:microVersion @LV2PLUGIN_VERSION_MICRO@ ; + + lv2:requiredFeature urid:map, bufsize:boundedBlockLength, work:schedule ; + lv2:optionalFeature lv2:hardRTCapable, opts:options, state:mapPath, state:freePath ; + lv2:extensionData opts:interface, state:interface, work:interface ; + + lv2:optionalFeature midnam:update ; + lv2:extensionData midnam:interface ; + + opts:supportedOption param:sampleRate ; + opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ; + + @LV2PLUGIN_IF_ENABLE_UI@ui:ui <@LV2PLUGIN_URI@#ui> ; + + patch:writable <@LV2PLUGIN_URI@:sfzfile> , + <@LV2PLUGIN_URI@:tuningfile> ; + patch:readable <@LV2PLUGIN_URI@:description> ; + + pg:mainOutput <@LV2PLUGIN_URI@#stereo_output> ; + + lv2:port [ + a lv2:InputPort, atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob>, <@LV2PLUGIN_URI@:Notify> ; + lv2:designation lv2:control ; + lv2:index 0 ; + lv2:symbol "control" ; + lv2:name "Control", + "Contrôle"@fr ; + rsz:minimumSize 524288 ; + ] , [ + a lv2:OutputPort, atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ; + lv2:designation lv2:control ; + lv2:index 1 ; + lv2:symbol "automate" ; + lv2:name "Automate", + "Automatisation"@fr ; + rsz:minimumSize 524288 ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 2 ; + lv2:symbol "out_left" ; + lv2:name "Left Output", + "Sortie gauche"@fr , + "Uscita Sinistra"@it ; + pg:group <@LV2PLUGIN_URI@#stereo_output> ; + lv2:designation pg:left + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 3 ; + lv2:symbol "out_right" ; + lv2:name "Right Output", + "Sortie droite"@fr , + "Uscita Destra"@it ; + pg:group <@LV2PLUGIN_URI@#stereo_output> ; + lv2:designation pg:right + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 4 ; + lv2:symbol "volume" ; + lv2:name "Volume" ; + lv2:default 0.0 ; + lv2:minimum -80.0 ; + lv2:maximum 6.0 ; + units:unit units:db + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 5 ; + lv2:symbol "num_voices" ; + lv2:name "Polyphony", + "Polyphonie"@fr , + "Polifonia"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 64 ; + lv2:minimum 8 ; + lv2:maximum 256 ; + lv2:scalePoint [ rdfs:label "8 voices", + "8 voix"@fr , + "8 Voci"@it; + rdf:value 8 + ] ; + lv2:scalePoint [ rdfs:label "16 voices", + "16 voix"@fr , + "16 Voci"@it; + rdf:value 16 + ] ; + lv2:scalePoint [ rdfs:label "32 voices", + "32 voix"@fr , + "32 Voci"@it; + rdf:value 32 + ] ; + lv2:scalePoint [ rdfs:label "64 voices", + "64 voix"@fr , + "64 Voci"@it; + rdf:value 64 + ] ; + lv2:scalePoint [ rdfs:label "128 voices", + "128 voix"@fr , + "128 Voci"@it; + rdf:value 128 + ] ; + lv2:scalePoint [ rdfs:label "256 voices", + "256 voix"@fr , + "256 Voci"@it; + rdf:value 256 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 6 ; + lv2:symbol "oversampling" ; + lv2:name "Oversampling factor", + "Facteur de suréchantillonnage"@fr , + "Fattore Sovracampionamento"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 1 ; + lv2:minimum 1 ; + lv2:maximum 8 ; + lv2:scalePoint [ rdfs:label "x1 oversampling", + "suréchantillonnage x1"@fr , + "x1 Sovracampionamento"@it; + rdf:value 1 + ] ; + lv2:scalePoint [ rdfs:label "x2 oversampling", + "suréchantillonnage x2"@fr , + "x2 Sovracampionamento"@it; + rdf:value 2 + ] ; + lv2:scalePoint [ rdfs:label "x4 oversampling", + "suréchantillonnage x4"@fr , + "x4 Sovracampionamento"@it; + rdf:value 4 + ] ; + lv2:scalePoint [ rdfs:label "x8 oversampling", + "suréchantillonnage x8"@fr , + "x8 Sovracampionamento"@it; + rdf:value 8 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 7 ; + lv2:symbol "preload_size" ; + lv2:name "Preload size", + "Taille préchargée"@fr , + "Grandezza Precaricamento"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 8192 ; + lv2:minimum 1024 ; + lv2:maximum 65536 ; + lv2:scalePoint [ rdfs:label "4 KB", + "4 Ko"@fr; + rdf:value 1024 + ] ; + lv2:scalePoint [ rdfs:label "8 KB", + "8 Ko"@fr; + rdf:value 2048 + ] ; + lv2:scalePoint [ rdfs:label "16 KB", + "16 Ko"@fr; + rdf:value 4096 + ] ; + lv2:scalePoint [ rdfs:label "32 KB", + "32 Ko"@fr; + rdf:value 8192 + ] ; + lv2:scalePoint [ rdfs:label "64 KB", + "64 Ko"@fr; + rdf:value 16384 + ] ; + lv2:scalePoint [ rdfs:label "128 KB", + "128 Ko"@fr; + rdf:value 32768 + ] ; + lv2:scalePoint [ rdfs:label "256 KB", + "256 Ko"@fr; + rdf:value 65536 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 8 ; + lv2:symbol "freewheeling" ; + lv2:name "Freewheeling", + "En roue libre (freewheeling)"@fr , + "A Ruota Libera"@it ; + lv2:designation lv2:freeWheeling ; + lv2:portProperty lv2:toggled ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 9 ; + lv2:symbol "scala_root_key" ; + lv2:name "Scala root key", + "Tonalité de base Scala"@fr , + "Tonalità di base Scala"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:portProperty lv2:integer ; + lv2:default 60 ; + lv2:minimum 0 ; + lv2:maximum 127 ; + units:unit units:midiNote + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 10 ; + lv2:symbol "tuning_frequency" ; + lv2:name "Tuning frequency", + "Fréquence d'accordage"@fr , + "Frequenza di accordatura"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:default 440.0 ; + lv2:minimum 300.0 ; + lv2:maximum 500.0 ; + units:unit units:hz + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 11 ; + lv2:symbol "stretched_tuning" ; + lv2:name "Stretched tuning", + "Accordage étiré"@fr , + "Tensione di accordatura"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + units:unit units:coef + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 12 ; + lv2:symbol "sample_quality" ; + lv2:name "Sample quality", + "Qualité des échantillons"@fr , + "Qualità del campione"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 2.0 ; + lv2:minimum 0.0 ; + lv2:maximum 10.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 13 ; + lv2:symbol "oscillator_quality" ; + lv2:name "Oscillator quality", + "Qualité des oscillateurs"@fr , + "Qualità dell'oscillatore"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 1.0 ; + lv2:minimum 0.0 ; + lv2:maximum 3.0 + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 14 ; + lv2:symbol "active_voices" ; + lv2:name "Active voices", + "Voix utilisées"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 256 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 15 ; + lv2:symbol "num_curves" ; + lv2:name "Number of curves", + "Nombre de courbes"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 16 ; + lv2:symbol "num_masters" ; + lv2:name "Number of masters", + "Nombre de maîtres"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 17 ; + lv2:symbol "num_groups" ; + lv2:name "Number of groups", + "Nombre de groupes"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 18 ; + lv2:symbol "num_regions" ; + lv2:name "Number of regions", + "Nombre de régions"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 19 ; + lv2:symbol "num_samples" ; + lv2:name "Number of samples", + "Nombre d'échantillons"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 20 ; + lv2:symbol "freewheeling_sample_quality" ; + lv2:name "Freewheeling Sample quality", + "Qualité des échantillons en roue libre"@fr; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 10.0 ; + lv2:minimum 0.0 ; + lv2:maximum 10.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 21 ; + lv2:symbol "freewheeling_oscillator_quality" ; + lv2:name "Freewheeling Oscillator quality", + "Qualité des oscillateurs en roue libre"@fr ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 3.0 ; + lv2:minimum 0.0 ; + lv2:maximum 3.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 22 ; + lv2:symbol "sustain_cancels_release" ; + lv2:name "Sustain cancels release"; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty lv2:toggled ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + ] ; + + rdfs:seeAlso . + +<@LV2PLUGIN_URI@-multi> + a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ; + + doap:name "@LV2PLUGIN_NAME@-multi" ; + doap:license ; + doap:maintainer [ + foaf:name "@LV2PLUGIN_AUTHOR@" ; + foaf:homepage <@LV2PLUGIN_URI@> ; + foaf:mbox ; + ] ; + rdfs:comment "@LV2PLUGIN_COMMENT@", + "Échantillonneur SFZ"@fr , + "Campionatore SFZ"@it ; + + lv2:minorVersion @LV2PLUGIN_VERSION_MINOR@ ; + lv2:microVersion @LV2PLUGIN_VERSION_MICRO@ ; + + lv2:requiredFeature urid:map, bufsize:boundedBlockLength, work:schedule ; + lv2:optionalFeature lv2:hardRTCapable, opts:options, state:mapPath, state:freePath ; + lv2:extensionData opts:interface, state:interface, work:interface ; + + lv2:optionalFeature midnam:update ; + lv2:extensionData midnam:interface ; + + opts:supportedOption param:sampleRate ; + opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ; + + @LV2PLUGIN_IF_ENABLE_UI@ui:ui <@LV2PLUGIN_URI@#ui> ; + + patch:writable <@LV2PLUGIN_URI@:sfzfile> , + <@LV2PLUGIN_URI@:tuningfile> ; + patch:readable <@LV2PLUGIN_URI@:description> ; + + pg:mainOutput <@LV2PLUGIN_URI@#stereo_output> ; + + lv2:port [ + a lv2:InputPort, atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob>, <@LV2PLUGIN_URI@:Notify> ; + lv2:designation lv2:control ; + lv2:index 0 ; + lv2:symbol "control" ; + lv2:name "Control", + "Contrôle"@fr ; + rsz:minimumSize 524288 ; + ] , [ + a lv2:OutputPort, atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ; + lv2:designation lv2:control ; + lv2:index 1 ; + lv2:symbol "automate" ; + lv2:name "Automate", + "Automatisation"@fr ; + rsz:minimumSize 524288 ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 2 ; + lv2:symbol "out1L" ; + lv2:name "Output 1 Left", + "Sortie gauche 1"@fr , + "Uscita Sinistra 1"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 3 ; + lv2:symbol "out1R" ; + lv2:name "Output 1 Right", + "Sortie droite 1"@fr , + "Uscita Destra 1"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 4 ; + lv2:symbol "out2L" ; + lv2:name "Output 2 Left", + "Sortie gauche 2"@fr , + "Uscita Sinistra 2"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 5 ; + lv2:symbol "out2R" ; + lv2:name "Output 2 Right", + "Sortie droite 2"@fr , + "Uscita Destra 2"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 6 ; + lv2:symbol "out3L" ; + lv2:name "Output 3 Left", + "Sortie gauche 3"@fr , + "Uscita Sinistra 3"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 7 ; + lv2:symbol "out3R" ; + lv2:name "Output 3 Right", + "Sortie droite 3"@fr , + "Uscita Destra 3"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 8 ; + lv2:symbol "out4L" ; + lv2:name "Output 4 Left", + "Sortie gauche 4"@fr , + "Uscita Sinistra 4"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 9 ; + lv2:symbol "out4R" ; + lv2:name "Output 4 Right", + "Sortie droite 4"@fr , + "Uscita Destra 4"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 10 ; + lv2:symbol "out5L" ; + lv2:name "Output 5 Left", + "Sortie gauche 5"@fr , + "Uscita Sinistra 5"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 11 ; + lv2:symbol "out5R" ; + lv2:name "Output 5 Right", + "Sortie droite 5"@fr , + "Uscita Destra 5"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 12 ; + lv2:symbol "out6L" ; + lv2:name "Output 6 Left", + "Sortie gauche 6"@fr , + "Uscita Sinistra 6"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 13 ; + lv2:symbol "out6R" ; + lv2:name "Output 6 Right", + "Sortie droite 6"@fr , + "Uscita Destra 6"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 14 ; + lv2:symbol "out7L" ; + lv2:name "Output 7 Left", + "Sortie gauche 7"@fr , + "Uscita Sinistra 7"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 15 ; + lv2:symbol "out7R" ; + lv2:name "Output 7 Right", + "Sortie droite 7"@fr , + "Uscita Destra 7"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 16 ; + lv2:symbol "out8L" ; + lv2:name "Output 8 Left", + "Sortie gauche 8"@fr , + "Uscita Sinistra 8"@it ; + ] , [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 17 ; + lv2:symbol "out8R" ; + lv2:name "Output 8 Right", + "Sortie droite 8"@fr , + "Uscita Destra 8"@it ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 18 ; + lv2:symbol "volume" ; + lv2:name "Volume" ; + lv2:default 0.0 ; + lv2:minimum -80.0 ; + lv2:maximum 6.0 ; + units:unit units:db + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 19 ; + lv2:symbol "num_voices" ; + lv2:name "Polyphony", + "Polyphonie"@fr , + "Polifonia"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 64 ; + lv2:minimum 8 ; + lv2:maximum 256 ; + lv2:scalePoint [ rdfs:label "8 voices", + "8 voix"@fr , + "8 Voci"@it; + rdf:value 8 + ] ; + lv2:scalePoint [ rdfs:label "16 voices", + "16 voix"@fr , + "16 Voci"@it; + rdf:value 16 + ] ; + lv2:scalePoint [ rdfs:label "32 voices", + "32 voix"@fr , + "32 Voci"@it; + rdf:value 32 + ] ; + lv2:scalePoint [ rdfs:label "64 voices", + "64 voix"@fr , + "64 Voci"@it; + rdf:value 64 + ] ; + lv2:scalePoint [ rdfs:label "128 voices", + "128 voix"@fr , + "128 Voci"@it; + rdf:value 128 + ] ; + lv2:scalePoint [ rdfs:label "256 voices", + "256 voix"@fr , + "256 Voci"@it; + rdf:value 256 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 20 ; + lv2:symbol "oversampling" ; + lv2:name "Oversampling factor", + "Facteur de suréchantillonnage"@fr , + "Fattore Sovracampionamento"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 1 ; + lv2:minimum 1 ; + lv2:maximum 8 ; + lv2:scalePoint [ rdfs:label "x1 oversampling", + "suréchantillonnage x1"@fr , + "x1 Sovracampionamento"@it; + rdf:value 1 + ] ; + lv2:scalePoint [ rdfs:label "x2 oversampling", + "suréchantillonnage x2"@fr , + "x2 Sovracampionamento"@it; + rdf:value 2 + ] ; + lv2:scalePoint [ rdfs:label "x4 oversampling", + "suréchantillonnage x4"@fr , + "x4 Sovracampionamento"@it; + rdf:value 4 + ] ; + lv2:scalePoint [ rdfs:label "x8 oversampling", + "suréchantillonnage x8"@fr , + "x8 Sovracampionamento"@it; + rdf:value 8 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 21 ; + lv2:symbol "preload_size" ; + lv2:name "Preload size", + "Taille préchargée"@fr , + "Grandezza Precaricamento"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty pprop:notAutomatic ; + lv2:portProperty pprop:expensive ; + lv2:portProperty lv2:integer ; + lv2:portProperty lv2:enumeration ; + lv2:default 8192 ; + lv2:minimum 1024 ; + lv2:maximum 65536 ; + lv2:scalePoint [ rdfs:label "4 KB", + "4 Ko"@fr; + rdf:value 1024 + ] ; + lv2:scalePoint [ rdfs:label "8 KB", + "8 Ko"@fr; + rdf:value 2048 + ] ; + lv2:scalePoint [ rdfs:label "16 KB", + "16 Ko"@fr; + rdf:value 4096 + ] ; + lv2:scalePoint [ rdfs:label "32 KB", + "32 Ko"@fr; + rdf:value 8192 + ] ; + lv2:scalePoint [ rdfs:label "64 KB", + "64 Ko"@fr; + rdf:value 16384 + ] ; + lv2:scalePoint [ rdfs:label "128 KB", + "128 Ko"@fr; + rdf:value 32768 + ] ; + lv2:scalePoint [ rdfs:label "256 KB", + "256 Ko"@fr; + rdf:value 65536 + ] ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 22 ; + lv2:symbol "freewheeling" ; + lv2:name "Freewheeling", + "En roue libre (freewheeling)"@fr , + "A Ruota Libera"@it ; + lv2:designation lv2:freeWheeling ; + lv2:portProperty lv2:toggled ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 23 ; + lv2:symbol "scala_root_key" ; + lv2:name "Scala root key", + "Tonalité de base Scala"@fr , + "Tonalità di base Scala"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:portProperty lv2:integer ; + lv2:default 60 ; + lv2:minimum 0 ; + lv2:maximum 127 ; + units:unit units:midiNote + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 24 ; + lv2:symbol "tuning_frequency" ; + lv2:name "Tuning frequency", + "Fréquence d'accordage"@fr , + "Frequenza di accordatura"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:default 440.0 ; + lv2:minimum 300.0 ; + lv2:maximum 500.0 ; + units:unit units:hz + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 25 ; + lv2:symbol "stretched_tuning" ; + lv2:name "Stretched tuning", + "Accordage étiré"@fr , + "Tensione di accordatura"@it ; + pg:group <@LV2PLUGIN_URI@#tuning> ; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + units:unit units:coef + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 26 ; + lv2:symbol "sample_quality" ; + lv2:name "Sample quality", + "Qualité des échantillons"@fr , + "Qualità del campione"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 2.0 ; + lv2:minimum 0.0 ; + lv2:maximum 10.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 27 ; + lv2:symbol "oscillator_quality" ; + lv2:name "Oscillator quality", + "Qualité des oscillateurs"@fr , + "Qualità dell'oscillatore"@it ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 1.0 ; + lv2:minimum 0.0 ; + lv2:maximum 3.0 + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 28 ; + lv2:symbol "active_voices" ; + lv2:name "Active voices", + "Voix utilisées"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 256 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 29 ; + lv2:symbol "num_curves" ; + lv2:name "Number of curves", + "Nombre de courbes"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 30 ; + lv2:symbol "num_masters" ; + lv2:name "Number of masters", + "Nombre de maîtres"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 31 ; + lv2:symbol "num_groups" ; + lv2:name "Number of groups", + "Nombre de groupes"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 32 ; + lv2:symbol "num_regions" ; + lv2:name "Number of regions", + "Nombre de régions"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:index 33 ; + lv2:symbol "num_samples" ; + lv2:name "Number of samples", + "Nombre d'échantillons"@fr ; + pg:group <@LV2PLUGIN_URI@#status> ; + lv2:portProperty lv2:integer ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 65535 ; + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 34 ; + lv2:symbol "freewheeling_sample_quality" ; + lv2:name "Freewheeling Sample quality", + "Qualité des échantillons en roue libre"@fr; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 10.0 ; + lv2:minimum 0.0 ; + lv2:maximum 10.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 35 ; + lv2:symbol "freewheeling_oscillator_quality" ; + lv2:name "Freewheeling Oscillator quality", + "Qualité des oscillateurs en roue libre"@fr ; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:default 3.0 ; + lv2:minimum 0.0 ; + lv2:maximum 3.0 + ] , [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 36 ; + lv2:symbol "sustain_cancels_release" ; + lv2:name "Sustain cancels release"; + pg:group <@LV2PLUGIN_URI@#config> ; + lv2:portProperty lv2:toggled ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + ] ; + + rdfs:seeAlso . diff --git a/plugins/lv2/sfizz_lv2.h b/plugins/lv2/sfizz_lv2.h new file mode 100644 index 0000000..ab3dc94 --- /dev/null +++ b/plugins/lv2/sfizz_lv2.h @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define MAX_PATH_SIZE 1024 +#define ATOM_TEMP_SIZE 8192 +#define OSC_TEMP_SIZE 8192 +#define MULTI_OUTPUT_COUNT 16 + +#define SFIZZ_URI "http://sfztools.github.io/sfizz" +#define SFIZZ_MULTI_URI SFIZZ_URI "-multi" +#define SFIZZ_UI_URI "http://sfztools.github.io/sfizz#ui" +#define SFIZZ_PREFIX SFIZZ_URI "#" +#define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile" +#define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile" +#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices" +#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size" +#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling" +#define SFIZZ__lastKeyswitch SFIZZ_URI ":" "last_keyswitch" +#define SFIZZ__description SFIZZ_URI ":" "description" +// These ones are just for the worker +#define SFIZZ__logStatus SFIZZ_URI ":" "log_status" +#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" +// OSC atoms +#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob" +#define SFIZZ__Notify SFIZZ_URI ":" "Notify" +// Level atoms +#define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel" + +enum +{ + SFIZZ_CONTROL = 0, + SFIZZ_AUTOMATE = 1, + SFIZZ_LEFT = 2, + SFIZZ_RIGHT = 3, + SFIZZ_VOLUME = 4, + SFIZZ_POLYPHONY = 5, + SFIZZ_OVERSAMPLING = 6, + SFIZZ_PRELOAD = 7, + SFIZZ_FREEWHEELING = 8, + SFIZZ_SCALA_ROOT_KEY = 9, + SFIZZ_TUNING_FREQUENCY = 10, + SFIZZ_STRETCH_TUNING = 11, + SFIZZ_SAMPLE_QUALITY = 12, + SFIZZ_OSCILLATOR_QUALITY = 13, + SFIZZ_ACTIVE_VOICES = 14, + SFIZZ_NUM_CURVES = 15, + SFIZZ_NUM_MASTERS = 16, + SFIZZ_NUM_GROUPS = 17, + SFIZZ_NUM_REGIONS = 18, + SFIZZ_NUM_SAMPLES = 19, + SFIZZ_FREEWHEELING_SAMPLE_QUALITY = 20, + SFIZZ_FREEWHEELING_OSCILLATOR_QUALITY = 21, + SFIZZ_SUSTAIN_CANCELS_RELEASE = 22, +}; + +enum +{ + SFIZZ_MULTI_CONTROL = 0, + SFIZZ_MULTI_AUTOMATE = 1, + SFIZZ_MULTI_OUT1L = 2, + SFIZZ_MULTI_OUT1R = 3, + SFIZZ_MULTI_OUT2L = 4, + SFIZZ_MULTI_OUT2R = 5, + SFIZZ_MULTI_OUT3L = 6, + SFIZZ_MULTI_OUT3R = 7, + SFIZZ_MULTI_OUT4L = 8, + SFIZZ_MULTI_OUT4R = 9, + SFIZZ_MULTI_OUT5L = 10, + SFIZZ_MULTI_OUT5R = 11, + SFIZZ_MULTI_OUT6L = 12, + SFIZZ_MULTI_OUT6R = 13, + SFIZZ_MULTI_OUT7L = 14, + SFIZZ_MULTI_OUT7R = 15, + SFIZZ_MULTI_OUT8L = 16, + SFIZZ_MULTI_OUT8R = 17, + SFIZZ_MULTI_VOLUME = 18, + SFIZZ_MULTI_POLYPHONY = 19, + SFIZZ_MULTI_OVERSAMPLING = 20, + SFIZZ_MULTI_PRELOAD = 21, + SFIZZ_MULTI_FREEWHEELING = 22, + SFIZZ_MULTI_SCALA_ROOT_KEY = 23, + SFIZZ_MULTI_TUNING_FREQUENCY = 24, + SFIZZ_MULTI_STRETCH_TUNING = 25, + SFIZZ_MULTI_SAMPLE_QUALITY = 26, + SFIZZ_MULTI_OSCILLATOR_QUALITY = 27, + SFIZZ_MULTI_ACTIVE_VOICES = 28, + SFIZZ_MULTI_NUM_CURVES = 29, + SFIZZ_MULTI_NUM_MASTERS = 30, + SFIZZ_MULTI_NUM_GROUPS = 31, + SFIZZ_MULTI_NUM_REGIONS = 32, + SFIZZ_MULTI_NUM_SAMPLES = 33, + SFIZZ_MULTI_FREEWHEELING_SAMPLE_QUALITY = 34, + SFIZZ_MULTI_FREEWHEELING_OSCILLATOR_QUALITY = 35, + SFIZZ_MULTI_SUSTAIN_CANCELS_RELEASE = 36, +}; + +// For use with instance-access +struct sfizz_plugin_t; + +/** + * @brief Fetch a copy of the current description, if more recent than what the + * version we already have. + * + * @param self The LV2 plugin + * @param serial The optional serial number to compare against + * @param desc The memory zone which receives the copy + * @param sizep The memory zone which receives the size + * @param serialp The memory zone which receives the new serial + * @return if serial is not null and its value isn't older, false + * otherwise, true, and the pointer returned in descp must be freed with delete[] after use + */ +bool sfizz_lv2_fetch_description( + sfizz_plugin_t *self, const int *serial, + uint8_t **descp, uint32_t *sizep, int *serialp); + +/** + * @brief Returns the number of outputs of the plugin + * + * @param self + * @return int + */ +int sfizz_lv2_get_num_outputs(sfizz_plugin_t *self); + +#if defined(SFIZZ_LV2_UI) +void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active); +#endif + +// Mapping URID to CC and vice-versa +struct sfizz_lv2_ccmap; +sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map); +void sfizz_lv2_ccmap_free(sfizz_lv2_ccmap *ccmap); +LV2_URID sfizz_lv2_ccmap_map(const sfizz_lv2_ccmap *ccmap, int cc); +int sfizz_lv2_ccmap_unmap(const sfizz_lv2_ccmap *ccmap, LV2_URID urid); + +struct sfizz_lv2_ccmap_delete { + void operator()(sfizz_lv2_ccmap* ccmap) const noexcept { sfizz_lv2_ccmap_free(ccmap); } +}; diff --git a/plugins/lv2/sfizz_lv2_common.cpp b/plugins/lv2/sfizz_lv2_common.cpp new file mode 100644 index 0000000..bd6a38e --- /dev/null +++ b/plugins/lv2/sfizz_lv2_common.cpp @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "sfizz_lv2.h" +#include "sfizz_lv2_plugin.h" +#include "sfizz/Config.h" + +bool sfizz_lv2_fetch_description( + sfizz_plugin_t *self, const int *serial, + uint8_t **descp, uint32_t *sizep, int *serialp) +{ + // must be thread-safe + + if (serial && self->sfz_blob_serial == *serial) + return false; + + self->sfz_blob_mutex->lock(); + uint32_t size = self->sfz_blob_size; + uint8_t *data = new uint8_t[size]; + int new_serial = self->sfz_blob_serial; + memcpy(data, self->sfz_blob_data, size); + self->sfz_blob_mutex->unlock(); + + *descp = data; + *sizep = size; + *serialp = new_serial; + + return true; +} + +int sfizz_lv2_get_num_outputs(sfizz_plugin_t *self) +{ + if (self->multi_out) + return MULTI_OUTPUT_COUNT; + + return 2; +} + +#if defined(SFIZZ_LV2_UI) +void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active) +{ + self->ui_active = ui_active; +} +#endif + +struct sfizz_lv2_ccmap { + LV2_URID *cc_to_urid; + int *urid_to_cc; + LV2_URID min_cc_urid; + LV2_URID max_cc_urid; +}; + +sfizz_lv2_ccmap * +sfizz_lv2_ccmap_create(LV2_URID_Map* map) +{ + // store the associations between CC number and parameter URID + // construct it trivially in the form of a tabulated perfect hash function + + LV2_URID* cc_to_urid = new LV2_URID[sfz::config::numCCs]; + int* urid_to_cc; + int urid_to_cc_size; + + LV2_URID min_cc_urid {}; + LV2_URID max_cc_urid {}; + for (int cc = 0; cc < sfz::config::numCCs; ++cc) { + char name[256]; + sprintf(name, SFIZZ_URI "#cc%03d", cc); + LV2_URID urid = map->map(map->handle, name); + if (cc == 0) { + min_cc_urid = urid; + max_cc_urid = urid; + } + else { + min_cc_urid = (urid < min_cc_urid) ? urid : min_cc_urid; + max_cc_urid = (urid > max_cc_urid) ? urid : max_cc_urid; + } + cc_to_urid[cc] = urid; + } + + urid_to_cc_size = max_cc_urid - min_cc_urid + 1; + urid_to_cc = new int[urid_to_cc_size]; + + for (int i = 0; i < urid_to_cc_size; ++i) + urid_to_cc[i] = -1; + + for (int cc = 0; cc < sfz::config::numCCs; ++cc) { + LV2_URID urid = cc_to_urid[cc]; + urid_to_cc[urid - min_cc_urid] = cc; + } + + sfizz_lv2_ccmap *self = new sfizz_lv2_ccmap; + self->cc_to_urid = cc_to_urid; + self->urid_to_cc = urid_to_cc; + self->min_cc_urid = min_cc_urid; + self->max_cc_urid = max_cc_urid; + return self; +} + +void sfizz_lv2_ccmap_free(sfizz_lv2_ccmap *ccmap) +{ + if (ccmap) { + delete[] ccmap->cc_to_urid; + delete[] ccmap->urid_to_cc; + delete ccmap; + } +} + +LV2_URID sfizz_lv2_ccmap_map(const sfizz_lv2_ccmap *ccmap, int cc) +{ + LV2_URID urid = 0; + if (cc >= 0 && cc < sfz::config::numCCs) + urid = ccmap->cc_to_urid[cc]; + return urid; +} + +int sfizz_lv2_ccmap_unmap(const sfizz_lv2_ccmap *ccmap, LV2_URID urid) +{ + int cc = -1; + if (urid >= ccmap->min_cc_urid && urid <= ccmap->max_cc_urid) + cc = ccmap->urid_to_cc[urid - ccmap->min_cc_urid]; + return cc; +} diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h new file mode 100644 index 0000000..e672239 --- /dev/null +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once + +#include "sfizz_lv2.h" +#if defined(SFIZZ_LV2_UI) +#include "plugin/RMSFollower.h" +#endif +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl" +#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz" +// This assumes that the longest path is the default sfz file; if not, change it +#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE)) + +struct sfizz_plugin_t +{ + // Features + LV2_URID_Map *map {}; + LV2_URID_Unmap *unmap {}; + LV2_Worker_Schedule *worker {}; + LV2_Log_Log *log {}; + LV2_Midnam *midnam {}; + + // Ports + bool multi_out { false }; + const LV2_Atom_Sequence *control_port {}; + LV2_Atom_Sequence *automate_port {}; + float *output_buffers[MULTI_OUTPUT_COUNT] {}; + const float *volume_port {}; + const float *polyphony_port {}; + const float *oversampling_port {}; + const float *preload_port {}; + const float *freewheel_port {}; + const float *scala_root_key_port {}; + const float *tuning_frequency_port {}; + const float *stretch_tuning_port {}; + const float *sample_quality_port {}; + const float *oscillator_quality_port {}; + const float *freewheeling_sample_quality_port {}; + const float *freewheeling_oscillator_quality_port {}; + const float *sustain_cancels_release_port {}; + float *active_voices_port {}; + float *num_curves_port {}; + float *num_masters_port {}; + float *num_groups_port {}; + float *num_regions_port {}; + float *num_samples_port {}; + + // Atom forge + LV2_Atom_Forge forge_automate {}; ///< Forge for writing automation atoms in run thread + LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers + + // Logger + LV2_Log_Logger logger {}; + + // URIs + LV2_URID midi_event_uri {}; + LV2_URID options_interface_uri {}; + LV2_URID max_block_length_uri {}; + LV2_URID nominal_block_length_uri {}; + LV2_URID sample_rate_uri {}; + LV2_URID atom_object_uri {}; + LV2_URID atom_blank_uri {}; + LV2_URID atom_float_uri {}; + LV2_URID atom_double_uri {}; + LV2_URID atom_int_uri {}; + LV2_URID atom_long_uri {}; + LV2_URID atom_urid_uri {}; + LV2_URID atom_path_uri {}; + LV2_URID patch_set_uri {}; + LV2_URID patch_get_uri {}; + LV2_URID patch_put_uri {}; + LV2_URID patch_property_uri {}; + LV2_URID patch_value_uri {}; + LV2_URID patch_body_uri {}; + LV2_URID state_changed_uri {}; + LV2_URID sfizz_sfz_file_uri {}; + LV2_URID sfizz_scala_file_uri {}; + LV2_URID sfizz_description_uri {}; + LV2_URID sfizz_num_voices_uri {}; + LV2_URID sfizz_preload_size_uri {}; + LV2_URID sfizz_oversampling_uri {}; + LV2_URID sfizz_last_keyswitch_uri {}; + LV2_URID sfizz_log_status_uri {}; + LV2_URID sfizz_check_modification_uri {}; + LV2_URID sfizz_active_voices_uri {}; + LV2_URID sfizz_osc_blob_uri {}; + LV2_URID sfizz_notify_uri {}; + LV2_URID sfizz_audio_level_uri {}; + LV2_URID time_position_uri {}; + LV2_URID time_bar_uri {}; + LV2_URID time_bar_beat_uri {}; + LV2_URID time_beat_unit_uri {}; + LV2_URID time_beats_per_bar_uri {}; + LV2_URID time_beats_per_minute_uri {}; + LV2_URID time_speed_uri {}; + + // CC parameters + sfizz_lv2_ccmap* ccmap {}; + + // Sfizz related data + sfizz_synth_t *synth {}; + sfizz_client_t *client {}; + spin_mutex_t *synth_mutex {}; + bool expect_nominal_block_length {}; + char sfz_file_path[MAX_PATH_SIZE] {}; + char scala_file_path[MAX_PATH_SIZE] {}; + int num_voices {}; + unsigned int preload_size {}; + sfizz_oversampling_factor_t oversampling {}; + float stretch_tuning {}; + volatile bool check_modification {}; + int max_block_size {}; + int sample_counter {}; + float sample_rate {}; + std::atomic must_update_midnam {}; + volatile bool must_automate_cc {}; + int last_keyswitch { -1 }; + + // Current instrument description + std::mutex *sfz_blob_mutex {}; + volatile int sfz_blob_serial {}; + const uint8_t *volatile sfz_blob_data {}; + volatile uint32_t sfz_blob_size {}; + char text_description[1024]; + volatile bool resend_description {}; + + // Current CC values in the synth (synchronized by `synth_mutex`) + // updated by hdcc or file load + float *cc_current {}; + volatile bool resync_cc { false }; + + // Timing data + int bar {}; + double bar_beat {}; + int beats_per_bar {}; + int beat_unit {}; + double bpm_tempo {}; + double speed {}; + + // Paths + char bundle_path[MAX_BUNDLE_PATH_SIZE] {}; + + // OSC + uint8_t osc_temp[OSC_TEMP_SIZE] {}; + +#if defined(SFIZZ_LV2_UI) + // UI + volatile bool ui_active = false; + RMSFollower rms_follower; +#endif +}; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp new file mode 100644 index 0000000..ba46895 --- /dev/null +++ b/plugins/lv2/sfizz_ui.cpp @@ -0,0 +1,903 @@ +/* + SPDX-License-Identifier: ISC + + Sfizz LV2 plugin + + Copyright 2019-2020, Paul Ferrand + + This file was based on skeleton and example code from the LV2 plugin + distribution available at http://lv2plug.in/ + + The LV2 sample plugins have the following copyright and notice, which are + extended to the current work: + Copyright 2011-2016 David Robillard + Copyright 2011 Gabriel M. Beddingfield + Copyright 2011 James Morris + + 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. + + Compiling this plugin statically against libsndfile implies distributing it + under the terms of the LGPL v3 license. See the LICENSE.md file for more + information. If you did not receive a LICENSE.md file, inform the current + maintainer. + + 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. +*/ + +#include "sfizz_lv2.h" + +#include "editor/Editor.h" +#include "editor/EditorController.h" +#include "editor/EditIds.h" +#include "plugin/InstrumentDescription.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LV2_UI__scaleFactor +#define LV2_UI__scaleFactor LV2_UI_PREFIX "scaleFactor" +#endif + +#include +#include +#include +#include +#include + +#include "vstgui_helpers.h" + +#include "editor/utility/vstgui_before.h" +#include "vstgui/lib/cframe.h" +#include "vstgui/lib/platform/iplatformframe.h" +#include "vstgui/lib/platform/platformfactory.h" +#if defined(_WIN32) +#include "vstgui/lib/platform/platform_win32.h" +#include "vstgui/lib/platform/win32/win32factory.h" +#endif +#include "editor/utility/vstgui_after.h" +using namespace VSTGUI; + +/// +struct FrameHolderDeleter { + void operator()(CFrame* frame) const + { + if (frame->getNbReference() != 1) + frame->forget(); + else + frame->close(); + } +}; +typedef std::unique_ptr FrameHolder; + +/// +struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { +#if LINUX + SoHandleInitializer soHandleInitializer; +#endif +#if MAC + BundleRefInitializer bundleRefInitializer; +#endif + VSTGUIInitializer vstguiInitializer; + LV2UI_Write_Function write = nullptr; + LV2UI_Controller con = nullptr; + LV2_URID_Map *map = nullptr; + LV2_URID_Unmap *unmap = nullptr; + LV2UI_Resize *resize = nullptr; + LV2UI_Touch *touch = nullptr; + sfizz_plugin_t *plugin = nullptr; + FrameHolder uiFrame; + std::unique_ptr editor; +#if LINUX + SharedPointer runLoop; +#endif + + /// VSTGUIEditorInterface + CFrame* getFrame() const override { return uiFrame.get(); } + int32_t getKnobMode () const override { return kLinearMode; } + LV2_Atom_Forge atom_forge; + LV2_URID atom_event_transfer_uri; + LV2_URID atom_object_uri; + LV2_URID atom_float_uri; + LV2_URID atom_path_uri; + LV2_URID atom_urid_uri; + LV2_URID midi_event_uri; + LV2_URID patch_get_uri; + LV2_URID patch_set_uri; + LV2_URID patch_property_uri; + LV2_URID patch_value_uri; +#if !defined(__APPLE__) + LV2_URID opt_scalefactor_uri; + float scale_factor = 0.0f; +#endif + LV2_URID sfizz_sfz_file_uri; + LV2_URID sfizz_scala_file_uri; + LV2_URID sfizz_osc_blob_uri; + LV2_URID sfizz_notify_uri; + LV2_URID sfizz_audio_level_uri; + std::unique_ptr ccmap; + + uint8_t osc_temp[OSC_TEMP_SIZE]; + alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE]; + + int sfz_serial = 0; + bool valid_sfz_serial = false; + bool multi_out = false; + +protected: + void uiSendValue(EditId id, const EditValue& v) override; + void uiBeginSend(EditId id) override; + void uiEndSend(EditId id) override; + void uiSendMIDI(const uint8_t* msg, uint32_t len) override; + void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) override; + +private: + void uiTouch(EditId id, bool t); +}; + +#if defined(_WIN32) +static bool fixBundlePath(std::string& path) +{ + // go up some directories until reaching the *.lv2 directory + bool valid = false; + while (!valid && !path.empty()) { + if (path.back() == '\\' || path.back() == '/') + path.pop_back(); + else if (path.size() > 4 && !memcmp(".lv2", path.data() + path.size() - 4, 4)) + valid = true; + else { + path.pop_back(); + while (!path.empty() && path.back() != '\\' && path.back() != '/') + path.pop_back(); + } + } + return valid; +} +#endif + +static LV2UI_Handle +instantiate(const LV2UI_Descriptor *descriptor, + const char *plugin_uri, + const char *bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget *widget, + const LV2_Feature * const *features) +{ + std::unique_ptr self { new sfizz_ui_t }; + + (void)descriptor; + (void)plugin_uri; + (void)bundle_path; + + self->write = write_function; + self->con = controller; + + void *parentWindowId = nullptr; + + LV2_URID_Map *map = nullptr; + LV2_URID_Unmap *unmap = nullptr; + const LV2_Options_Option* options = nullptr; + + for (const LV2_Feature *const *f = features; *f; f++) + { + if (!strcmp((**f).URI, LV2_URID__map)) + self->map = map = (LV2_URID_Map *)(**f).data; + else if (!strcmp((**f).URI, LV2_URID__unmap)) + self->unmap = unmap = (LV2_URID_Unmap *)(**f).data; + else if (!strcmp((**f).URI, LV2_UI__resize)) + self->resize = (LV2UI_Resize *)(**f).data; + else if (!strcmp((**f).URI, LV2_UI__touch)) + self->touch = (LV2UI_Touch*)(**f).data; + else if (!strcmp((**f).URI, LV2_UI__parent)) + parentWindowId = (**f).data; + else if (!strcmp((**f).URI, LV2_INSTANCE_ACCESS_URI)) + self->plugin = (sfizz_plugin_t *)(**f).data; + else if (!strcmp((**f).URI, LV2_OPTIONS__options)) + options = (const LV2_Options_Option*)(**f).data; // scaleFactor only ATM + } + + // The map feature is required + if (!map || !unmap) + return nullptr; + + // The instance-access feature is required + if (!self->plugin) + return nullptr; + + LV2_Atom_Forge *forge = &self->atom_forge; + lv2_atom_forge_init(forge, map); + self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer); + self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object); + self->atom_float_uri = map->map(map->handle, LV2_ATOM__Float); + self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path); + self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID); + self->midi_event_uri = map->map(map->handle, LV2_MIDI__MidiEvent); +#if !defined(__APPLE__) + self->opt_scalefactor_uri = map->map(map->handle, LV2_UI__scaleFactor); +#endif + self->patch_get_uri = map->map(map->handle, LV2_PATCH__Get); + self->patch_set_uri = map->map(map->handle, LV2_PATCH__Set); + self->patch_property_uri = map->map(map->handle, LV2_PATCH__property); + self->patch_value_uri = map->map(map->handle, LV2_PATCH__value); + self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); + self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); + self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); + self->sfizz_notify_uri = map->map(map->handle, SFIZZ__Notify); + self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel); + self->ccmap.reset(sfizz_lv2_ccmap_create(map)); + +#if !defined(__APPLE__) + if (options != nullptr) { + for (int i=0; options[i].key != 0; ++i) + { + if (options[i].key == self->opt_scalefactor_uri) { + if (options[i].type == self->atom_float_uri) { + self->scale_factor = *(const float*)options[i].value; + fprintf(stderr, "[sfizz] scale factor: %f\n", self->scale_factor); + } else { + fprintf(stderr, "[sfizz] Host provides UI scale factor but has wrong value type\n"); + } + } + } + } +#endif + + // set up the resource path + // * on Linux, this is determined by going 2 folders back from the SO path + // name, and appending "Contents/Resources" (not overridable) + // * on Windows, the folder is set programmatically + // * on macOS, resource files are looked up using CFBundle APIs + +#if defined(_WIN32) + // some hosts give us the DLL path instead of the bundle path, + // so we have to work around that. + std::string realBundlePath { bundle_path }; + if (!fixBundlePath(realBundlePath)) + return nullptr; + + const Win32Factory* winFactory = VSTGUI::getPlatformFactory().asWin32Factory(); + std::string resourcePath = realBundlePath + "\\Contents\\Resources\\"; + winFactory->setResourceBasePath(resourcePath.c_str()); +#endif + + // makes labels refresh correctly + CView::kDirtyCallAlwaysOnMainThread = true; + + const CRect uiBounds(0, 0, Editor::viewWidth, Editor::viewHeight); + CFrame* uiFrame = new CFrame(uiBounds, self.get()); + self->uiFrame.reset(uiFrame); + + IPlatformFrameConfig* config = nullptr; +#if LINUX + SharedPointer runLoop = owned(new Lv2IdleRunLoop); + self->runLoop = runLoop; + VSTGUI::X11::FrameConfig x11Config; + x11Config.runLoop = runLoop; + config = &x11Config; +#endif + + if (!uiFrame->open(parentWindowId, PlatformType::kDefaultNative, config)) + return nullptr; + + Editor *editor = new Editor(*self); + self->editor.reset(editor); + editor->open(*uiFrame); + + // let the editor know about plugin format + self->uiReceiveValue(EditId::PluginFormat, std::string("LV2")); + + // user files dir is not relevant to LV2 (not yet?) + // LV2 has its own path management mechanism + self->uiReceiveValue(EditId::CanEditUserFilesDir, 0); + + // Send the number of outputs if necessary + int numOutputs = sfizz_lv2_get_num_outputs(self->plugin); + if (numOutputs > 2) { + self->multi_out = true; + self->uiReceiveValue(EditId::PluginOutputs, numOutputs); + } + + *widget = reinterpret_cast(uiFrame->getPlatformFrame()->getPlatformRepresentation()); + +#if !defined (__APPLE__) + float scaleFactor = uiFrame->getZoom() * self->scale_factor; + uiFrame->setZoom(scaleFactor); + fprintf(stderr, "[sfizz] zoom factor: %f\n", scaleFactor); + + int width = Editor::viewWidth * scaleFactor; + int height = Editor::viewHeight * scaleFactor; +#else + int width = Editor::viewWidth; + int height = Editor::viewHeight; +#endif + if (self->resize) + self->resize->ui_resize(self->resize->handle, width, height); + + // send a request to receive all parameters + uint8_t buffer[256]; + lv2_atom_forge_set_buffer(forge, buffer, sizeof(buffer)); + LV2_Atom_Forge_Frame frame; + LV2_Atom *msg = (LV2_Atom *)lv2_atom_forge_object(forge, &frame, 0, self->patch_get_uri); + lv2_atom_forge_pop(forge, &frame); + write_function(controller, 0, lv2_atom_total_size(msg), self->atom_event_transfer_uri, msg); + + return self.release(); +} + +static void +cleanup(LV2UI_Handle ui) +{ + sfizz_ui_t *self = (sfizz_ui_t *)ui; + sfizz_lv2_set_ui_active(self->plugin, false); + delete self; +} + +static void +port_event_stereo(sfizz_ui_t *self, uint32_t port_index, const void *buffer) +{ + const float v = *reinterpret_cast(buffer); + + switch (port_index) { + case SFIZZ_VOLUME: + self->uiReceiveValue(EditId::Volume, v); + break; + case SFIZZ_POLYPHONY: + self->uiReceiveValue(EditId::Polyphony, v); + break; + case SFIZZ_OVERSAMPLING: + self->uiReceiveValue(EditId::Oversampling, v); + break; + case SFIZZ_PRELOAD: + self->uiReceiveValue(EditId::PreloadSize, v); + break; + case SFIZZ_SCALA_ROOT_KEY: + self->uiReceiveValue(EditId::ScalaRootKey, v); + break; + case SFIZZ_TUNING_FREQUENCY: + self->uiReceiveValue(EditId::TuningFrequency, v); + break; + case SFIZZ_STRETCH_TUNING: + self->uiReceiveValue(EditId::StretchTuning, v); + break; + case SFIZZ_SAMPLE_QUALITY: + self->uiReceiveValue(EditId::SampleQuality, v); + break; + case SFIZZ_OSCILLATOR_QUALITY: + self->uiReceiveValue(EditId::OscillatorQuality, v); + break; + case SFIZZ_ACTIVE_VOICES: + self->uiReceiveValue(EditId::UINumActiveVoices, v); + break; + case SFIZZ_NUM_CURVES: + self->uiReceiveValue(EditId::UINumCurves, v); + break; + case SFIZZ_NUM_MASTERS: + self->uiReceiveValue(EditId::UINumMasters, v); + break; + case SFIZZ_NUM_GROUPS: + self->uiReceiveValue(EditId::UINumGroups, v); + break; + case SFIZZ_NUM_REGIONS: + self->uiReceiveValue(EditId::UINumRegions, v); + break; + case SFIZZ_NUM_SAMPLES: + self->uiReceiveValue(EditId::UINumPreloadedSamples, v); + break; + case SFIZZ_FREEWHEELING_SAMPLE_QUALITY: + self->uiReceiveValue(EditId::FreewheelingSampleQuality, v); + break; + case SFIZZ_FREEWHEELING_OSCILLATOR_QUALITY: + self->uiReceiveValue(EditId::FreewheelingOscillatorQuality, v); + break; + case SFIZZ_SUSTAIN_CANCELS_RELEASE: + self->uiReceiveValue(EditId::SustainCancelsRelease, v); + break; + } +} + +static void +port_event_multi(sfizz_ui_t *self, uint32_t port_index, const void *buffer) +{ + const float v = *reinterpret_cast(buffer); + + switch (port_index) { + case SFIZZ_MULTI_VOLUME: + self->uiReceiveValue(EditId::Volume, v); + break; + case SFIZZ_MULTI_POLYPHONY: + self->uiReceiveValue(EditId::Polyphony, v); + break; + case SFIZZ_MULTI_OVERSAMPLING: + self->uiReceiveValue(EditId::Oversampling, v); + break; + case SFIZZ_MULTI_PRELOAD: + self->uiReceiveValue(EditId::PreloadSize, v); + break; + case SFIZZ_MULTI_SCALA_ROOT_KEY: + self->uiReceiveValue(EditId::ScalaRootKey, v); + break; + case SFIZZ_MULTI_TUNING_FREQUENCY: + self->uiReceiveValue(EditId::TuningFrequency, v); + break; + case SFIZZ_MULTI_STRETCH_TUNING: + self->uiReceiveValue(EditId::StretchTuning, v); + break; + case SFIZZ_MULTI_SAMPLE_QUALITY: + self->uiReceiveValue(EditId::SampleQuality, v); + break; + case SFIZZ_MULTI_OSCILLATOR_QUALITY: + self->uiReceiveValue(EditId::OscillatorQuality, v); + break; + case SFIZZ_MULTI_ACTIVE_VOICES: + self->uiReceiveValue(EditId::UINumActiveVoices, v); + break; + case SFIZZ_MULTI_NUM_CURVES: + self->uiReceiveValue(EditId::UINumCurves, v); + break; + case SFIZZ_MULTI_NUM_MASTERS: + self->uiReceiveValue(EditId::UINumMasters, v); + break; + case SFIZZ_MULTI_NUM_GROUPS: + self->uiReceiveValue(EditId::UINumGroups, v); + break; + case SFIZZ_MULTI_NUM_REGIONS: + self->uiReceiveValue(EditId::UINumRegions, v); + break; + case SFIZZ_MULTI_NUM_SAMPLES: + self->uiReceiveValue(EditId::UINumPreloadedSamples, v); + break; + case SFIZZ_MULTI_FREEWHEELING_SAMPLE_QUALITY: + self->uiReceiveValue(EditId::FreewheelingSampleQuality, v); + break; + case SFIZZ_MULTI_FREEWHEELING_OSCILLATOR_QUALITY: + self->uiReceiveValue(EditId::FreewheelingOscillatorQuality, v); + break; + case SFIZZ_MULTI_SUSTAIN_CANCELS_RELEASE: + self->uiReceiveValue(EditId::SustainCancelsRelease, v); + break; + } +} + +static void +port_event(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void *buffer) +{ + sfizz_ui_t *self = (sfizz_ui_t *)ui; + if (format == 0) { + if (self->multi_out) + port_event_multi(self, port_index, buffer); + else + port_event_stereo(self, port_index, buffer); + } + else if (format == self->atom_event_transfer_uri) { + auto *atom = reinterpret_cast(buffer); + + if (atom->type == self->atom_object_uri) { + const LV2_Atom *prop = nullptr; + const LV2_Atom *value = nullptr; + + lv2_atom_object_get( + reinterpret_cast(atom), + self->patch_property_uri, &prop, self->patch_value_uri, &value, 0); + + if (prop && value && prop->type == self->atom_urid_uri) { + const LV2_URID prop_uri = reinterpret_cast(prop)->body; + auto *value_body = reinterpret_cast(LV2_ATOM_BODY_CONST(value)); + + int cc = sfizz_lv2_ccmap_unmap(self->ccmap.get(), prop_uri); + + if (cc != -1) { + if (value->type == self->atom_float_uri) { + float ccvalue = *reinterpret_cast(value_body); + self->uiReceiveValue(editIdForCC(cc), ccvalue); + } + } + else if (prop_uri == self->sfizz_sfz_file_uri && value->type == self->atom_path_uri) { + std::string path(value_body, strnlen(value_body, value->size)); + self->uiReceiveValue(EditId::SfzFile, path); + } + else if (prop_uri == self->sfizz_scala_file_uri && value->type == self->atom_path_uri) { + std::string path(value_body, strnlen(value_body, value->size)); + self->uiReceiveValue(EditId::ScalaFile, path); + } + } + } + else if (atom->type == self->sfizz_osc_blob_uri) { + const char *path; + const char *sig; + const sfizz_arg_t *args; + uint8_t buffer[1024]; + if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0) + self->uiReceiveMessage(path, sig, args); + } + else if (atom->type == self->sfizz_audio_level_uri) { + const LV2_Atom_Vector *vector = reinterpret_cast(atom); + + if (vector->body.child_type == self->atom_float_uri && + vector->body.child_size == sizeof(float)) + { + const uint8_t *vec_body = reinterpret_cast( + LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vector)); + const uint8_t *vec_end = reinterpret_cast( + LV2_ATOM_BODY_CONST(&vector->atom)) + vector->atom.size; + + const float *levels = reinterpret_cast(vec_body); + uint32_t count = static_cast((vec_end - vec_body) / sizeof(float)); + + for (uint32_t i = 0; i < count; ++i) + self->uiReceiveValue(editIdForLevel(i), levels[i]); + } + } + } + + (void)buffer_size; +} + +static void +sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc) +{ + self->uiReceiveValue(EditId::UINumCurves, desc.numCurves); + self->uiReceiveValue(EditId::UINumMasters, desc.numMasters); + self->uiReceiveValue(EditId::UINumGroups, desc.numGroups); + self->uiReceiveValue(EditId::UINumRegions, desc.numRegions); + self->uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); + + const fs::path rootPath = fs::u8path(desc.rootPath); + const fs::path imagePath = rootPath / fs::u8path(desc.image); + const fs::path imageCtrlPath = rootPath / fs::u8path(desc.image_controls); + self->uiReceiveValue(EditId::BackgroundImage, imagePath.u8string()); + self->uiReceiveValue(EditId::ControlsImage, imageCtrlPath.u8string()); + + for (unsigned key = 0; key < 128; ++key) { + bool keyUsed = desc.keyUsed.test(key); + bool keyswitchUsed = desc.keyswitchUsed.test(key); + self->uiReceiveValue(editIdForKeyUsed(key), float(keyUsed)); + self->uiReceiveValue(editIdForKeyswitchUsed(key), float(keyswitchUsed)); + if (keyUsed) + self->uiReceiveValue(editIdForKeyLabel(key), desc.keyLabel[key]); + if (keyswitchUsed) + self->uiReceiveValue(editIdForKeyswitchLabel(key), desc.keyswitchLabel[key]); + } + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); + self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); + if (ccUsed) { + self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); + self->uiReceiveValue(editIdForCCLabel(cc), desc.ccLabel[cc]); + } + } +} + +static void +sfizz_ui_check_sfz_update(sfizz_ui_t *self) +{ + uint8_t *data = nullptr; + uint32_t size = 0; + int new_serial = 0; + const int *serial = self->valid_sfz_serial ? &self->sfz_serial : nullptr; + + bool update = sfizz_lv2_fetch_description( + self->plugin, serial, &data, &size, &new_serial); + + if (update) { + std::unique_ptr cleanup(data); + self->sfz_serial = new_serial; + self->valid_sfz_serial = true; + + const InstrumentDescription desc = parseDescriptionBlob( + absl::string_view(reinterpret_cast(data), size)); + sfizz_ui_update_description(self, desc); + } +} + +static int +idle(LV2UI_Handle ui) +{ + sfizz_ui_t *self = (sfizz_ui_t *)ui; + + // check if there are news regarding the current SFZ + sfizz_ui_check_sfz_update(self); + +#if LINUX + self->runLoop->execIdle(); +#else + (void)self; +#endif + + sfizz_lv2_set_ui_active(self->plugin, self->uiFrame->isVisible()); + + return 0; +} + +static const LV2UI_Idle_Interface idle_interface = { + &idle, +}; + +static int +show(LV2UI_Handle ui) +{ + sfizz_ui_t *self = (sfizz_ui_t *)ui; + self->uiFrame->setVisible(true); + return 0; +} + +static int +hide(LV2UI_Handle ui) +{ + sfizz_ui_t *self = (sfizz_ui_t *)ui; + self->uiFrame->setVisible(false); + return 0; +} + +static const LV2UI_Show_Interface show_interface = { + &show, + &hide, +}; + +const void * +extension_data(const char *uri) +{ + if (!strcmp(uri, LV2_UI__idleInterface)) + return &idle_interface; + + if (!strcmp(uri, LV2_UI__showInterface)) + return &show_interface; + + return nullptr; +} + +static const LV2UI_Descriptor descriptor = { + SFIZZ_UI_URI, + instantiate, + cleanup, + port_event, + extension_data, +}; + +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor * +lv2ui_descriptor(uint32_t index) +{ + switch (index) + { + case 0: + return &descriptor; + default: + return nullptr; + } +} + +/// +void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v) +{ + auto sendFloat = [this](int port, float value) { + write(con, port, sizeof(float), 0, &value); + }; + + auto sendPath = [this](LV2_URID property, const std::string& value) { + LV2_Atom_Forge *forge = &atom_forge; + LV2_Atom_Forge_Frame frame; + auto *atom = reinterpret_cast(atom_temp); + lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp)); + if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) && + lv2_atom_forge_key(forge, patch_property_uri) && + lv2_atom_forge_urid(forge, property) && + lv2_atom_forge_key(forge, patch_value_uri) && + lv2_atom_forge_path(forge, value.data(), value.size())) + { + lv2_atom_forge_pop(forge, &frame); + write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom); + } + }; + + auto sendController = [this](LV2_URID property, float value) { + LV2_Atom_Forge *forge = &atom_forge; + LV2_Atom_Forge_Frame frame; + auto *atom = reinterpret_cast(atom_temp); + lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp)); + if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) && + lv2_atom_forge_key(forge, patch_property_uri) && + lv2_atom_forge_urid(forge, property) && + lv2_atom_forge_key(forge, patch_value_uri) && + lv2_atom_forge_float(forge, value)) + { + lv2_atom_forge_pop(forge, &frame); + write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom); + } + }; + + switch (id) { + case EditId::Volume: + if (multi_out) + sendFloat(SFIZZ_MULTI_VOLUME, v.to_float()); + else + sendFloat(SFIZZ_VOLUME, v.to_float()); + break; + case EditId::Polyphony: + if (multi_out) + sendFloat(SFIZZ_MULTI_POLYPHONY, v.to_float()); + else + sendFloat(SFIZZ_POLYPHONY, v.to_float()); + break; + case EditId::Oversampling: + if (multi_out) + sendFloat(SFIZZ_MULTI_OVERSAMPLING, v.to_float()); + else + sendFloat(SFIZZ_OVERSAMPLING, v.to_float()); + break; + case EditId::PreloadSize: + if (multi_out) + sendFloat(SFIZZ_MULTI_PRELOAD, v.to_float()); + else + sendFloat(SFIZZ_PRELOAD, v.to_float()); + break; + case EditId::ScalaRootKey: + if (multi_out) + sendFloat(SFIZZ_MULTI_SCALA_ROOT_KEY, v.to_float()); + else + sendFloat(SFIZZ_SCALA_ROOT_KEY, v.to_float()); + break; + case EditId::TuningFrequency: + if (multi_out) + sendFloat(SFIZZ_MULTI_TUNING_FREQUENCY, v.to_float()); + else + sendFloat(SFIZZ_TUNING_FREQUENCY, v.to_float()); + break; + case EditId::StretchTuning: + if (multi_out) + sendFloat(SFIZZ_MULTI_STRETCH_TUNING, v.to_float()); + else + sendFloat(SFIZZ_STRETCH_TUNING, v.to_float()); + break; + case EditId::SampleQuality: + if (multi_out) + sendFloat(SFIZZ_MULTI_SAMPLE_QUALITY, v.to_float()); + else + sendFloat(SFIZZ_SAMPLE_QUALITY, v.to_float()); + break; + case EditId::OscillatorQuality: + if (multi_out) + sendFloat(SFIZZ_MULTI_OSCILLATOR_QUALITY, v.to_float()); + else + sendFloat(SFIZZ_OSCILLATOR_QUALITY, v.to_float()); + break; + case EditId::FreewheelingSampleQuality: + if (multi_out) + sendFloat(SFIZZ_MULTI_FREEWHEELING_SAMPLE_QUALITY, v.to_float()); + else + sendFloat(SFIZZ_FREEWHEELING_SAMPLE_QUALITY, v.to_float()); + break; + case EditId::FreewheelingOscillatorQuality: + if (multi_out) + sendFloat(SFIZZ_MULTI_FREEWHEELING_OSCILLATOR_QUALITY, v.to_float()); + else + sendFloat(SFIZZ_FREEWHEELING_OSCILLATOR_QUALITY, v.to_float()); + break; + case EditId::SustainCancelsRelease: + if (multi_out) + sendFloat(SFIZZ_MULTI_SUSTAIN_CANCELS_RELEASE, v.to_float()); + else + sendFloat(SFIZZ_SUSTAIN_CANCELS_RELEASE, v.to_float()); + break; + case EditId::SfzFile: + sendPath(sfizz_sfz_file_uri, v.to_string()); + break; + case EditId::ScalaFile: + sendPath(sfizz_scala_file_uri, v.to_string()); + break; + default: + if (editIdIsCC(id)) { + int cc = ccForEditId(id); +#if defined(SFIZZ_LV2_PSA) + if (cc >= 0 && cc < 128) { + // Send MIDI message + uint8_t msg[3]; + msg[0] = 0xB0; + msg[1] = static_cast(cc); + msg[2] = static_cast(v.to_float() * 127); + uiSendMIDI(msg, 3); + break; + } +#endif + LV2_URID urid = sfizz_lv2_ccmap_map(ccmap.get(), cc); + sendController(urid, v.to_float()); + } + break; + } +} + +void sfizz_ui_t::uiBeginSend(EditId id) +{ + uiTouch(id, true); +} + +void sfizz_ui_t::uiEndSend(EditId id) +{ + uiTouch(id, false); +} + +void sfizz_ui_t::uiTouch(EditId id, bool t) +{ + if (!touch) + return; + + switch (id) { + case EditId::Volume: + touch->touch(touch->handle, SFIZZ_VOLUME, t); + break; + case EditId::Polyphony: + touch->touch(touch->handle, SFIZZ_POLYPHONY, t); + break; + case EditId::Oversampling: + touch->touch(touch->handle, SFIZZ_OVERSAMPLING, t); + break; + case EditId::PreloadSize: + touch->touch(touch->handle, SFIZZ_PRELOAD, t); + break; + case EditId::ScalaRootKey: + touch->touch(touch->handle, SFIZZ_SCALA_ROOT_KEY, t); + break; + case EditId::TuningFrequency: + touch->touch(touch->handle, SFIZZ_TUNING_FREQUENCY, t); + break; + case EditId::StretchTuning: + touch->touch(touch->handle, SFIZZ_STRETCH_TUNING, t); + break; + case EditId::SampleQuality: + touch->touch(touch->handle, SFIZZ_SAMPLE_QUALITY, t); + break; + case EditId::OscillatorQuality: + touch->touch(touch->handle, SFIZZ_OSCILLATOR_QUALITY, t); + break; + default: + break; + } +} + +void sfizz_ui_t::uiSendMIDI(const uint8_t* msg, uint32_t len) +{ + LV2_Atom_Forge *forge = &atom_forge; + auto *atom = reinterpret_cast(atom_temp); + lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp)); + if (lv2_atom_forge_atom(forge, len, midi_event_uri) && + lv2_atom_forge_write(forge, msg, len)) + { + write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom); + } +} + +void sfizz_ui_t::uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) +{ + uint8_t *osc_temp = this->osc_temp; + uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args); + + if (osc_size > OSC_TEMP_SIZE) + return; + + LV2_Atom_Forge *forge = &atom_forge; + auto *atom = reinterpret_cast(atom_temp); + lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp)); + + if (lv2_atom_forge_atom(forge, osc_size, sfizz_osc_blob_uri) && + lv2_atom_forge_raw(forge, osc_temp, osc_size)) + { + write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom); + } +} diff --git a/plugins/lv2/sfizz_ui.ttl.in b/plugins/lv2/sfizz_ui.ttl.in new file mode 100644 index 0000000..5347031 --- /dev/null +++ b/plugins/lv2/sfizz_ui.ttl.in @@ -0,0 +1,17 @@ +@prefix lv2: . +@prefix opts: . +@prefix ui: . +@prefix urid: . + +<@LV2PLUGIN_URI@#ui> + lv2:extensionData ui:idleInterface ; + lv2:extensionData ui:showInterface ; + lv2:requiredFeature ui:idleInterface ; + lv2:optionalFeature ui:noUserResize ; + lv2:optionalFeature ui:resize ; + lv2:optionalFeature ui:parent ; + lv2:optionalFeature ui:touch ; + opts:supportedOption ui:scaleFactor ; + lv2:requiredFeature urid:map ; + lv2:requiredFeature urid:unmap ; + lv2:requiredFeature . diff --git a/plugins/lv2/vendor/lv2/atom/atom.h b/plugins/lv2/vendor/lv2/atom/atom.h new file mode 100644 index 0000000..a6178cf --- /dev/null +++ b/plugins/lv2/vendor/lv2/atom/atom.h @@ -0,0 +1,247 @@ +// Copyright 2008-2016 David Robillard +// SPDX-License-Identifier: ISC + +#ifndef LV2_ATOM_H +#define LV2_ATOM_H + +/** + @defgroup atom Atom + @ingroup lv2 + + A generic value container and several data types. + + See for details. + + @{ +*/ + +#include + +// clang-format off + +#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" ///< http://lv2plug.in/ns/ext/atom +#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" ///< http://lv2plug.in/ns/ext/atom# + +#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" ///< http://lv2plug.in/ns/ext/atom#Atom +#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" ///< http://lv2plug.in/ns/ext/atom#AtomPort +#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" ///< http://lv2plug.in/ns/ext/atom#Blank +#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" ///< http://lv2plug.in/ns/ext/atom#Bool +#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" ///< http://lv2plug.in/ns/ext/atom#Chunk +#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" ///< http://lv2plug.in/ns/ext/atom#Double +#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" ///< http://lv2plug.in/ns/ext/atom#Event +#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" ///< http://lv2plug.in/ns/ext/atom#Float +#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" ///< http://lv2plug.in/ns/ext/atom#Int +#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" ///< http://lv2plug.in/ns/ext/atom#Literal +#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" ///< http://lv2plug.in/ns/ext/atom#Long +#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" ///< http://lv2plug.in/ns/ext/atom#Number +#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" ///< http://lv2plug.in/ns/ext/atom#Object +#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" ///< http://lv2plug.in/ns/ext/atom#Path +#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" ///< http://lv2plug.in/ns/ext/atom#Property +#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" ///< http://lv2plug.in/ns/ext/atom#Resource +#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" ///< http://lv2plug.in/ns/ext/atom#Sequence +#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" ///< http://lv2plug.in/ns/ext/atom#Sound +#define LV2_ATOM__String LV2_ATOM_PREFIX "String" ///< http://lv2plug.in/ns/ext/atom#String +#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" ///< http://lv2plug.in/ns/ext/atom#Tuple +#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" ///< http://lv2plug.in/ns/ext/atom#URI +#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" ///< http://lv2plug.in/ns/ext/atom#URID +#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" ///< http://lv2plug.in/ns/ext/atom#Vector +#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" ///< http://lv2plug.in/ns/ext/atom#atomTransfer +#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" ///< http://lv2plug.in/ns/ext/atom#beatTime +#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" ///< http://lv2plug.in/ns/ext/atom#bufferType +#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" ///< http://lv2plug.in/ns/ext/atom#childType +#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" ///< http://lv2plug.in/ns/ext/atom#eventTransfer +#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" ///< http://lv2plug.in/ns/ext/atom#frameTime +#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" ///< http://lv2plug.in/ns/ext/atom#supports +#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "timeUnit" ///< http://lv2plug.in/ns/ext/atom#timeUnit + +// clang-format on + +#define LV2_ATOM_REFERENCE_TYPE 0 ///< The special type for a reference atom + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond */ +/** This expression will fail to compile if double does not fit in 64 bits. */ +typedef char lv2_atom_assert_double_fits_in_64_bits + [((sizeof(double) <= sizeof(uint64_t)) * 2) - 1]; +/** @endcond */ + +/** + Return a pointer to the contents of an Atom. The "contents" of an atom + is the data past the complete type-specific header. + @param type The type of the atom, for example LV2_Atom_String. + @param atom A variable-sized atom. +*/ +#define LV2_ATOM_CONTENTS(type, atom) ((void*)((uint8_t*)(atom) + sizeof(type))) + +/** + Const version of LV2_ATOM_CONTENTS. +*/ +#define LV2_ATOM_CONTENTS_CONST(type, atom) \ + ((const void*)((const uint8_t*)(atom) + sizeof(type))) + +/** + Return a pointer to the body of an Atom. The "body" of an atom is the + data just past the LV2_Atom head (i.e. the same offset for all types). +*/ +#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom) + +/** + Const version of LV2_ATOM_BODY. +*/ +#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom) + +/** The header of an atom:Atom. */ +typedef struct { + uint32_t size; /**< Size in bytes, not including type and size. */ + uint32_t type; /**< Type of this atom (mapped URI). */ +} LV2_Atom; + +/** An atom:Int or atom:Bool. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int32_t body; /**< Integer value. */ +} LV2_Atom_Int; + +/** An atom:Long. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int64_t body; /**< Integer value. */ +} LV2_Atom_Long; + +/** An atom:Float. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + float body; /**< Floating point value. */ +} LV2_Atom_Float; + +/** An atom:Double. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + double body; /**< Floating point value. */ +} LV2_Atom_Double; + +/** An atom:Bool. May be cast to LV2_Atom. */ +typedef LV2_Atom_Int LV2_Atom_Bool; + +/** An atom:URID. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + uint32_t body; /**< URID. */ +} LV2_Atom_URID; + +/** An atom:String. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + /* Contents (a null-terminated UTF-8 string) follow here. */ +} LV2_Atom_String; + +/** The body of an atom:Literal. */ +typedef struct { + uint32_t datatype; /**< Datatype URID. */ + uint32_t lang; /**< Language URID. */ + /* Contents (a null-terminated UTF-8 string) follow here. */ +} LV2_Atom_Literal_Body; + +/** An atom:Literal. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Literal_Body body; /**< Body. */ +} LV2_Atom_Literal; + +/** An atom:Tuple. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + /* Contents (a series of complete atoms) follow here. */ +} LV2_Atom_Tuple; + +/** The body of an atom:Vector. */ +typedef struct { + uint32_t child_size; /**< The size of each element in the vector. */ + uint32_t child_type; /**< The type of each element in the vector. */ + /* Contents (a series of packed atom bodies) follow here. */ +} LV2_Atom_Vector_Body; + +/** An atom:Vector. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Vector_Body body; /**< Body. */ +} LV2_Atom_Vector; + +/** The body of an atom:Property (typically in an atom:Object). */ +typedef struct { + uint32_t key; /**< Key (predicate) (mapped URI). */ + uint32_t context; /**< Context URID (may be, and generally is, 0). */ + LV2_Atom value; /**< Value atom header. */ + /* Value atom body follows here. */ +} LV2_Atom_Property_Body; + +/** An atom:Property. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Property_Body body; /**< Body. */ +} LV2_Atom_Property; + +/** The body of an atom:Object. May be cast to LV2_Atom. */ +typedef struct { + uint32_t id; /**< URID, or 0 for blank. */ + uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */ + /* Contents (a series of property bodies) follow here. */ +} LV2_Atom_Object_Body; + +/** An atom:Object. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + LV2_Atom_Object_Body body; /**< Body. */ +} LV2_Atom_Object; + +/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */ +typedef struct { + /** Time stamp. Which type is valid is determined by context. */ + union { + int64_t frames; /**< Time in audio frames. */ + double beats; /**< Time in beats. */ + } time; + LV2_Atom body; /**< Event body atom header. */ + /* Body atom contents follow here. */ +} LV2_Atom_Event; + +/** + The body of an atom:Sequence (a sequence of events). + + The unit field is either a URID that described an appropriate time stamp + type, or may be 0 where a default stamp type is known. For + LV2_Descriptor::run(), the default stamp type is audio frames. + + The contents of a sequence is a series of LV2_Atom_Event, each aligned + to 64-bits, for example: +