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;