From 87abc7919b81717ff99c9371628bce109ceaadc1 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 17 Apr 2025 12:39:45 +0100 Subject: [PATCH] DirectX: Split out function definitions into a separate cpp file --- .../Builds/Android/app/CMakeLists.txt | 2 + .../VisualStudio2019/DemoRunner_App.vcxproj | 3 + .../DemoRunner_App.vcxproj.filters | 3 + .../VisualStudio2022/DemoRunner_App.vcxproj | 3 + .../DemoRunner_App.vcxproj.filters | 3 + .../Builds/Android/app/CMakeLists.txt | 2 + .../AudioPerformanceTest_App.vcxproj | 3 + .../AudioPerformanceTest_App.vcxproj.filters | 3 + .../Builds/Android/app/CMakeLists.txt | 2 + .../AudioPluginHost_App.vcxproj | 3 + .../AudioPluginHost_App.vcxproj.filters | 3 + .../AudioPluginHost_App.vcxproj | 3 + .../AudioPluginHost_App.vcxproj.filters | 3 + .../Builds/Android/app/CMakeLists.txt | 2 + .../NetworkGraphicsDemo_App.vcxproj | 3 + .../NetworkGraphicsDemo_App.vcxproj.filters | 3 + .../VisualStudio2019/Projucer_App.vcxproj | 3 + .../Projucer_App.vcxproj.filters | 3 + .../VisualStudio2022/Projucer_App.vcxproj | 3 + .../Projucer_App.vcxproj.filters | 3 + .../UnitTestRunner_ConsoleApp.vcxproj | 3 + .../UnitTestRunner_ConsoleApp.vcxproj.filters | 3 + .../UnitTestRunner_ConsoleApp.vcxproj | 3 + .../UnitTestRunner_ConsoleApp.vcxproj.filters | 3 + .../WindowsDLL_DynamicLibrary.vcxproj | 3 + .../WindowsDLL_DynamicLibrary.vcxproj.filters | 3 + modules/juce_graphics/juce_graphics.cpp | 1 + .../native/juce_DirectX_windows.cpp | 1469 +++++++++++++++++ .../native/juce_DirectX_windows.h | 1382 +--------------- 29 files changed, 1627 insertions(+), 1299 deletions(-) create mode 100644 modules/juce_graphics/native/juce_DirectX_windows.cpp diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt index 2c62ecc51f..17fdff48fb 100644 --- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt +++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt @@ -1991,6 +1991,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" @@ -4645,6 +4646,7 @@ set_source_files_properties( "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj index a428ff778a..389dcbbfc4 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj @@ -2418,6 +2418,9 @@ true + + true + true diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters index 2c37590daa..3dc2be9f82 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters @@ -3196,6 +3196,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj index d24f003984..a584739c91 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj @@ -2418,6 +2418,9 @@ true + + true + true diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters index 3b8b335b54..bfd7177362 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters @@ -3196,6 +3196,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt index 6cc3556093..de5dbfa690 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt @@ -1753,6 +1753,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" @@ -4021,6 +4022,7 @@ set_source_files_properties( "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj index f27d566a08..5fa0947f26 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj @@ -2109,6 +2109,9 @@ true + + true + true diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters index ecf7d322fa..c59991b45c 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters @@ -2704,6 +2704,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt index 9c780cfb87..2f132d11bb 100644 --- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt @@ -1883,6 +1883,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" @@ -4304,6 +4305,7 @@ set_source_files_properties( "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj index 0a8743b63c..21504e547e 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj @@ -2243,6 +2243,9 @@ true + + true + true diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters index 9f7bbe750b..205a49d177 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters @@ -2911,6 +2911,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj index 75311b8d20..1790e6aceb 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj @@ -2243,6 +2243,9 @@ true + + true + true diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters index cc98879f6d..41635442a1 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters @@ -2911,6 +2911,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt index 03d1198f33..55c4f4104e 100644 --- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt +++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt @@ -1772,6 +1772,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" @@ -4120,6 +4121,7 @@ set_source_files_properties( "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h" "../../../../../modules/juce_graphics/native/juce_Direct2DPixelDataPage_windows.h" "../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp" + "../../../../../modules/juce_graphics/native/juce_DirectX_windows.cpp" "../../../../../modules/juce_graphics/native/juce_DirectX_windows.h" "../../../../../modules/juce_graphics/native/juce_EventTracing.h" "../../../../../modules/juce_graphics/native/juce_Fonts_android.cpp" diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj index 03dc121a80..95bea5dcb4 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj @@ -2130,6 +2130,9 @@ true + + true + true diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters index 94395de944..39217dba1e 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters @@ -2758,6 +2758,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj index 7a1e41bb71..5421661e1a 100644 --- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj @@ -1272,6 +1272,9 @@ true + + true + true diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters index b834587298..8f33150987 100644 --- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters @@ -1597,6 +1597,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj index 6652434b4c..d1854b1428 100644 --- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj @@ -1272,6 +1272,9 @@ true + + true + true diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters index abf132d7bc..516194f1a1 100644 --- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters @@ -1597,6 +1597,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj index 649e37c1ac..9155e420bf 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj @@ -2251,6 +2251,9 @@ true + + true + true diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters index 04e7da90cd..27f050e2e5 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -2959,6 +2959,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj index e4b79b165b..00969388f6 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj @@ -2251,6 +2251,9 @@ true + + true + true diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters index 609f3d2c89..031a976ede 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -2959,6 +2959,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj index b4562fba97..cf92a7d5b9 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj @@ -2129,6 +2129,9 @@ true + + true + true diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters index 6a598eb1f8..52ca71fc8e 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters @@ -2755,6 +2755,9 @@ JUCE Modules\juce_graphics\native + + JUCE Modules\juce_graphics\native + JUCE Modules\juce_graphics\native diff --git a/modules/juce_graphics/juce_graphics.cpp b/modules/juce_graphics/juce_graphics.cpp index 51b075acb2..f0c775358d 100644 --- a/modules/juce_graphics/juce_graphics.cpp +++ b/modules/juce_graphics/juce_graphics.cpp @@ -228,6 +228,7 @@ extern "C" #include "native/juce_Direct2DGraphicsContextImpl_windows.h" #include "native/juce_Direct2DImageContext_windows.h" + #include "native/juce_DirectX_windows.cpp" #include "native/juce_DirectWriteTypeface_windows.cpp" #include "native/juce_IconHelpers_windows.cpp" #include "native/juce_Direct2DGraphicsContext_windows.cpp" diff --git a/modules/juce_graphics/native/juce_DirectX_windows.cpp b/modules/juce_graphics/native/juce_DirectX_windows.cpp new file mode 100644 index 0000000000..8d7c4593f7 --- /dev/null +++ b/modules/juce_graphics/native/juce_DirectX_windows.cpp @@ -0,0 +1,1469 @@ +/* + ============================================================================== + + This file is part of the JUCE framework. + Copyright (c) Raw Material Software Limited + + JUCE is an open source framework subject to commercial or open source + licensing. + + By downloading, installing, or using the JUCE framework, or combining the + JUCE framework with any other source code, object code, content or any other + copyrightable work, you agree to the terms of the JUCE End User Licence + Agreement, and all incorporated terms including the JUCE Privacy Policy and + the JUCE Website Terms of Service, as applicable, which will bind you. If you + do not agree to the terms of these agreements, we will not license the JUCE + framework to you, and you must discontinue the installation or download + process and cease use of the JUCE framework. + + JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/ + JUCE Privacy Policy: https://juce.com/juce-privacy-policy + JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/ + + Or: + + You may also use this code under the terms of the AGPLv3: + https://www.gnu.org/licenses/agpl-3.0.en.html + + THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL + WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +constexpr auto enableDirectXDebugLayer = false; + +ScopedMultithread::ScopedMultithread (ID2D1Multithread* multithreadIn) + : multithread (addComSmartPtrOwner (multithreadIn)) +{ + multithreadIn->Enter(); +} + +ScopedMultithread::~ScopedMultithread() +{ + multithread->Leave(); +} + +//============================================================================== +ScopedGeometryWithSink::ScopedGeometryWithSink (ID2D1Factory* factory, D2D1_FILL_MODE fillMode) +{ + if (const auto hr = factory->CreatePathGeometry (geometry.resetAndGetPointerAddress()); FAILED (hr)) + return; + + if (const auto hr = geometry->Open (sink.resetAndGetPointerAddress()); FAILED (hr)) + return; + + sink->SetFillMode (fillMode); +} + +ScopedGeometryWithSink::~ScopedGeometryWithSink() +{ + if (sink == nullptr) + return; + + const auto hr = sink->Close(); + jassertquiet (SUCCEEDED (hr)); +} + +//============================================================================== +bool D2DHelpers::isTransformAxisAligned (const AffineTransform& transform) +{ + return transform.mat01 == 0.0f && transform.mat10 == 0.0f; +} + +void D2DHelpers::pathToGeometrySink (const Path& path, + ID2D1GeometrySink* sink, + const AffineTransform& transform, + D2D1_FIGURE_BEGIN figureMode) +{ + class ScopedFigure + { + public: + ScopedFigure (ID2D1GeometrySink* s, D2D1_POINT_2F pt, D2D1_FIGURE_BEGIN mode) + : sink (s) + { + sink->BeginFigure (pt, mode); + } + + ~ScopedFigure() + { + if (sink != nullptr) + sink->EndFigure (end); + } + + void setClosed() + { + end = D2D1_FIGURE_END_CLOSED; + } + + private: + ID2D1GeometrySink* sink = nullptr; + D2D1_FIGURE_END end = D2D1_FIGURE_END_OPEN; + + JUCE_DECLARE_NON_COPYABLE (ScopedFigure) + JUCE_DECLARE_NON_MOVEABLE (ScopedFigure) + }; + + // Every call to BeginFigure must have a matching call to EndFigure. But - the Path does not necessarily + // have matching startNewSubPath and closePath markers. + D2D1_POINT_2F lastLocation{}; + std::optional figure; + Path::Iterator it (path); + + const auto doTransform = [&transform] (float x, float y) + { + transform.transformPoint (x, y); + return D2D1_POINT_2F { x, y }; + }; + + const auto updateFigure = [&] (float x, float y) + { + if (! figure.has_value()) + figure.emplace (sink, lastLocation, figureMode); + + lastLocation = doTransform (x, y); + }; + + while (it.next()) + { + switch (it.elementType) + { + case Path::Iterator::lineTo: + updateFigure (it.x1, it.y1); + sink->AddLine (lastLocation); + break; + + case Path::Iterator::quadraticTo: + updateFigure (it.x2, it.y2); + sink->AddQuadraticBezier ({ doTransform (it.x1, it.y1), lastLocation }); + break; + + case Path::Iterator::cubicTo: + updateFigure (it.x3, it.y3); + sink->AddBezier ({ doTransform (it.x1, it.y1), doTransform (it.x2, it.y2), lastLocation }); + break; + + case Path::Iterator::closePath: + if (figure.has_value()) + figure->setClosed(); + + figure.reset(); + break; + + case Path::Iterator::startNewSubPath: + figure.reset(); + lastLocation = doTransform (it.x1, it.y1); + figure.emplace (sink, lastLocation, figureMode); + break; + } + } +} + +D2D1_POINT_2F D2DHelpers::pointTransformed (Point pt, const AffineTransform& transform) +{ + transform.transformPoint (pt.x, pt.y); + return { (FLOAT) pt.x, (FLOAT) pt.y }; +} + +void D2DHelpers::rectToGeometrySink (const Rectangle& rect, + ID2D1GeometrySink* sink, + const AffineTransform& transform, + D2D1_FIGURE_BEGIN figureMode) +{ + const auto a = pointTransformed (rect.getTopLeft(), transform); + const auto b = pointTransformed (rect.getTopRight(), transform); + const auto c = pointTransformed (rect.getBottomRight(), transform); + const auto d = pointTransformed (rect.getBottomLeft(), transform); + + sink->BeginFigure (a, figureMode); + sink->AddLine (b); + sink->AddLine (c); + sink->AddLine (d); + sink->EndFigure (D2D1_FIGURE_END_CLOSED); +} + +ComSmartPtr D2DHelpers::rectListToPathGeometry (ID2D1Factory* factory, + const RectangleList& clipRegion, + const AffineTransform& transform, + D2D1_FILL_MODE fillMode, + D2D1_FIGURE_BEGIN figureMode, + [[maybe_unused]] Direct2DMetrics* metrics) +{ + JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime) + ScopedGeometryWithSink objects { factory, fillMode }; + + if (objects.sink == nullptr) + return {}; + + for (int i = clipRegion.getNumRectangles(); --i >= 0;) + rectToGeometrySink (clipRegion.getRectangle (i), objects.sink, transform, figureMode); + + return objects.geometry; +} + +ComSmartPtr D2DHelpers::pathToPathGeometry (ID2D1Factory* factory, + const Path& path, + const AffineTransform& transform, + D2D1_FIGURE_BEGIN figureMode, + [[maybe_unused]] Direct2DMetrics* metrics) +{ + JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime) + ScopedGeometryWithSink objects { factory, path.isUsingNonZeroWinding() ? D2D1_FILL_MODE_WINDING : D2D1_FILL_MODE_ALTERNATE }; + + if (objects.sink == nullptr) + return {}; + + pathToGeometrySink (path, objects.sink, transform, figureMode); + + return objects.geometry; +} + +ComSmartPtr D2DHelpers::pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory, + const PathStrokeType& strokeType) +{ + // JUCE JointStyle ID2D1StrokeStyle + // --------------- ---------------- + // mitered D2D1_LINE_JOIN_MITER + // curved D2D1_LINE_JOIN_ROUND + // beveled D2D1_LINE_JOIN_BEVEL + // + // JUCE EndCapStyle ID2D1StrokeStyle + // ---------------- ---------------- + // butt D2D1_CAP_STYLE_FLAT + // square D2D1_CAP_STYLE_SQUARE + // rounded D2D1_CAP_STYLE_ROUND + auto lineJoin = D2D1_LINE_JOIN_MITER; + switch (strokeType.getJointStyle()) + { + case PathStrokeType::JointStyle::mitered: + // already set + break; + + case PathStrokeType::JointStyle::curved: + lineJoin = D2D1_LINE_JOIN_ROUND; + break; + + case PathStrokeType::JointStyle::beveled: + lineJoin = D2D1_LINE_JOIN_BEVEL; + break; + + default: + // invalid EndCapStyle + jassertfalse; + break; + } + + auto capStyle = D2D1_CAP_STYLE_FLAT; + switch (strokeType.getEndStyle()) + { + case PathStrokeType::EndCapStyle::butt: + // already set + break; + + case PathStrokeType::EndCapStyle::square: + capStyle = D2D1_CAP_STYLE_SQUARE; + break; + + case PathStrokeType::EndCapStyle::rounded: + capStyle = D2D1_CAP_STYLE_ROUND; + break; + + default: + // invalid EndCapStyle + jassertfalse; + break; + } + + D2D1_STROKE_STYLE_PROPERTIES1 strokeStyleProperties + { + capStyle, + capStyle, + capStyle, + lineJoin, + strokeType.getStrokeThickness(), + D2D1_DASH_STYLE_SOLID, + 0.0f, + D2D1_STROKE_TRANSFORM_TYPE_NORMAL + }; + ComSmartPtr strokeStyle; + factory->CreateStrokeStyle (strokeStyleProperties, + nullptr, + 0, + strokeStyle.resetAndGetPointerAddress()); + + return strokeStyle; +} + +//============================================================================== +void DirectWriteGlyphRun::replace (Span> positions, float scale) +{ + advances.resize (positions.size(), 0.0f); + offsets.resize (positions.size()); + std::transform (positions.begin(), positions.end(), offsets.begin(), [&] (auto& g) + { + return DWRITE_GLYPH_OFFSET { g.x / scale, -g.y }; + }); +} + +//============================================================================== +auto DxgiAdapter::create (ComSmartPtr d2dFactory, + ComSmartPtr dxgiAdapterIn) -> Ptr +{ + if (dxgiAdapterIn == nullptr || d2dFactory == nullptr) + return {}; + + Ptr result = new DxgiAdapter; + result->dxgiAdapter = dxgiAdapterIn; + + for (UINT i = 0;; ++i) + { + ComSmartPtr output; + const auto hr = result->dxgiAdapter->EnumOutputs (i, output.resetAndGetPointerAddress()); + + if (hr == DXGI_ERROR_NOT_FOUND || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) + break; + + result->dxgiOutputs.push_back (output); + } + + // This flag adds support for surfaces with a different color channel ordering + // than the API default. It is required for compatibility with Direct2D. + const auto creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT + | (enableDirectXDebugLayer ? D3D11_CREATE_DEVICE_DEBUG : 0); + + if (const auto hr = D3D11CreateDevice (result->dxgiAdapter, + D3D_DRIVER_TYPE_UNKNOWN, + nullptr, + creationFlags, + nullptr, + 0, + D3D11_SDK_VERSION, + result->direct3DDevice.resetAndGetPointerAddress(), + nullptr, + nullptr); FAILED (hr)) + { + return {}; + } + + if (const auto hr = result->direct3DDevice->QueryInterface (result->dxgiDevice.resetAndGetPointerAddress()); FAILED (hr)) + return {}; + + if (const auto hr = d2dFactory->CreateDevice (result->dxgiDevice, result->direct2DDevice.resetAndGetPointerAddress()); FAILED (hr)) + return {}; + + return result; +} + +bool DxgiAdapter::uniqueIDMatches (ReferenceCountedObjectPtr other) const +{ + if (other == nullptr) + return false; + + auto luid = getAdapterUniqueID(); + auto otherLuid = other->getAdapterUniqueID(); + return (luid.HighPart == otherLuid.HighPart) && (luid.LowPart == otherLuid.LowPart); +} + +LUID DxgiAdapter::getAdapterUniqueID() const +{ + DXGI_ADAPTER_DESC1 desc; + + if (auto hr = dxgiAdapter->GetDesc1 (&desc); SUCCEEDED (hr)) + return desc.AdapterLuid; + + return LUID { 0, 0 }; +} + +DxgiAdapters::DxgiAdapters (ComSmartPtr d2dFactoryIn) + : d2dFactory (d2dFactoryIn) +{ + updateAdapters(); +} + +DxgiAdapters::~DxgiAdapters() +{ + releaseAdapters(); +} + +void DxgiAdapters::updateAdapters() +{ + if (factory != nullptr && factory->IsCurrent() && ! adapterArray.isEmpty()) + return; + + releaseAdapters(); + + if (factory == nullptr || ! factory->IsCurrent()) + factory = makeDxgiFactory(); + + if (factory == nullptr) + { + // If you hit this, we were unable to create a DXGI Factory, so we won't be able to + // render anything using Direct2D. + // Maybe this version of Windows doesn't have Direct2D support. + jassertfalse; + return; + } + + for (UINT i = 0;; ++i) + { + ComSmartPtr dxgiAdapter; + + if (factory->EnumAdapters1 (i, dxgiAdapter.resetAndGetPointerAddress()) == DXGI_ERROR_NOT_FOUND) + break; + + if (const auto adapter = DxgiAdapter::create (d2dFactory, dxgiAdapter)) + { + adapterArray.add (adapter); + listeners.call ([adapter] (DxgiAdapterListener& l) { l.adapterCreated (adapter); }); + } + } +} + +void DxgiAdapters::releaseAdapters() +{ + for (const auto& adapter : adapterArray) + listeners.call ([adapter] (DxgiAdapterListener& l) { l.adapterRemoved (adapter); }); + + adapterArray.clear(); +} + +DxgiAdapter::Ptr DxgiAdapters::getAdapterForHwnd (HWND hwnd) const +{ + const auto monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONULL); + + if (monitor == nullptr) + return getDefaultAdapter(); + + for (auto& adapter : adapterArray) + { + for (const auto& dxgiOutput : adapter->dxgiOutputs) + { + DXGI_OUTPUT_DESC desc{}; + + if (FAILED (dxgiOutput->GetDesc (&desc))) + continue; + + if (desc.Monitor == monitor) + return adapter; + } + } + + return getDefaultAdapter(); +} + +DxgiAdapter::Ptr DxgiAdapters::getDefaultAdapter() const +{ + return adapterArray.getFirst(); +} + +void DxgiAdapters::addListener (DxgiAdapterListener& l) +{ + listeners.add (&l); +} + +void DxgiAdapters::removeListener (DxgiAdapterListener& l) +{ + listeners.remove (&l); +} + +ComSmartPtr DxgiAdapters::makeDxgiFactory() +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + ComSmartPtr result; + if (const auto hr = CreateDXGIFactory2 (0, __uuidof (IDXGIFactory2), (void**) result.resetAndGetPointerAddress()); SUCCEEDED (hr)) + return result; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + // If CreateDXGIFactory fails, check to see if this is being called in the context of DllMain. + // CreateDXGIFactory will always fail if called from the context of DllMain. In this case, the renderer + // will create a software image instead as a fallback, but that won't perform as well. + // + // You may be creating an Image as a static object, which will likely be created in the context of DllMain. + // Consider deferring your Image creation until later. + jassertfalse; + return {}; +} + +//============================================================================== +DirectX::DirectX() + : d2dSharedFactory { std::invoke ([] + { + D2D1_FACTORY_OPTIONS options; + options.debugLevel = enableDirectXDebugLayer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + ComSmartPtr result; + auto hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED, + __uuidof (ID2D1Factory2), + &options, + (void**) result.resetAndGetPointerAddress()); + jassertquiet (SUCCEEDED (hr)); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return result; + }) } +{} + +//============================================================================== +ComSmartPtr D2DUtilities::getDeviceForContext (ComSmartPtr context) +{ + if (context == nullptr) + return {}; + + ComSmartPtr device; + context->GetDevice (device.resetAndGetPointerAddress()); + return device.getInterface(); +} + +//============================================================================== +ComSmartPtr Direct2DDeviceContext::create (ComSmartPtr device) +{ + ComSmartPtr result; + + if (const auto hr = device->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, + result.resetAndGetPointerAddress()); + FAILED (hr)) + { + jassertfalse; + return {}; + } + + result->SetTextAntialiasMode (D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + result->SetAntialiasMode (D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + result->SetUnitMode (D2D1_UNIT_MODE_PIXELS); + + return result; +} + +//============================================================================== +ComSmartPtr Direct2DBitmap::toBitmap (const Image& image, + ComSmartPtr deviceContext, + Image::PixelFormat outputFormat) +{ + JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); + + jassert (outputFormat == Image::ARGB || outputFormat == Image::SingleChannel); + + JUCE_TRACE_LOG_D2D_PAINT_CALL (etw::createDirect2DBitmapFromImage, etw::graphicsKeyword); + + // Calling Image::convertedToFormat could cause unchecked recursion since convertedToFormat + // calls Graphics::drawImageAt which calls Direct2DGraphicsContext::drawImage which calls this function... + // + // Use a software image for the conversion instead so the Graphics::drawImageAt call doesn't go + // through the Direct2D renderer + // + // Be sure to explicitly set the DPI to 96.0 for the image; otherwise it will default to the screen DPI + // and may be scaled incorrectly + const auto convertedImage = SoftwareImageType{}.convert (image).convertedToFormat (outputFormat); + + if (! convertedImage.isValid()) + return {}; + + Image::BitmapData bitmapData { convertedImage, Image::BitmapData::readWrite }; + + D2D1_BITMAP_PROPERTIES1 bitmapProperties{}; + bitmapProperties.pixelFormat.format = outputFormat == Image::SingleChannel + ? DXGI_FORMAT_A8_UNORM + : DXGI_FORMAT_B8G8R8A8_UNORM; + bitmapProperties.pixelFormat.alphaMode = outputFormat == Image::RGB + ? D2D1_ALPHA_MODE_IGNORE + : D2D1_ALPHA_MODE_PREMULTIPLIED; + bitmapProperties.dpiX = USER_DEFAULT_SCREEN_DPI; + bitmapProperties.dpiY = USER_DEFAULT_SCREEN_DPI; + + const D2D1_SIZE_U size { (UINT32) image.getWidth(), (UINT32) image.getHeight() }; + + ComSmartPtr bitmap; + deviceContext->CreateBitmap (size, + bitmapData.data, + (UINT32) bitmapData.lineStride, + bitmapProperties, + bitmap.resetAndGetPointerAddress()); + return bitmap; +} + +ComSmartPtr Direct2DBitmap::createBitmap (ComSmartPtr deviceContext, + Image::PixelFormat format, + D2D_SIZE_U size, + D2D1_BITMAP_OPTIONS options) +{ + JUCE_TRACE_LOG_D2D_PAINT_CALL (etw::createDirect2DBitmap, etw::graphicsKeyword); + + JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); + + // Verify that the GPU can handle a bitmap of this size + // + // If you need a bitmap larger than this, you'll need to either split it up into multiple bitmaps + // or use a software image (see SoftwareImageType). + const auto maxBitmapSize = deviceContext->GetMaximumBitmapSize(); + jassertquiet (size.width <= maxBitmapSize && size.height <= maxBitmapSize); + + const auto pixelFormat = D2D1::PixelFormat (format == Image::SingleChannel + ? DXGI_FORMAT_A8_UNORM + : DXGI_FORMAT_B8G8R8A8_UNORM, + format == Image::RGB + ? D2D1_ALPHA_MODE_IGNORE + : D2D1_ALPHA_MODE_PREMULTIPLIED); + const auto bitmapProperties = D2D1::BitmapProperties1 (options, pixelFormat); + + ComSmartPtr bitmap; + const auto hr = deviceContext->CreateBitmap (size, + {}, + {}, + bitmapProperties, + bitmap.resetAndGetPointerAddress()); + + jassertquiet (SUCCEEDED (hr) && bitmap != nullptr); + return bitmap; +} + +//============================================================================== +void UpdateRegion::findRECTAndValidate (HWND windowHandle) +{ + numRect = 0; + + auto regionHandle = CreateRectRgn (0, 0, 0, 0); + + if (regionHandle == nullptr) + { + ValidateRect (windowHandle, nullptr); + return; + } + + auto regionType = GetUpdateRgn (windowHandle, regionHandle, false); + + if (regionType == SIMPLEREGION || regionType == COMPLEXREGION) + { + auto regionDataBytes = GetRegionData (regionHandle, (DWORD) block.getSize(), (RGNDATA*) block.getData()); + + if (regionDataBytes > block.getSize()) + { + block.ensureSize (regionDataBytes); + regionDataBytes = GetRegionData (regionHandle, (DWORD) block.getSize(), (RGNDATA*) block.getData()); + } + + if (regionDataBytes > 0) + { + auto header = (RGNDATAHEADER const* const) block.getData(); + + if (header->iType == RDH_RECTANGLES) + numRect = header->nCount; + } + } + + if (numRect > 0) + ValidateRgn (windowHandle, regionHandle); + else + ValidateRect (windowHandle, nullptr); + + DeleteObject (regionHandle); +} + +//============================================================================== +ComSmartPtr makeGradientStopCollection (const ColourGradient& gradient, + ComSmartPtr deviceContext, + [[maybe_unused]] Direct2DMetrics* metrics) noexcept +{ + JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGradientTime); + + const int numColors = gradient.getNumColours(); + + std::vector stops ((size_t) numColors); + + for (auto [index, stop] : enumerate (stops, int{})) + { + stop.color = D2DUtilities::toCOLOR_F (gradient.getColour (index)); + stop.position = (FLOAT) gradient.getColourPosition (index); + } + + ComSmartPtr result; + deviceContext->CreateGradientStopCollection (stops.data(), (UINT32) stops.size(), result.resetAndGetPointerAddress()); + return result; +} + +ComSmartPtr LinearGradientCache::get (const ColourGradient& gradient, + ComSmartPtr deviceContext, + Direct2DMetrics* metrics) +{ + jassert (! gradient.isRadial); + + return cache.get (gradient, [&deviceContext, &metrics] (const auto& key) + { + const auto gradientStops = makeGradientStopCollection (key, deviceContext, metrics); + const auto p1 = key.point1; + const auto p2 = key.point2; + const auto linearGradientBrushProperties = D2D1::LinearGradientBrushProperties ({ p1.x, p1.y }, { p2.x, p2.y }); + const D2D1_BRUSH_PROPERTIES brushProps { 1.0f, D2D1::IdentityMatrix() }; + + ComSmartPtr result; + deviceContext->CreateLinearGradientBrush (linearGradientBrushProperties, + brushProps, + gradientStops, + result.resetAndGetPointerAddress()); + return result; + }); +} + +ComSmartPtr RadialGradientCache::get (const ColourGradient& gradient, + ComSmartPtr deviceContext, + Direct2DMetrics* metrics) +{ + jassert (gradient.isRadial); + + return cache.get (gradient, [&deviceContext, &metrics] (const auto& key) + { + const auto gradientStops = makeGradientStopCollection (key, deviceContext, metrics); + + const auto p1 = key.point1; + const auto p2 = key.point2; + const auto r = p1.getDistanceFrom (p2); + const auto radialGradientBrushProperties = D2D1::RadialGradientBrushProperties ({ p1.x, p1.y }, {}, r, r); + const D2D1_BRUSH_PROPERTIES brushProps { 1.0F, D2D1::IdentityMatrix() }; + + ComSmartPtr result; + deviceContext->CreateRadialGradientBrush (radialGradientBrushProperties, + brushProps, + gradientStops, + result.resetAndGetPointerAddress()); + return result; + }); +} + +RectangleListSpriteBatch::~RectangleListSpriteBatch() +{ + release(); +} + +void RectangleListSpriteBatch::release() +{ + whiteRectangle = nullptr; + spriteBatches = {}; + destinations.free(); + destinationsCapacity = 0; +} + +ComSmartPtr RectangleListSpriteBatch::getSpriteBatch (ID2D1DeviceContext3& dc, uint32 key) +{ + return spriteBatches.get ((uint32) key, [&dc] (auto) -> ComSmartPtr + { + ComSmartPtr result; + if (const auto hr = dc.CreateSpriteBatch (result.resetAndGetPointerAddress()); SUCCEEDED (hr)) + return result; + + return nullptr; + }); +} + +//============================================================================== +DxgiAdapter::Ptr Direct2DDeviceResources::findAdapter (const DxgiAdapters& adapters, + ID2D1Bitmap1* bitmap) +{ + if (bitmap == nullptr) + return {}; + + ComSmartPtr surface; + bitmap->GetSurface (surface.resetAndGetPointerAddress()); + + if (surface == nullptr) + return {}; + + ComSmartPtr device; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + surface->GetDevice (__uuidof (device), (void**) device.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return findAdapter (adapters, device); +} + +DxgiAdapter::Ptr Direct2DDeviceResources::findAdapter (const DxgiAdapters& dxgiAdapters, + IDXGIDevice* dxgiDevice) +{ + if (dxgiDevice == nullptr) + return {}; + + ComSmartPtr adapter; + dxgiDevice->GetAdapter (adapter.resetAndGetPointerAddress()); + + if (adapter == nullptr) + return {}; + + ComSmartPtr adapter1; + adapter.QueryInterface (adapter1); + + if (adapter1 == nullptr) + return {}; + + const auto adapterLuid = getLUID (adapter1); + + const auto& adapters = dxgiAdapters.getAdapterArray(); + + const auto it = std::find_if (adapters.begin(), adapters.end(), [&] (DxgiAdapter::Ptr ptr) + { + const auto tie = [] (const LUID& x) { return std::tie (x.LowPart, x.HighPart); }; + + const auto thisLuid = getLUID (ptr->dxgiAdapter); + return tie (thisLuid) == tie (adapterLuid); + }); + + if (it == adapters.end()) + return {}; + + return *it; +} + +DxgiAdapter::Ptr Direct2DDeviceResources::findAdapter (const DxgiAdapters& dxgiAdapters, + ID2D1DeviceContext1* context) +{ + if (context == nullptr) + return {}; + + ComSmartPtr device; + context->GetDevice (device.resetAndGetPointerAddress()); + + if (device == nullptr) + return {}; + + ComSmartPtr dxgiDevice; + device.QueryInterface (dxgiDevice); + + return findAdapter (dxgiAdapters, dxgiDevice); +} + +LUID Direct2DDeviceResources::getLUID (ComSmartPtr adapter) +{ + DXGI_ADAPTER_DESC1 desc{}; + adapter->GetDesc1 (&desc); + return desc.AdapterLuid; +} + +std::optional Direct2DDeviceResources::create (ComSmartPtr context) +{ + if (context == nullptr) + return {}; + + Direct2DDeviceResources result; + + if (const auto hr = context->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), + result.colourBrush.resetAndGetPointerAddress()); + FAILED (hr)) + { + jassertfalse; + return {}; + } + + result.rectangleListSpriteBatch = std::make_unique(); + + return result; +} + +//============================================================================== +HRESULT SwapChain::create (HWND hwnd, Rectangle size, DxgiAdapter::Ptr adapter) +{ + if (chain != nullptr || hwnd == nullptr) + return S_OK; + + auto dxgiFactory = directX->adapters.getFactory(); + + if (dxgiFactory == nullptr || adapter->direct3DDevice == nullptr) + return E_FAIL; + + buffer = nullptr; + chain = nullptr; + + // Make the waitable swap chain + // Create the swap chain with premultiplied alpha support for transparent windows + DXGI_SWAP_CHAIN_DESC1 swapChainDescription = {}; + swapChainDescription.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + swapChainDescription.Width = (UINT) size.getWidth(); + swapChainDescription.Height = (UINT) size.getHeight(); + swapChainDescription.SampleDesc.Count = 1; + swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDescription.BufferCount = 2; + swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDescription.Flags = swapChainFlags; + + swapChainDescription.Scaling = DXGI_SCALING_STRETCH; + swapChainDescription.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + + if (const auto hr = dxgiFactory->CreateSwapChainForComposition (adapter->direct3DDevice, + &swapChainDescription, + nullptr, + chain.resetAndGetPointerAddress()); + FAILED (hr)) + { + return hr; + } + + // Get the waitable swap chain presentation event and set the maximum frame latency + ComSmartPtr chain2; + if (const auto hr = chain.QueryInterface (chain2); FAILED (hr)) + return hr; + + if (chain2 == nullptr) + return E_FAIL; + + swapChainEvent.emplace (chain2->GetFrameLatencyWaitableObject()); + if (swapChainEvent->getHandle() == INVALID_HANDLE_VALUE) + return E_NOINTERFACE; + + chain2->SetMaximumFrameLatency (1); + + createBuffer (adapter); + return buffer != nullptr ? S_OK : E_FAIL; +} + +bool SwapChain::canPaint() const +{ + return chain != nullptr && buffer != nullptr; +} + +HRESULT SwapChain::resize (Rectangle newSize) +{ + if (chain == nullptr) + return E_FAIL; + + constexpr auto minFrameSize = 1; + constexpr auto maxFrameSize = 16384; + + auto scaledSize = newSize.getUnion ({ minFrameSize, minFrameSize }) + .getIntersection ({ maxFrameSize, maxFrameSize }); + + buffer = nullptr; + + if (const auto hr = chain->ResizeBuffers (0, (UINT) scaledSize.getWidth(), (UINT) scaledSize.getHeight(), DXGI_FORMAT_B8G8R8A8_UNORM, swapChainFlags); FAILED (hr)) + return hr; + + ComSmartPtr device; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + chain->GetDevice (__uuidof (device), (void**) device.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + createBuffer (Direct2DDeviceResources::findAdapter (directX->adapters, device)); + + return buffer != nullptr ? S_OK : E_FAIL; +} + +Rectangle SwapChain::getSize() const +{ + const auto surface = getSurface(); + + if (surface == nullptr) + return {}; + + DXGI_SURFACE_DESC desc{}; + if (FAILED (surface->GetDesc (&desc))) + return {}; + + return { (int) desc.Width, (int) desc.Height }; +} + +WindowsScopedEvent* SwapChain::getEvent() +{ + if (swapChainEvent.has_value()) + return &*swapChainEvent; + + return nullptr; +} + +ComSmartPtr SwapChain::getSurface() const +{ + if (chain == nullptr) + return nullptr; + + ComSmartPtr surface; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + if (const auto hr = chain->GetBuffer (0, __uuidof (surface), reinterpret_cast (surface.resetAndGetPointerAddress())); FAILED (hr)) + return nullptr; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return surface; +} + +void SwapChain::createBuffer (DxgiAdapter::Ptr adapter) +{ + buffer = nullptr; + + const auto deviceContext = Direct2DDeviceContext::create (adapter); + + if (deviceContext == nullptr) + return; + + const auto surface = getSurface(); + + if (surface == nullptr) + return; + + D2D1_BITMAP_PROPERTIES1 bitmapProperties{}; + bitmapProperties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; + bitmapProperties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + bitmapProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + + deviceContext->CreateBitmapFromDxgiSurface (surface, bitmapProperties, buffer.resetAndGetPointerAddress()); +} + +//============================================================================== +std::optional CompositionTree::create (IDXGIDevice* dxgiDevice, + HWND hwnd, + IDXGISwapChain1* swapChain) +{ + if (dxgiDevice == nullptr) + return {}; + + CompositionTree result; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + if (const auto hr = DCompositionCreateDevice (dxgiDevice, + __uuidof (IDCompositionDevice), + reinterpret_cast (result.compositionDevice.resetAndGetPointerAddress())); + FAILED (hr)) + { + return {}; + } + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + if (const auto hr = result.compositionDevice->CreateTargetForHwnd (hwnd, FALSE, result.compositionTarget.resetAndGetPointerAddress()); FAILED (hr)) + return {}; + if (const auto hr = result.compositionDevice->CreateVisual (result.compositionVisual.resetAndGetPointerAddress()); FAILED (hr)) + return {}; + if (const auto hr = result.compositionTarget->SetRoot (result.compositionVisual); FAILED (hr)) + return {}; + if (const auto hr = result.compositionVisual->SetContent (swapChain); FAILED (hr)) + return {}; + if (const auto hr = result.compositionDevice->Commit(); FAILED (hr)) + return {}; + + return result; +} + +//============================================================================== +String getLocalisedName (IDWriteLocalizedStrings* names) +{ + jassert (names != nullptr); + + uint32 index = 0; + BOOL exists = false; + [[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists); + + if (! exists) + index = 0; + + uint32 length = 0; + hr = names->GetStringLength (index, &length); + + HeapBlock name (length + 1); + hr = names->GetString (index, name, length + 1); + + return static_cast (name); +} + +String getFontFamilyName (IDWriteFontFamily* family) +{ + jassert (family != nullptr); + ComSmartPtr familyNames; + auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); + jassertquiet (SUCCEEDED (hr)); + return getLocalisedName (familyNames); +} + +String getFontFaceName (IDWriteFont* font) +{ + jassert (font != nullptr); + ComSmartPtr faceNames; + auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); + jassertquiet (SUCCEEDED (hr)); + return getLocalisedName (faceNames); +} + +//============================================================================== +AggregateFontCollection::AggregateFontCollection (ComSmartPtr baseCollection) + : collections { std::move (baseCollection) } {} + +StringArray AggregateFontCollection::findAllTypefaceNames() +{ + const std::scoped_lock lock { mutex }; + + std::set strings; + + for (const auto& collection : collections) + { + const auto count = collection->GetFontFamilyCount(); + + for (auto i = decltype (count){}; i < count; ++i) + { + ComSmartPtr family; + + if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr) + continue; + + strings.insert (getFontFamilyName (family)); + } + } + + return stringArrayFromRange (strings); +} + +std::vector> AggregateFontCollection::getAllFontsInFamily (IDWriteFontFamily* fontFamily) +{ + const auto fontFacesCount = fontFamily->GetFontCount(); + std::vector> result; + result.reserve (fontFacesCount); + + for (UINT32 i = 0; i < fontFacesCount; ++i) + { + ComSmartPtr dwFont; + + if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()))) + continue; + + if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) + continue; + + result.push_back (dwFont); + } + + return result; +} + +StringArray AggregateFontCollection::findAllTypefaceStyles (const String& family) +{ + const std::scoped_lock lock { mutex }; + + for (const auto& collection : collections) + { + BOOL fontFound = false; + uint32 fontIndex = 0; + + if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound) + continue; + + ComSmartPtr fontFamily; + + if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr) + continue; + + std::set uniqueResults; + StringArray orderedResults; + + for (const auto& font : getAllFontsInFamily (fontFamily)) + { + const auto name = getFontFaceName (font); + + if (uniqueResults.insert (name).second) + orderedResults.add (name); + } + + return orderedResults; + } + + return {}; +} + +ComSmartPtr AggregateFontCollection::getFamilyByName (const wchar_t* name) +{ + const std::scoped_lock lock { mutex }; + + for (const auto& collection : collections) + { + const auto fontIndex = [&] + { + BOOL found = false; + UINT32 index = 0; + + return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found) + ? index + : (UINT32) -1; + }(); + + if (fontIndex == (UINT32) -1) + continue; + + ComSmartPtr family; + + if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr) + continue; + + return family; + } + + return {}; +} + +ComSmartPtr AggregateFontCollection::findFontForFace (IDWriteFontFace* face) +{ + for (const auto& collection : collections) + { + ComSmartPtr result; + + if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress()))) + return result; + } + + return {}; +} + +void AggregateFontCollection::addCollection (ComSmartPtr collection) +{ + const std::scoped_lock lock { mutex }; + collections.push_back (std::move (collection)); +} + +void AggregateFontCollection::removeCollection (ComSmartPtr collection) +{ + const std::scoped_lock lock { mutex }; + const auto iter = std::find (collections.begin(), collections.end(), collection); + + if (iter != collections.end()) + collections.erase (iter); +} + +auto AggregateFontCollection::mapCharacters (IDWriteFontFallback* fallback, + IDWriteTextAnalysisSource* analysisSource, + UINT32 textPosition, + UINT32 textLength, + wchar_t const* baseFamilyName, + DWRITE_FONT_WEIGHT baseWeight, + DWRITE_FONT_STYLE baseStyle, + DWRITE_FONT_STRETCH baseStretch) noexcept -> MapResult +{ + const std::scoped_lock lock { mutex }; + + // For reasons I don't understand, the system may pick better substitutions when passing + // nullptr, instead of the system collection, as the "default collection to use". + auto collectionsToCheck = collections; + collectionsToCheck.insert (collectionsToCheck.begin(), nullptr); + + MapResult bestMatch; + for (const auto& collection : collectionsToCheck) + { + MapResult result; + const auto status = fallback->MapCharacters (analysisSource, + textPosition, + textLength, + collection, + baseFamilyName, + baseWeight, + baseStyle, + baseStretch, + &result.length, + result.font.resetAndGetPointerAddress(), + &result.scale); + + if (FAILED (status) || result.font == nullptr) + continue; + + if (result.length == textLength) + return result; + + if (result.length >= bestMatch.length) + bestMatch = result; + } + + return bestMatch; +} + +//============================================================================== +MemoryFontFileStream::MemoryFontFileStream (std::shared_ptr blockIn) + : block (std::move (blockIn)) +{ +} + +JUCE_COMRESULT MemoryFontFileStream::GetFileSize (UINT64* fileSize) noexcept +{ + *fileSize = block->getSize(); + return S_OK; +} + +JUCE_COMRESULT MemoryFontFileStream::GetLastWriteTime (UINT64* lastWriteTime) noexcept +{ + *lastWriteTime = 0; + return S_OK; +} + +JUCE_COMRESULT MemoryFontFileStream::ReadFileFragment (const void** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + void** fragmentContext) noexcept +{ + if (fileOffset + fragmentSize > block->getSize()) + { + *fragmentStart = nullptr; + *fragmentContext = nullptr; + return E_INVALIDARG; + } + + *fragmentStart = addBytesToPointer (block->getData(), fileOffset); + *fragmentContext = this; + return S_OK; +} + +//============================================================================== +MemoryFontFileLoader::MemoryFontFileLoader (MemoryBlock blob) + : block (std::make_shared (std::move (blob))) +{ +} + +HRESULT WINAPI MemoryFontFileLoader::CreateStreamFromKey (const void* fontFileReferenceKey, + UINT32 keySize, + IDWriteFontFileStream** fontFileStream) noexcept +{ + if (keySize != Uuid::size()) + return E_INVALIDARG; + + Uuid requestedKey { static_cast (fontFileReferenceKey) }; + + if (requestedKey == uuid) + { + *fontFileStream = new MemoryFontFileStream { block }; + return S_OK; + } + + return E_INVALIDARG; +} + +//============================================================================== +FontFileEnumerator::FontFileEnumerator (IDWriteFactory& factoryIn, + ComSmartPtr loaderIn) + : factory (factoryIn), loader (loaderIn) {} + +HRESULT WINAPI FontFileEnumerator::GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept +{ + *fontFile = nullptr; + + if (! isPositiveAndBelow (rawDataIndex, 1)) + return E_FAIL; + + const auto uuid = loader->getUuid(); + return factory.CreateCustomFontFileReference (uuid.getRawData(), + (UINT32) uuid.size(), + loader, + fontFile); +} + +HRESULT WINAPI FontFileEnumerator::MoveNext (BOOL* hasCurrentFile) noexcept +{ + ++rawDataIndex; + *hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE; + return S_OK; +} + +//============================================================================== +DirectWriteCustomFontCollectionLoader::DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn) + : factory (factoryIn) +{ +} + +DirectWriteCustomFontCollectionLoader::~DirectWriteCustomFontCollectionLoader() +{ + for (const auto& loader : fileLoaders) + factory.UnregisterFontFileLoader (loader); +} + +Uuid DirectWriteCustomFontCollectionLoader::addRawFontData (Span blob) +{ + const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } }); + + factory.RegisterFontFileLoader (loader); + + fileLoaders.push_back (loader); + + return fileLoaders.back()->getUuid(); +} + +HRESULT WINAPI DirectWriteCustomFontCollectionLoader::CreateEnumeratorFromKey (IDWriteFactory* factoryIn, + const void* collectionKey, + UINT32 collectionKeySize, + IDWriteFontFileEnumerator** fontFileEnumerator) noexcept +{ + if (collectionKeySize != Uuid::size()) + return E_INVALIDARG; + + const Uuid requestedCollectionKey { static_cast (collectionKey) }; + + for (const auto& loader : fileLoaders) + { + if (loader->getUuid() != requestedCollectionKey) + continue; + + *fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader }; + return S_OK; + } + + return E_INVALIDARG; +} + +//============================================================================== +Direct2DFactories::Direct2DFactories() + : d2dFactory { std::invoke ([&]() -> ComSmartPtr + { + JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, + D2D1CreateFactory, + d2d1CreateFactory, + HRESULT, + (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) + + if (d2d1CreateFactory == nullptr) + return {}; + + D2D1_FACTORY_OPTIONS options; + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + + ComSmartPtr result; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof (ID2D1Factory), + &options, + (void**) result.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return result; + }) }, + directWriteFactory { std::invoke ([&]() -> ComSmartPtr + { + JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, + DWriteCreateFactory, + dWriteCreateFactory, + HRESULT, + (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) + + if (dWriteCreateFactory == nullptr) + return {}; + + ComSmartPtr result; + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") + dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, + __uuidof (IDWriteFactory), + (IUnknown**) result.resetAndGetPointerAddress()); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + return result; + }) }, + collectionLoader { std::invoke ([&]() -> ComSmartPtr + { + auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory }); + directWriteFactory->RegisterFontCollectionLoader (result); + + return result; + }) } +{ + ComSmartPtr collection; + + if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr) + fonts.emplace (collection); + else + jassertfalse; +} + +Direct2DFactories::~Direct2DFactories() +{ + if (directWriteFactory == nullptr) + return; + + directWriteFactory->UnregisterFontCollectionLoader (collectionLoader); +} + +} // namespace juce diff --git a/modules/juce_graphics/native/juce_DirectX_windows.h b/modules/juce_graphics/native/juce_DirectX_windows.h index 67aca6a6f8..e61b890980 100644 --- a/modules/juce_graphics/native/juce_DirectX_windows.h +++ b/modules/juce_graphics/native/juce_DirectX_windows.h @@ -35,21 +35,12 @@ namespace juce { -constexpr auto enableDirectXDebugLayer = false; - class ScopedMultithread { public: - explicit ScopedMultithread (ID2D1Multithread* multithreadIn) - : multithread (addComSmartPtrOwner (multithreadIn)) - { - multithreadIn->Enter(); - } + explicit ScopedMultithread (ID2D1Multithread* multithreadIn); - ~ScopedMultithread() - { - multithread->Leave(); - } + ~ScopedMultithread(); private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedMultithread) @@ -60,25 +51,9 @@ private: /* ScopedGeometryWithSink creates an ID2D1PathGeometry object with an open sink. */ struct ScopedGeometryWithSink { - ScopedGeometryWithSink (ID2D1Factory* factory, D2D1_FILL_MODE fillMode) - { - if (const auto hr = factory->CreatePathGeometry (geometry.resetAndGetPointerAddress()); FAILED (hr)) - return; + ScopedGeometryWithSink (ID2D1Factory* factory, D2D1_FILL_MODE fillMode); - if (const auto hr = geometry->Open (sink.resetAndGetPointerAddress()); FAILED (hr)) - return; - - sink->SetFillMode (fillMode); - } - - ~ScopedGeometryWithSink() - { - if (sink == nullptr) - return; - - const auto hr = sink->Close(); - jassertquiet (SUCCEEDED (hr)); - } + ~ScopedGeometryWithSink(); ComSmartPtr geometry; ComSmartPtr sink; @@ -111,232 +86,35 @@ private: //============================================================================== struct D2DHelpers { - static bool isTransformAxisAligned (const AffineTransform& transform) - { - return transform.mat01 == 0.0f && transform.mat10 == 0.0f; - } + static bool isTransformAxisAligned (const AffineTransform& transform); static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform, - D2D1_FIGURE_BEGIN figureMode) - { - class ScopedFigure - { - public: - ScopedFigure (ID2D1GeometrySink* s, D2D1_POINT_2F pt, D2D1_FIGURE_BEGIN mode) - : sink (s) - { - sink->BeginFigure (pt, mode); - } + D2D1_FIGURE_BEGIN figureMode); - ~ScopedFigure() - { - if (sink != nullptr) - sink->EndFigure (end); - } - - void setClosed() - { - end = D2D1_FIGURE_END_CLOSED; - } - - private: - ID2D1GeometrySink* sink = nullptr; - D2D1_FIGURE_END end = D2D1_FIGURE_END_OPEN; - - JUCE_DECLARE_NON_COPYABLE (ScopedFigure) - JUCE_DECLARE_NON_MOVEABLE (ScopedFigure) - }; - - // Every call to BeginFigure must have a matching call to EndFigure. But - the Path does not necessarily - // have matching startNewSubPath and closePath markers. - D2D1_POINT_2F lastLocation{}; - std::optional figure; - Path::Iterator it (path); - - const auto doTransform = [&transform] (float x, float y) - { - transform.transformPoint (x, y); - return D2D1_POINT_2F { x, y }; - }; - - const auto updateFigure = [&] (float x, float y) - { - if (! figure.has_value()) - figure.emplace (sink, lastLocation, figureMode); - - lastLocation = doTransform (x, y); - }; - - while (it.next()) - { - switch (it.elementType) - { - case Path::Iterator::lineTo: - updateFigure (it.x1, it.y1); - sink->AddLine (lastLocation); - break; - - case Path::Iterator::quadraticTo: - updateFigure (it.x2, it.y2); - sink->AddQuadraticBezier ({ doTransform (it.x1, it.y1), lastLocation }); - break; - - case Path::Iterator::cubicTo: - updateFigure (it.x3, it.y3); - sink->AddBezier ({ doTransform (it.x1, it.y1), doTransform (it.x2, it.y2), lastLocation }); - break; - - case Path::Iterator::closePath: - if (figure.has_value()) - figure->setClosed(); - - figure.reset(); - break; - - case Path::Iterator::startNewSubPath: - figure.reset(); - lastLocation = doTransform (it.x1, it.y1); - figure.emplace (sink, lastLocation, figureMode); - break; - } - } - } - - static D2D1_POINT_2F pointTransformed (Point pt, const AffineTransform& transform) - { - transform.transformPoint (pt.x, pt.y); - return { (FLOAT) pt.x, (FLOAT) pt.y }; - } + static D2D1_POINT_2F pointTransformed (Point pt, const AffineTransform& transform); static void rectToGeometrySink (const Rectangle& rect, ID2D1GeometrySink* sink, const AffineTransform& transform, - D2D1_FIGURE_BEGIN figureMode) - { - const auto a = pointTransformed (rect.getTopLeft(), transform); - const auto b = pointTransformed (rect.getTopRight(), transform); - const auto c = pointTransformed (rect.getBottomRight(), transform); - const auto d = pointTransformed (rect.getBottomLeft(), transform); - - sink->BeginFigure (a, figureMode); - sink->AddLine (b); - sink->AddLine (c); - sink->AddLine (d); - sink->EndFigure (D2D1_FIGURE_END_CLOSED); - } + D2D1_FIGURE_BEGIN figureMode); static ComSmartPtr rectListToPathGeometry (ID2D1Factory* factory, const RectangleList& clipRegion, const AffineTransform& transform, D2D1_FILL_MODE fillMode, D2D1_FIGURE_BEGIN figureMode, - [[maybe_unused]] Direct2DMetrics* metrics) - { - JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime) - ScopedGeometryWithSink objects { factory, fillMode }; - - if (objects.sink == nullptr) - return {}; - - for (int i = clipRegion.getNumRectangles(); --i >= 0;) - rectToGeometrySink (clipRegion.getRectangle (i), objects.sink, transform, figureMode); - - return objects.geometry; - } + [[maybe_unused]] Direct2DMetrics* metrics); static ComSmartPtr pathToPathGeometry (ID2D1Factory* factory, const Path& path, const AffineTransform& transform, D2D1_FIGURE_BEGIN figureMode, - [[maybe_unused]] Direct2DMetrics* metrics) - { - JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime) - ScopedGeometryWithSink objects { factory, path.isUsingNonZeroWinding() ? D2D1_FILL_MODE_WINDING : D2D1_FILL_MODE_ALTERNATE }; + [[maybe_unused]] Direct2DMetrics* metrics); - if (objects.sink == nullptr) - return {}; - - pathToGeometrySink (path, objects.sink, transform, figureMode); - - return objects.geometry; - } - - static ComSmartPtr pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory, const PathStrokeType& strokeType) - { - // JUCE JointStyle ID2D1StrokeStyle - // --------------- ---------------- - // mitered D2D1_LINE_JOIN_MITER - // curved D2D1_LINE_JOIN_ROUND - // beveled D2D1_LINE_JOIN_BEVEL - // - // JUCE EndCapStyle ID2D1StrokeStyle - // ---------------- ---------------- - // butt D2D1_CAP_STYLE_FLAT - // square D2D1_CAP_STYLE_SQUARE - // rounded D2D1_CAP_STYLE_ROUND - auto lineJoin = D2D1_LINE_JOIN_MITER; - switch (strokeType.getJointStyle()) - { - case PathStrokeType::JointStyle::mitered: - // already set - break; - - case PathStrokeType::JointStyle::curved: - lineJoin = D2D1_LINE_JOIN_ROUND; - break; - - case PathStrokeType::JointStyle::beveled: - lineJoin = D2D1_LINE_JOIN_BEVEL; - break; - - default: - // invalid EndCapStyle - jassertfalse; - break; - } - - auto capStyle = D2D1_CAP_STYLE_FLAT; - switch (strokeType.getEndStyle()) - { - case PathStrokeType::EndCapStyle::butt: - // already set - break; - - case PathStrokeType::EndCapStyle::square: - capStyle = D2D1_CAP_STYLE_SQUARE; - break; - - case PathStrokeType::EndCapStyle::rounded: - capStyle = D2D1_CAP_STYLE_ROUND; - break; - - default: - // invalid EndCapStyle - jassertfalse; - break; - } - - D2D1_STROKE_STYLE_PROPERTIES1 strokeStyleProperties - { - capStyle, - capStyle, - capStyle, - lineJoin, - strokeType.getStrokeThickness(), - D2D1_DASH_STYLE_SOLID, - 0.0f, - D2D1_STROKE_TRANSFORM_TYPE_NORMAL - }; - ComSmartPtr strokeStyle; - factory->CreateStrokeStyle (strokeStyleProperties, - nullptr, - 0, - strokeStyle.resetAndGetPointerAddress()); - - return strokeStyle; - } + static ComSmartPtr pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory, + const PathStrokeType& strokeType); }; @@ -345,15 +123,7 @@ struct D2DHelpers class DirectWriteGlyphRun { public: - void replace (Span> positions, float scale) - { - advances.resize (positions.size(), 0.0f); - offsets.resize (positions.size()); - std::transform (positions.begin(), positions.end(), offsets.begin(), [&] (auto& g) - { - return DWRITE_GLYPH_OFFSET { g.x / scale, -g.y }; - }); - } + void replace (Span> positions, float scale); auto* getAdvances() const { return advances.data(); } auto* getOffsets() const { return offsets .data(); } @@ -367,72 +137,11 @@ struct DxgiAdapter : public ReferenceCountedObject { using Ptr = ReferenceCountedObjectPtr; - static Ptr create (ComSmartPtr d2dFactory, ComSmartPtr dxgiAdapterIn) - { - if (dxgiAdapterIn == nullptr || d2dFactory == nullptr) - return {}; + static Ptr create (ComSmartPtr, ComSmartPtr); - Ptr result = new DxgiAdapter; - result->dxgiAdapter = dxgiAdapterIn; + bool uniqueIDMatches (ReferenceCountedObjectPtr other) const; - for (UINT i = 0;; ++i) - { - ComSmartPtr output; - const auto hr = result->dxgiAdapter->EnumOutputs (i, output.resetAndGetPointerAddress()); - - if (hr == DXGI_ERROR_NOT_FOUND || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) - break; - - result->dxgiOutputs.push_back (output); - } - - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - const auto creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT - | (enableDirectXDebugLayer ? D3D11_CREATE_DEVICE_DEBUG : 0); - - if (const auto hr = D3D11CreateDevice (result->dxgiAdapter, - D3D_DRIVER_TYPE_UNKNOWN, - nullptr, - creationFlags, - nullptr, - 0, - D3D11_SDK_VERSION, - result->direct3DDevice.resetAndGetPointerAddress(), - nullptr, - nullptr); FAILED (hr)) - { - return {}; - } - - if (const auto hr = result->direct3DDevice->QueryInterface (result->dxgiDevice.resetAndGetPointerAddress()); FAILED (hr)) - return {}; - - if (const auto hr = d2dFactory->CreateDevice (result->dxgiDevice, result->direct2DDevice.resetAndGetPointerAddress()); FAILED (hr)) - return {}; - - return result; - } - - bool uniqueIDMatches (ReferenceCountedObjectPtr other) const - { - if (other == nullptr) - return false; - - auto luid = getAdapterUniqueID(); - auto otherLuid = other->getAdapterUniqueID(); - return (luid.HighPart == otherLuid.HighPart) && (luid.LowPart == otherLuid.LowPart); - } - - LUID getAdapterUniqueID() const - { - DXGI_ADAPTER_DESC1 desc; - - if (auto hr = dxgiAdapter->GetDesc1 (&desc); SUCCEEDED (hr)) - return desc.AdapterLuid; - - return LUID { 0, 0 }; - } + LUID getAdapterUniqueID() const; ComSmartPtr direct3DDevice; ComSmartPtr dxgiDevice; @@ -454,58 +163,13 @@ struct DxgiAdapterListener class DxgiAdapters { public: - explicit DxgiAdapters (ComSmartPtr d2dFactoryIn) - : d2dFactory (d2dFactoryIn) - { - updateAdapters(); - } + explicit DxgiAdapters (ComSmartPtr d2dFactoryIn); - ~DxgiAdapters() - { - releaseAdapters(); - } + ~DxgiAdapters(); - void updateAdapters() - { - if (factory != nullptr && factory->IsCurrent() && ! adapterArray.isEmpty()) - return; + void updateAdapters(); - releaseAdapters(); - - if (factory == nullptr || ! factory->IsCurrent()) - factory = makeDxgiFactory(); - - if (factory == nullptr) - { - // If you hit this, we were unable to create a DXGI Factory, so we won't be able to - // render anything using Direct2D. - // Maybe this version of Windows doesn't have Direct2D support. - jassertfalse; - return; - } - - for (UINT i = 0;; ++i) - { - ComSmartPtr dxgiAdapter; - - if (factory->EnumAdapters1 (i, dxgiAdapter.resetAndGetPointerAddress()) == DXGI_ERROR_NOT_FOUND) - break; - - if (const auto adapter = DxgiAdapter::create (d2dFactory, dxgiAdapter)) - { - adapterArray.add (adapter); - listeners.call ([adapter] (DxgiAdapterListener& l) { l.adapterCreated (adapter); }); - } - } - } - - void releaseAdapters() - { - for (const auto& adapter : adapterArray) - listeners.call ([adapter] (DxgiAdapterListener& l) { l.adapterRemoved (adapter); }); - - adapterArray.clear(); - } + void releaseAdapters(); const auto& getAdapterArray() const { @@ -517,63 +181,15 @@ public: return factory; } - DxgiAdapter::Ptr getAdapterForHwnd (HWND hwnd) const - { - const auto monitor = MonitorFromWindow (hwnd, MONITOR_DEFAULTTONULL); + DxgiAdapter::Ptr getAdapterForHwnd (HWND hwnd) const; - if (monitor == nullptr) - return getDefaultAdapter(); + DxgiAdapter::Ptr getDefaultAdapter() const; - for (auto& adapter : adapterArray) - { - for (const auto& dxgiOutput : adapter->dxgiOutputs) - { - DXGI_OUTPUT_DESC desc{}; - - if (FAILED (dxgiOutput->GetDesc (&desc))) - continue; - - if (desc.Monitor == monitor) - return adapter; - } - } - - return getDefaultAdapter(); - } - - DxgiAdapter::Ptr getDefaultAdapter() const - { - return adapterArray.getFirst(); - } - - void addListener (DxgiAdapterListener& l) - { - listeners.add (&l); - } - - void removeListener (DxgiAdapterListener& l) - { - listeners.remove (&l); - } + void addListener (DxgiAdapterListener& l); + void removeListener (DxgiAdapterListener& l); private: - static ComSmartPtr makeDxgiFactory() - { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - ComSmartPtr result; - if (const auto hr = CreateDXGIFactory2 (0, __uuidof (IDXGIFactory2), (void**) result.resetAndGetPointerAddress()); SUCCEEDED (hr)) - return result; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - // If CreateDXGIFactory fails, check to see if this is being called in the context of DllMain. - // CreateDXGIFactory will always fail if called from the context of DllMain. In this case, the renderer - // will create a software image instead as a fallback, but that won't perform as well. - // - // You may be creating an Image as a static object, which will likely be created in the context of DllMain. - // Consider deferring your Image creation until later. - jassertfalse; - return {}; - } + static ComSmartPtr makeDxgiFactory(); ComSmartPtr d2dFactory; @@ -587,26 +203,12 @@ private: class DirectX { public: - DirectX() = default; + DirectX(); auto getD2DFactory() const { return d2dSharedFactory; } private: - ComSmartPtr d2dSharedFactory = [&] - { - D2D1_FACTORY_OPTIONS options; - options.debugLevel = enableDirectXDebugLayer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - ComSmartPtr result; - auto hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED, - __uuidof (ID2D1Factory2), - &options, - (void**) result.resetAndGetPointerAddress()); - jassertquiet (SUCCEEDED (hr)); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return result; - }(); + ComSmartPtr d2dSharedFactory; public: DxgiAdapters adapters { d2dSharedFactory }; @@ -665,38 +267,13 @@ struct D2DUtilities return { (int) s.width, (int) s.height }; } - static ComSmartPtr getDeviceForContext (ComSmartPtr context) - { - if (context == nullptr) - return {}; - - ComSmartPtr device; - context->GetDevice (device.resetAndGetPointerAddress()); - return device.getInterface(); - } + static ComSmartPtr getDeviceForContext (ComSmartPtr context); }; //============================================================================== struct Direct2DDeviceContext { - static ComSmartPtr create (ComSmartPtr device) - { - ComSmartPtr result; - - if (const auto hr = device->CreateDeviceContext (D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, - result.resetAndGetPointerAddress()); - FAILED (hr)) - { - jassertfalse; - return {}; - } - - result->SetTextAntialiasMode (D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); - result->SetAntialiasMode (D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - result->SetUnitMode (D2D1_UNIT_MODE_PIXELS); - - return result; - } + static ComSmartPtr create (ComSmartPtr device); static ComSmartPtr create (DxgiAdapter::Ptr adapter) { @@ -713,84 +290,12 @@ struct Direct2DBitmap static ComSmartPtr toBitmap (const Image& image, ComSmartPtr deviceContext, - Image::PixelFormat outputFormat) - { - JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); - - jassert (outputFormat == Image::ARGB || outputFormat == Image::SingleChannel); - - JUCE_TRACE_LOG_D2D_PAINT_CALL (etw::createDirect2DBitmapFromImage, etw::graphicsKeyword); - - // Calling Image::convertedToFormat could cause unchecked recursion since convertedToFormat - // calls Graphics::drawImageAt which calls Direct2DGraphicsContext::drawImage which calls this function... - // - // Use a software image for the conversion instead so the Graphics::drawImageAt call doesn't go - // through the Direct2D renderer - // - // Be sure to explicitly set the DPI to 96.0 for the image; otherwise it will default to the screen DPI - // and may be scaled incorrectly - const auto convertedImage = SoftwareImageType{}.convert (image).convertedToFormat (outputFormat); - - if (! convertedImage.isValid()) - return {}; - - Image::BitmapData bitmapData { convertedImage, Image::BitmapData::readWrite }; - - D2D1_BITMAP_PROPERTIES1 bitmapProperties{}; - bitmapProperties.pixelFormat.format = outputFormat == Image::SingleChannel - ? DXGI_FORMAT_A8_UNORM - : DXGI_FORMAT_B8G8R8A8_UNORM; - bitmapProperties.pixelFormat.alphaMode = outputFormat == Image::RGB - ? D2D1_ALPHA_MODE_IGNORE - : D2D1_ALPHA_MODE_PREMULTIPLIED; - bitmapProperties.dpiX = USER_DEFAULT_SCREEN_DPI; - bitmapProperties.dpiY = USER_DEFAULT_SCREEN_DPI; - - const D2D1_SIZE_U size { (UINT32) image.getWidth(), (UINT32) image.getHeight() }; - - ComSmartPtr bitmap; - deviceContext->CreateBitmap (size, - bitmapData.data, - (UINT32) bitmapData.lineStride, - bitmapProperties, - bitmap.resetAndGetPointerAddress()); - return bitmap; - } + Image::PixelFormat outputFormat); static ComSmartPtr createBitmap (ComSmartPtr deviceContext, Image::PixelFormat format, D2D_SIZE_U size, - D2D1_BITMAP_OPTIONS options) - { - JUCE_TRACE_LOG_D2D_PAINT_CALL (etw::createDirect2DBitmap, etw::graphicsKeyword); - - JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime); - - // Verify that the GPU can handle a bitmap of this size - // - // If you need a bitmap larger than this, you'll need to either split it up into multiple bitmaps - // or use a software image (see SoftwareImageType). - const auto maxBitmapSize = deviceContext->GetMaximumBitmapSize(); - jassertquiet (size.width <= maxBitmapSize && size.height <= maxBitmapSize); - - const auto pixelFormat = D2D1::PixelFormat (format == Image::SingleChannel - ? DXGI_FORMAT_A8_UNORM - : DXGI_FORMAT_B8G8R8A8_UNORM, - format == Image::RGB - ? D2D1_ALPHA_MODE_IGNORE - : D2D1_ALPHA_MODE_PREMULTIPLIED); - const auto bitmapProperties = D2D1::BitmapProperties1 (options, pixelFormat); - - ComSmartPtr bitmap; - const auto hr = deviceContext->CreateBitmap (size, - {}, - {}, - bitmapProperties, - bitmap.resetAndGetPointerAddress()); - - jassertquiet (SUCCEEDED (hr) && bitmap != nullptr); - return bitmap; - } + D2D1_BITMAP_OPTIONS options); }; //============================================================================== @@ -803,46 +308,7 @@ struct Direct2DBitmap class UpdateRegion { public: - void findRECTAndValidate (HWND windowHandle) - { - numRect = 0; - - auto regionHandle = CreateRectRgn (0, 0, 0, 0); - - if (regionHandle == nullptr) - { - ValidateRect (windowHandle, nullptr); - return; - } - - auto regionType = GetUpdateRgn (windowHandle, regionHandle, false); - - if (regionType == SIMPLEREGION || regionType == COMPLEXREGION) - { - auto regionDataBytes = GetRegionData (regionHandle, (DWORD) block.getSize(), (RGNDATA*) block.getData()); - - if (regionDataBytes > block.getSize()) - { - block.ensureSize (regionDataBytes); - regionDataBytes = GetRegionData (regionHandle, (DWORD) block.getSize(), (RGNDATA*) block.getData()); - } - - if (regionDataBytes > 0) - { - auto header = (RGNDATAHEADER const* const) block.getData(); - - if (header->iType == RDH_RECTANGLES) - numRect = header->nCount; - } - } - - if (numRect > 0) - ValidateRgn (windowHandle, regionHandle); - else - ValidateRect (windowHandle, nullptr); - - DeleteObject (regionHandle); - } + void findRECTAndValidate (HWND windowHandle); void clear() { @@ -861,52 +327,16 @@ private: uint32 numRect = 0; }; -static ComSmartPtr makeGradientStopCollection (const ColourGradient& gradient, - ComSmartPtr deviceContext, - [[maybe_unused]] Direct2DMetrics* metrics) noexcept -{ - JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGradientTime); - - const int numColors = gradient.getNumColours(); - - std::vector stops ((size_t) numColors); - - for (auto [index, stop] : enumerate (stops, int{})) - { - stop.color = D2DUtilities::toCOLOR_F (gradient.getColour (index)); - stop.position = (FLOAT) gradient.getColourPosition (index); - } - - ComSmartPtr result; - deviceContext->CreateGradientStopCollection (stops.data(), (UINT32) stops.size(), result.resetAndGetPointerAddress()); - return result; -} +ComSmartPtr makeGradientStopCollection (const ColourGradient& gradient, + ComSmartPtr deviceContext, + [[maybe_unused]] Direct2DMetrics* metrics) noexcept; class LinearGradientCache { public: ComSmartPtr get (const ColourGradient& gradient, ComSmartPtr deviceContext, - Direct2DMetrics* metrics) - { - jassert (! gradient.isRadial); - - return cache.get (gradient, [&deviceContext, &metrics] (const auto& key) - { - const auto gradientStops = makeGradientStopCollection (key, deviceContext, metrics); - const auto p1 = key.point1; - const auto p2 = key.point2; - const auto linearGradientBrushProperties = D2D1::LinearGradientBrushProperties ({ p1.x, p1.y }, { p2.x, p2.y }); - const D2D1_BRUSH_PROPERTIES brushProps { 1.0f, D2D1::IdentityMatrix() }; - - ComSmartPtr result; - deviceContext->CreateLinearGradientBrush (linearGradientBrushProperties, - brushProps, - gradientStops, - result.resetAndGetPointerAddress()); - return result; - }); - } + Direct2DMetrics* metrics); private: LruCache> cache; @@ -917,28 +347,7 @@ class RadialGradientCache public: ComSmartPtr get (const ColourGradient& gradient, ComSmartPtr deviceContext, - Direct2DMetrics* metrics) - { - jassert (gradient.isRadial); - - return cache.get (gradient, [&deviceContext, &metrics] (const auto& key) - { - const auto gradientStops = makeGradientStopCollection (key, deviceContext, metrics); - - const auto p1 = key.point1; - const auto p2 = key.point2; - const auto r = p1.getDistanceFrom (p2); - const auto radialGradientBrushProperties = D2D1::RadialGradientBrushProperties ({ p1.x, p1.y }, {}, r, r); - const D2D1_BRUSH_PROPERTIES brushProps { 1.0F, D2D1::IdentityMatrix() }; - - ComSmartPtr result; - deviceContext->CreateRadialGradientBrush (radialGradientBrushProperties, - brushProps, - gradientStops, - result.resetAndGetPointerAddress()); - return result; - }); - } + Direct2DMetrics* metrics); private: LruCache> cache; @@ -949,18 +358,9 @@ class RectangleListSpriteBatch public: RectangleListSpriteBatch() = default; - ~RectangleListSpriteBatch() - { - release(); - } + ~RectangleListSpriteBatch(); - void release() - { - whiteRectangle = nullptr; - spriteBatches = {}; - destinations.free(); - destinationsCapacity = 0; - } + void release(); template bool fillRectangles (ComSmartPtr deviceContext, @@ -1066,17 +466,7 @@ public: } private: - ComSmartPtr getSpriteBatch (ID2D1DeviceContext3& dc, uint32 key) - { - return spriteBatches.get ((uint32) key, [&dc] (auto) -> ComSmartPtr - { - ComSmartPtr result; - if (const auto hr = dc.CreateSpriteBatch (result.resetAndGetPointerAddress()); SUCCEEDED (hr)) - return result; - - return nullptr; - }); - } + ComSmartPtr getSpriteBatch (ID2D1DeviceContext3& dc, uint32 key); static constexpr uint32 rectangleSize = 32; ComSmartPtr whiteRectangle; @@ -1088,103 +478,13 @@ private: class Direct2DDeviceResources { public: - static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& adapters, ID2D1Bitmap1* bitmap) - { - if (bitmap == nullptr) - return {}; + static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& adapters, ID2D1Bitmap1* bitmap); + static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, IDXGIDevice* dxgiDevice); + static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, ID2D1DeviceContext1* context); - ComSmartPtr surface; - bitmap->GetSurface (surface.resetAndGetPointerAddress()); + static LUID getLUID (ComSmartPtr adapter); - if (surface == nullptr) - return {}; - - ComSmartPtr device; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - surface->GetDevice (__uuidof (device), (void**) device.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return findAdapter (adapters, device); - } - - static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, IDXGIDevice* dxgiDevice) - { - if (dxgiDevice == nullptr) - return {}; - - ComSmartPtr adapter; - dxgiDevice->GetAdapter (adapter.resetAndGetPointerAddress()); - - if (adapter == nullptr) - return {}; - - ComSmartPtr adapter1; - adapter.QueryInterface (adapter1); - - if (adapter1 == nullptr) - return {}; - - const auto adapterLuid = getLUID (adapter1); - - const auto& adapters = dxgiAdapters.getAdapterArray(); - - const auto it = std::find_if (adapters.begin(), adapters.end(), [&] (DxgiAdapter::Ptr ptr) - { - const auto tie = [] (const LUID& x) { return std::tie (x.LowPart, x.HighPart); }; - - const auto thisLuid = getLUID (ptr->dxgiAdapter); - return tie (thisLuid) == tie (adapterLuid); - }); - - if (it == adapters.end()) - return {}; - - return *it; - } - - static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, ID2D1DeviceContext1* context) - { - if (context == nullptr) - return {}; - - ComSmartPtr device; - context->GetDevice (device.resetAndGetPointerAddress()); - - if (device == nullptr) - return {}; - - ComSmartPtr dxgiDevice; - device.QueryInterface (dxgiDevice); - - return findAdapter (dxgiAdapters, dxgiDevice); - } - - static LUID getLUID (ComSmartPtr adapter) - { - DXGI_ADAPTER_DESC1 desc{}; - adapter->GetDesc1 (&desc); - return desc.AdapterLuid; - } - - static std::optional create (ComSmartPtr context) - { - if (context == nullptr) - return {}; - - Direct2DDeviceResources result; - - if (const auto hr = context->CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f), - result.colourBrush.resetAndGetPointerAddress()); - FAILED (hr)) - { - jassertfalse; - return {}; - } - - result.rectangleListSpriteBatch = std::make_unique(); - - return result; - } + static std::optional create (ComSmartPtr context); ComSmartPtr colourBrush; LinearGradientCache linearGradientCache; @@ -1200,113 +500,15 @@ class SwapChain public: SwapChain() = default; - HRESULT create (HWND hwnd, Rectangle size, DxgiAdapter::Ptr adapter) - { - if (chain != nullptr || hwnd == nullptr) - return S_OK; + HRESULT create (HWND hwnd, Rectangle size, DxgiAdapter::Ptr adapter); - auto dxgiFactory = directX->adapters.getFactory(); + bool canPaint() const; - if (dxgiFactory == nullptr || adapter->direct3DDevice == nullptr) - return E_FAIL; + HRESULT resize (Rectangle newSize); - buffer = nullptr; - chain = nullptr; + Rectangle getSize() const; - // Make the waitable swap chain - // Create the swap chain with premultiplied alpha support for transparent windows - DXGI_SWAP_CHAIN_DESC1 swapChainDescription = {}; - swapChainDescription.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - swapChainDescription.Width = (UINT) size.getWidth(); - swapChainDescription.Height = (UINT) size.getHeight(); - swapChainDescription.SampleDesc.Count = 1; - swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDescription.BufferCount = 2; - swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - swapChainDescription.Flags = swapChainFlags; - - swapChainDescription.Scaling = DXGI_SCALING_STRETCH; - swapChainDescription.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; - - if (const auto hr = dxgiFactory->CreateSwapChainForComposition (adapter->direct3DDevice, - &swapChainDescription, - nullptr, - chain.resetAndGetPointerAddress()); - FAILED (hr)) - { - return hr; - } - - // Get the waitable swap chain presentation event and set the maximum frame latency - ComSmartPtr chain2; - if (const auto hr = chain.QueryInterface (chain2); FAILED (hr)) - return hr; - - if (chain2 == nullptr) - return E_FAIL; - - swapChainEvent.emplace (chain2->GetFrameLatencyWaitableObject()); - if (swapChainEvent->getHandle() == INVALID_HANDLE_VALUE) - return E_NOINTERFACE; - - chain2->SetMaximumFrameLatency (1); - - createBuffer (adapter); - return buffer != nullptr ? S_OK : E_FAIL; - } - - bool canPaint() const - { - return chain != nullptr && buffer != nullptr; - } - - HRESULT resize (Rectangle newSize) - { - if (chain == nullptr) - return E_FAIL; - - constexpr auto minFrameSize = 1; - constexpr auto maxFrameSize = 16384; - - auto scaledSize = newSize.getUnion ({ minFrameSize, minFrameSize }) - .getIntersection ({ maxFrameSize, maxFrameSize }); - - buffer = nullptr; - - if (const auto hr = chain->ResizeBuffers (0, (UINT) scaledSize.getWidth(), (UINT) scaledSize.getHeight(), DXGI_FORMAT_B8G8R8A8_UNORM, swapChainFlags); FAILED (hr)) - return hr; - - ComSmartPtr device; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - chain->GetDevice (__uuidof (device), (void**) device.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - createBuffer (Direct2DDeviceResources::findAdapter (directX->adapters, device)); - - return buffer != nullptr ? S_OK : E_FAIL; - } - - Rectangle getSize() const - { - const auto surface = getSurface(); - - if (surface == nullptr) - return {}; - - DXGI_SURFACE_DESC desc{}; - if (FAILED (surface->GetDesc (&desc))) - return {}; - - return { (int) desc.Width, (int) desc.Height }; - } - - WindowsScopedEvent* getEvent() - { - if (swapChainEvent.has_value()) - return &*swapChainEvent; - - return nullptr; - } + WindowsScopedEvent* getEvent(); auto getChain() const { @@ -1323,41 +525,8 @@ public: static constexpr uint32 presentFlags = 0; private: - ComSmartPtr getSurface() const - { - if (chain == nullptr) - return nullptr; - - ComSmartPtr surface; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - if (const auto hr = chain->GetBuffer (0, __uuidof (surface), reinterpret_cast (surface.resetAndGetPointerAddress())); FAILED (hr)) - return nullptr; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return surface; - } - - void createBuffer (DxgiAdapter::Ptr adapter) - { - buffer = nullptr; - - const auto deviceContext = Direct2DDeviceContext::create (adapter); - - if (deviceContext == nullptr) - return; - - const auto surface = getSurface(); - - if (surface == nullptr) - return; - - D2D1_BITMAP_PROPERTIES1 bitmapProperties{}; - bitmapProperties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; - bitmapProperties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; - bitmapProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; - - deviceContext->CreateBitmapFromDxgiSurface (surface, bitmapProperties, buffer.resetAndGetPointerAddress()); - } + ComSmartPtr getSurface() const; + void createBuffer (DxgiAdapter::Ptr adapter); class AssignableDirectX { @@ -1392,36 +561,7 @@ private: class CompositionTree { public: - static std::optional create (IDXGIDevice* dxgiDevice, HWND hwnd, IDXGISwapChain1* swapChain) - { - if (dxgiDevice == nullptr) - return {}; - - CompositionTree result; - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - if (const auto hr = DCompositionCreateDevice (dxgiDevice, - __uuidof (IDCompositionDevice), - reinterpret_cast (result.compositionDevice.resetAndGetPointerAddress())); - FAILED (hr)) - { - return {}; - } - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - if (const auto hr = result.compositionDevice->CreateTargetForHwnd (hwnd, FALSE, result.compositionTarget.resetAndGetPointerAddress()); FAILED (hr)) - return {}; - if (const auto hr = result.compositionDevice->CreateVisual (result.compositionVisual.resetAndGetPointerAddress()); FAILED (hr)) - return {}; - if (const auto hr = result.compositionTarget->SetRoot (result.compositionVisual); FAILED (hr)) - return {}; - if (const auto hr = result.compositionVisual->SetContent (swapChain); FAILED (hr)) - return {}; - if (const auto hr = result.compositionDevice->Commit(); FAILED (hr)) - return {}; - - return result; - } + static std::optional create (IDXGIDevice* dxgiDevice, HWND hwnd, IDXGISwapChain1* swapChain); private: CompositionTree() = default; @@ -1431,43 +571,9 @@ private: ComSmartPtr compositionVisual; }; -static String getLocalisedName (IDWriteLocalizedStrings* names) -{ - jassert (names != nullptr); - - uint32 index = 0; - BOOL exists = false; - [[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists); - - if (! exists) - index = 0; - - uint32 length = 0; - hr = names->GetStringLength (index, &length); - - HeapBlock name (length + 1); - hr = names->GetString (index, name, length + 1); - - return static_cast (name); -} - -static String getFontFamilyName (IDWriteFontFamily* family) -{ - jassert (family != nullptr); - ComSmartPtr familyNames; - auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); - jassertquiet (SUCCEEDED (hr)); - return getLocalisedName (familyNames); -} - -static String getFontFaceName (IDWriteFont* font) -{ - jassert (font != nullptr); - ComSmartPtr faceNames; - auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); - jassertquiet (SUCCEEDED (hr)); - return getLocalisedName (faceNames); -} +String getLocalisedName (IDWriteLocalizedStrings* names); +String getFontFamilyName (IDWriteFontFamily* family); +String getFontFaceName (IDWriteFont* font); template static StringArray stringArrayFromRange (Range&& range) @@ -1483,146 +589,21 @@ static StringArray stringArrayFromRange (Range&& range) class AggregateFontCollection { public: - explicit AggregateFontCollection (ComSmartPtr baseCollection) - : collections { std::move (baseCollection) } {} + explicit AggregateFontCollection (ComSmartPtr baseCollection); - StringArray findAllTypefaceNames() - { - const std::scoped_lock lock { mutex }; + StringArray findAllTypefaceNames(); - std::set strings; + static std::vector> getAllFontsInFamily (IDWriteFontFamily* fontFamily); - for (const auto& collection : collections) - { - const auto count = collection->GetFontFamilyCount(); + StringArray findAllTypefaceStyles (const String& family); - for (auto i = decltype (count){}; i < count; ++i) - { - ComSmartPtr family; + ComSmartPtr getFamilyByName (const wchar_t* name); - if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr) - continue; + ComSmartPtr findFontForFace (IDWriteFontFace* face); - strings.insert (getFontFamilyName (family)); - } - } + void addCollection (ComSmartPtr collection); - return stringArrayFromRange (strings); - } - - static std::vector> getAllFontsInFamily (IDWriteFontFamily* fontFamily) - { - const auto fontFacesCount = fontFamily->GetFontCount(); - std::vector> result; - result.reserve (fontFacesCount); - - for (UINT32 i = 0; i < fontFacesCount; ++i) - { - ComSmartPtr dwFont; - - if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()))) - continue; - - if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) - continue; - - result.push_back (dwFont); - } - - return result; - } - - StringArray findAllTypefaceStyles (const String& family) - { - const std::scoped_lock lock { mutex }; - - for (const auto& collection : collections) - { - BOOL fontFound = false; - uint32 fontIndex = 0; - - if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound) - continue; - - ComSmartPtr fontFamily; - - if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr) - continue; - - std::set uniqueResults; - StringArray orderedResults; - - for (const auto& font : getAllFontsInFamily (fontFamily)) - { - const auto name = getFontFaceName (font); - - if (uniqueResults.insert (name).second) - orderedResults.add (name); - } - - return orderedResults; - } - - return {}; - } - - ComSmartPtr getFamilyByName (const wchar_t* name) - { - const std::scoped_lock lock { mutex }; - - for (const auto& collection : collections) - { - const auto fontIndex = [&] - { - BOOL found = false; - UINT32 index = 0; - - return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found) - ? index - : (UINT32) -1; - }(); - - if (fontIndex == (UINT32) -1) - continue; - - ComSmartPtr family; - - if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr) - continue; - - return family; - } - - return {}; - } - - ComSmartPtr findFontForFace (IDWriteFontFace* face) - { - for (const auto& collection : collections) - { - ComSmartPtr result; - - if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress()))) - return result; - } - - return {}; - } - - void addCollection (ComSmartPtr collection) - { - const std::scoped_lock lock { mutex }; - collections.push_back (std::move (collection)); - } - - void removeCollection (ComSmartPtr collection) - { - const std::scoped_lock lock { mutex }; - const auto iter = std::find (collections.begin(), collections.end(), collection); - - if (iter != collections.end()) - collections.erase (iter); - } + void removeCollection (ComSmartPtr collection); struct MapResult { @@ -1644,43 +625,7 @@ public: wchar_t const* baseFamilyName, DWRITE_FONT_WEIGHT baseWeight, DWRITE_FONT_STYLE baseStyle, - DWRITE_FONT_STRETCH baseStretch) noexcept - { - const std::scoped_lock lock { mutex }; - - // For reasons I don't understand, the system may pick better substitutions when passing - // nullptr, instead of the system collection, as the "default collection to use". - auto collectionsToCheck = collections; - collectionsToCheck.insert (collectionsToCheck.begin(), nullptr); - - MapResult bestMatch; - for (const auto& collection : collectionsToCheck) - { - MapResult result; - const auto status = fallback->MapCharacters (analysisSource, - textPosition, - textLength, - collection, - baseFamilyName, - baseWeight, - baseStyle, - baseStretch, - &result.length, - result.font.resetAndGetPointerAddress(), - &result.scale); - - if (FAILED (status) || result.font == nullptr) - continue; - - if (result.length == textLength) - return result; - - if (result.length >= bestMatch.length) - bestMatch = result; - } - - return bestMatch; - } + DWRITE_FONT_STRETCH baseStretch) noexcept; private: std::vector> collections; @@ -1690,39 +635,14 @@ private: class MemoryFontFileStream final : public ComBaseClassHelper { public: - explicit MemoryFontFileStream (std::shared_ptr blockIn) - : block (std::move (blockIn)) - { - } - - JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override - { - *fileSize = block->getSize(); - return S_OK; - } - - JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override - { - *lastWriteTime = 0; - return S_OK; - } + explicit MemoryFontFileStream (std::shared_ptr blockIn); + JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override; + JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override; JUCE_COMRESULT ReadFileFragment (const void** fragmentStart, UINT64 fileOffset, UINT64 fragmentSize, - void** fragmentContext) noexcept override - { - if (fileOffset + fragmentSize > block->getSize()) - { - *fragmentStart = nullptr; - *fragmentContext = nullptr; - return E_INVALIDARG; - } - - *fragmentStart = addBytesToPointer (block->getData(), fileOffset); - *fragmentContext = this; - return S_OK; - } + void** fragmentContext) noexcept override; void WINAPI ReleaseFileFragment (void*) noexcept override {} @@ -1733,28 +653,11 @@ private: class MemoryFontFileLoader final : public ComBaseClassHelper { public: - explicit MemoryFontFileLoader (MemoryBlock blob) - : block (std::make_shared (std::move (blob))) - { - } + explicit MemoryFontFileLoader (MemoryBlock blob); HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey, UINT32 keySize, - IDWriteFontFileStream** fontFileStream) noexcept override - { - if (keySize != Uuid::size()) - return E_INVALIDARG; - - Uuid requestedKey { static_cast (fontFileReferenceKey) }; - - if (requestedKey == uuid) - { - *fontFileStream = new MemoryFontFileStream { block }; - return S_OK; - } - - return E_INVALIDARG; - } + IDWriteFontFileStream** fontFileStream) noexcept override; Uuid getUuid() const { return uuid; } @@ -1768,29 +671,10 @@ private: class FontFileEnumerator final : public ComBaseClassHelper { public: - FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr loaderIn) - : factory (factoryIn), loader (loaderIn) {} + FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr loaderIn); - HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override - { - *fontFile = nullptr; - - if (! isPositiveAndBelow (rawDataIndex, 1)) - return E_FAIL; - - const auto uuid = loader->getUuid(); - return factory.CreateCustomFontFileReference (uuid.getRawData(), - (UINT32) uuid.size(), - loader, - fontFile); - } - - HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override - { - ++rawDataIndex; - *hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE; - return S_OK; - } + HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override; + HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override; private: IDWriteFactory& factory; @@ -1803,49 +687,16 @@ private: class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper { public: - explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn) - : factory (factoryIn) - { - } + explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn); - ~DirectWriteCustomFontCollectionLoader() override - { - for (const auto& loader : fileLoaders) - factory.UnregisterFontFileLoader (loader); - } + ~DirectWriteCustomFontCollectionLoader() override; - Uuid addRawFontData (Span blob) - { - const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } }); - - factory.RegisterFontFileLoader (loader); - - fileLoaders.push_back (loader); - - return fileLoaders.back()->getUuid(); - } + Uuid addRawFontData (Span blob); HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn, const void* collectionKey, UINT32 collectionKeySize, - IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override - { - if (collectionKeySize != Uuid::size()) - return E_INVALIDARG; - - const Uuid requestedCollectionKey { static_cast (collectionKey) }; - - for (const auto& loader : fileLoaders) - { - if (loader->getUuid() != requestedCollectionKey) - continue; - - *fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader }; - return S_OK; - } - - return E_INVALIDARG; - } + IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override; private: IDWriteFactory& factory; @@ -1856,23 +707,9 @@ private: class Direct2DFactories { public: - Direct2DFactories() - { - ComSmartPtr collection; + Direct2DFactories(); - if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr) - fonts.emplace (collection); - else - jassertfalse; - } - - ~Direct2DFactories() - { - if (directWriteFactory == nullptr) - return; - - directWriteFactory->UnregisterFontCollectionLoader (collectionLoader); - } + ~Direct2DFactories(); [[nodiscard]] ComSmartPtr getDWriteFactory() const { return directWriteFactory; } [[nodiscard]] ComSmartPtr getDWriteFactory4() const { return directWriteFactory4; } @@ -1882,62 +719,9 @@ public: private: DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" }; - const ComSmartPtr d2dFactory = [&]() -> ComSmartPtr - { - JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, - D2D1CreateFactory, - d2d1CreateFactory, - HRESULT, - (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) - - if (d2d1CreateFactory == nullptr) - return {}; - - D2D1_FACTORY_OPTIONS options; - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; - - ComSmartPtr result; - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof (ID2D1Factory), - &options, - (void**) result.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return result; - }(); - - const ComSmartPtr directWriteFactory = [&]() -> ComSmartPtr - { - JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, - DWriteCreateFactory, - dWriteCreateFactory, - HRESULT, - (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) - - if (dWriteCreateFactory == nullptr) - return {}; - - ComSmartPtr result; - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") - dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, - __uuidof (IDWriteFactory), - (IUnknown**) result.resetAndGetPointerAddress()); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - return result; - }(); - - const ComSmartPtr collectionLoader = [&]() -> ComSmartPtr - { - auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory }); - directWriteFactory->RegisterFontCollectionLoader (result); - - return result; - }(); - + const ComSmartPtr d2dFactory; + const ComSmartPtr directWriteFactory; + const ComSmartPtr collectionLoader; const ComSmartPtr directWriteFactory4 = directWriteFactory.getInterface(); std::optional fonts;