diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
index d3ab2b3b2f..48d60a1cf5 100644
--- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
+++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
@@ -1949,8 +1949,20 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -2050,6 +2062,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
@@ -4432,8 +4445,20 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -4533,6 +4558,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
index 4472af4312..470450b0ce 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
@@ -2351,6 +2351,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4220,6 +4238,12 @@
+
+
+
+
+
+
@@ -4275,6 +4299,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
index 927e467c9d..5ec3094dc5 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
@@ -3103,6 +3103,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7314,6 +7332,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7479,6 +7515,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
index 386672621c..73f73f0de7 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
@@ -2351,6 +2351,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4220,6 +4238,12 @@
+
+
+
+
+
+
@@ -4275,6 +4299,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
index 719d35ca9c..a406b70e15 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
@@ -3103,6 +3103,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7314,6 +7332,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7479,6 +7515,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
index bcc8892132..bb22be11a5 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
@@ -2351,6 +2351,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4220,6 +4238,12 @@
+
+
+
+
+
+
@@ -4275,6 +4299,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
index f2bd15238a..3dacfcc93f 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
@@ -3103,6 +3103,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7314,6 +7332,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7479,6 +7515,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/examples/DemoRunner/Source/UI/MainComponent.cpp b/examples/DemoRunner/Source/UI/MainComponent.cpp
index ecd11ef99a..975c32a754 100644
--- a/examples/DemoRunner/Source/UI/MainComponent.cpp
+++ b/examples/DemoRunner/Source/UI/MainComponent.cpp
@@ -288,7 +288,7 @@ MainComponent::MainComponent()
{
#if JUCE_MAC && USE_COREGRAPHICS_RENDERING
setRenderingEngine (1);
- #else
+ #elif ! JUCE_WINDOWS
setRenderingEngine (0);
#endif
}
diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
index 258cb171a3..dfab116b91 100644
--- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
@@ -1711,8 +1711,20 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -1812,6 +1824,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
@@ -3876,8 +3889,20 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -3977,6 +4002,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
index b3f26783c9..13eaac485d 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
@@ -2044,6 +2044,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3691,6 +3709,12 @@
+
+
+
+
+
+
@@ -3746,6 +3770,7 @@
+
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
index a17006751e..c21a5fb549 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
@@ -2647,6 +2647,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6369,6 +6387,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6534,6 +6570,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
index 876c932b7e..3480c79f14 100644
--- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
@@ -1841,8 +1841,20 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -1942,6 +1954,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
@@ -4159,8 +4172,20 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -4260,6 +4285,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
index efe24d5370..b671307ed1 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
@@ -2178,6 +2178,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3924,6 +3942,12 @@
+
+
+
+
+
+
@@ -3979,6 +4003,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
index 2c14ce6efc..8c6f069a3a 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
@@ -2854,6 +2854,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6804,6 +6822,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6969,6 +7005,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
index 01f090e1f6..81ca1234d4 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
@@ -2178,6 +2178,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3924,6 +3942,12 @@
+
+
+
+
+
+
@@ -3979,6 +4003,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
index a9ec14bd1f..a292c2ed5e 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
@@ -2854,6 +2854,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6804,6 +6822,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6969,6 +7005,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
index 531ae00fa6..6ba5be41be 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
@@ -2178,6 +2178,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3924,6 +3942,12 @@
+
+
+
+
+
+
@@ -3979,6 +4003,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
index 2bfc7ee7fd..164d80dfdf 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
@@ -2854,6 +2854,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6804,6 +6822,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6969,6 +7005,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
index 5466961251..e06bb633a4 100644
--- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
+++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
@@ -1730,8 +1730,20 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -1831,6 +1843,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
@@ -3975,8 +3988,20 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_CoreGraphicsHelpers_mac.h"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHelpers_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DHwndContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImage_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DImageContext_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.cpp"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DMetrics_windows.h"
+ "../../../../../modules/juce_graphics/native/juce_Direct2DResources_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_DirectWriteTypeLayout_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"
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
@@ -4076,6 +4101,7 @@ set_source_files_properties(
"../../../../../modules/juce_gui_basics/detail/juce_ScopedContentSharerInterface.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h"
"../../../../../modules/juce_gui_basics/detail/juce_ScopedMessageBoxInterface.h"
+ "../../../../../modules/juce_gui_basics/detail/juce_StandardCachedComponentImage.h"
"../../../../../modules/juce_gui_basics/detail/juce_ToolbarItemDragAndDropOverlayComponent.h"
"../../../../../modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h"
"../../../../../modules/juce_gui_basics/detail/juce_ViewportHelpers.h"
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
index 6dd4b3b37b..782e2ff0bd 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
@@ -2065,6 +2065,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3789,6 +3807,12 @@
+
+
+
+
+
+
@@ -3844,6 +3868,7 @@
+
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
index 813d5906a9..8c75d8c006 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
@@ -2701,6 +2701,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6531,6 +6549,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6696,6 +6732,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/NetworkGraphicsDemo/Source/SharedCanvas.h b/extras/NetworkGraphicsDemo/Source/SharedCanvas.h
index c98e3eaf27..1bc2b5a395 100644
--- a/extras/NetworkGraphicsDemo/Source/SharedCanvas.h
+++ b/extras/NetworkGraphicsDemo/Source/SharedCanvas.h
@@ -307,9 +307,10 @@ public:
}
//==============================================================================
- bool isVectorDevice() const override { return true; }
- float getPhysicalPixelScaleFactor() override { return 1.0f; }
- void setOrigin (Point o) override { addTransform (AffineTransform::translation ((float) o.x, (float) o.y)); }
+ bool isVectorDevice() const override { return true; }
+ float getPhysicalPixelScaleFactor() const override { return 1.0f; }
+ uint64_t getFrameId() const override { return 0; }
+ void setOrigin (Point o) override { addTransform (AffineTransform::translation ((float) o.x, (float) o.y)); }
void addTransform (const AffineTransform& t) override
{
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
index 9805bc0059..8a0dcd6fd3 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
@@ -1247,6 +1247,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -2606,6 +2624,12 @@
+
+
+
+
+
+
@@ -2661,6 +2685,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
index 501a5f7948..4afdbb74b7 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
@@ -1645,6 +1645,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4509,6 +4527,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4674,6 +4710,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
index cf7dd55d5b..5a95bf893b 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
@@ -1247,6 +1247,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -2606,6 +2624,12 @@
+
+
+
+
+
+
@@ -2661,6 +2685,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
index 6c47970b38..6b17fd3496 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
@@ -1645,6 +1645,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4509,6 +4527,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4674,6 +4710,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
index ba9ca29d0d..79dcdea15c 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
@@ -1247,6 +1247,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -2606,6 +2624,12 @@
+
+
+
+
+
+
@@ -2661,6 +2685,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
index 6a0a19264c..31635c9d15 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
@@ -1645,6 +1645,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4509,6 +4527,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -4674,6 +4710,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
index 90eb689173..9d30fa240e 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
@@ -2186,6 +2186,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4020,6 +4038,12 @@
+
+
+
+
+
+
@@ -4075,6 +4099,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
index eec19e5c28..d7f24e70d1 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -2875,6 +2875,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6918,6 +6936,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7083,6 +7119,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
index 95766d8b0b..7d083e9e14 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
@@ -2186,6 +2186,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4020,6 +4038,12 @@
+
+
+
+
+
+
@@ -4075,6 +4099,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
index f0d8c5d661..e30b11dead 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -2875,6 +2875,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6918,6 +6936,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7083,6 +7119,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
index 0bc775c155..9440bacc4d 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
@@ -2186,6 +2186,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -4020,6 +4038,12 @@
+
+
+
+
+
+
@@ -4075,6 +4099,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
index 03dd29757d..b0818c6f51 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -2875,6 +2875,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6918,6 +6936,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -7083,6 +7119,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
index 3487f33952..31f1069025 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
@@ -2064,6 +2064,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
true
@@ -3765,6 +3783,12 @@
+
+
+
+
+
+
@@ -3820,6 +3844,7 @@
+
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
index 37f34aedc0..0b69cd7a7b 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
@@ -2698,6 +2698,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6498,6 +6516,24 @@
JUCE Modules\juce_graphics\native
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
+
+ JUCE Modules\juce_graphics\native
+
JUCE Modules\juce_graphics\native
@@ -6663,6 +6699,9 @@
JUCE Modules\juce_gui_basics\detail
+
+ JUCE Modules\juce_gui_basics\detail
+
JUCE Modules\juce_gui_basics\detail
diff --git a/modules/juce_core/native/juce_BasicNativeHeaders.h b/modules/juce_core/native/juce_BasicNativeHeaders.h
index cca7a67f38..32c7126d05 100644
--- a/modules/juce_core/native/juce_BasicNativeHeaders.h
+++ b/modules/juce_core/native/juce_BasicNativeHeaders.h
@@ -100,13 +100,8 @@
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#define STRICT 1
#define WIN32_LEAN_AND_MEAN 1
- #if JUCE_MINGW
- #if ! defined (_WIN32_WINNT)
- #define _WIN32_WINNT 0x0600
- #endif
- #else
- #define _WIN32_WINNT 0x0602
- #endif
+ #define WINVER _WIN32_WINNT_WIN10
+ #define _WIN32_WINNT _WIN32_WINNT_WIN10
#define _UNICODE 1
#define UNICODE 1
#ifndef _WIN32_IE
diff --git a/modules/juce_graphics/colour/juce_ColourGradient.cpp b/modules/juce_graphics/colour/juce_ColourGradient.cpp
index cb13bd07f0..11317ef36c 100644
--- a/modules/juce_graphics/colour/juce_ColourGradient.cpp
+++ b/modules/juce_graphics/colour/juce_ColourGradient.cpp
@@ -89,8 +89,6 @@ ColourGradient::ColourGradient (Colour colour1, Point p1,
ColourPoint { 1.0, colour2 });
}
-ColourGradient::~ColourGradient() {}
-
ColourGradient ColourGradient::vertical (Colour c1, float y1, Colour c2, float y2)
{
return { c1, 0, y1, c2, 0, y2, false };
@@ -101,17 +99,45 @@ ColourGradient ColourGradient::horizontal (Colour c1, float x1, Colour c2, float
return { c1, x1, 0, c2, x2, 0, false };
}
-bool ColourGradient::operator== (const ColourGradient& other) const noexcept
+struct PointComparisons
{
- return point1 == other.point1 && point2 == other.point2
- && isRadial == other.isRadial
- && colours == other.colours;
+ auto tie() const { return std::tie (point->x, point->y); }
+
+ bool operator== (const PointComparisons& other) const { return tie() == other.tie(); }
+ bool operator!= (const PointComparisons& other) const { return tie() != other.tie(); }
+ bool operator< (const PointComparisons& other) const { return tie() < other.tie(); }
+
+ const Point* point = nullptr;
+};
+
+struct ColourGradient::ColourPointArrayComparisons
+{
+ bool operator== (const ColourPointArrayComparisons& other) const { return *array == *other.array; }
+ bool operator!= (const ColourPointArrayComparisons& other) const { return *array != *other.array; }
+
+ bool operator< (const ColourPointArrayComparisons& other) const
+ {
+ return std::lexicographical_compare (array->begin(), array->end(), other.array->begin(), other.array->end());
+ }
+
+ const Array* array = nullptr;
+};
+
+auto ColourGradient::tie() const
+{
+ return std::tuple (PointComparisons { &point1 },
+ PointComparisons { &point2 },
+ isRadial,
+ ColourPointArrayComparisons { &colours });
}
-bool ColourGradient::operator!= (const ColourGradient& other) const noexcept
-{
- return ! operator== (other);
-}
+bool ColourGradient::operator== (const ColourGradient& other) const noexcept { return tie() == other.tie(); }
+bool ColourGradient::operator!= (const ColourGradient& other) const noexcept { return tie() != other.tie(); }
+
+bool ColourGradient::operator< (const ColourGradient& other) const noexcept { return tie() < other.tie(); }
+bool ColourGradient::operator<= (const ColourGradient& other) const noexcept { return tie() <= other.tie(); }
+bool ColourGradient::operator> (const ColourGradient& other) const noexcept { return tie() > other.tie(); }
+bool ColourGradient::operator>= (const ColourGradient& other) const noexcept { return tie() >= other.tie(); }
//==============================================================================
void ColourGradient::clearColours()
@@ -265,15 +291,4 @@ bool ColourGradient::isInvisible() const noexcept
return true;
}
-bool ColourGradient::ColourPoint::operator== (ColourPoint other) const noexcept
-{
- const auto tie = [] (const ColourPoint& p) { return std::tie (p.position, p.colour); };
- return tie (*this) == tie (other);
-}
-
-bool ColourGradient::ColourPoint::operator!= (ColourPoint other) const noexcept
-{
- return ! operator== (other);
-}
-
} // namespace juce
diff --git a/modules/juce_graphics/colour/juce_ColourGradient.h b/modules/juce_graphics/colour/juce_ColourGradient.h
index b427c68d10..6439c2377a 100644
--- a/modules/juce_graphics/colour/juce_ColourGradient.h
+++ b/modules/juce_graphics/colour/juce_ColourGradient.h
@@ -124,9 +124,6 @@ public:
return horizontal (colourLeft, (float) area.getX(), colourRight, (float) area.getRight());
}
- /** Destructor */
- ~ColourGradient();
-
//==============================================================================
/** Removes any colours that have been added.
@@ -224,18 +221,32 @@ public:
bool operator== (const ColourGradient&) const noexcept;
bool operator!= (const ColourGradient&) const noexcept;
+ /** This comparison, and the other ordered comparisons are provided only for compatibility with
+ ordered container types like std::set and std::map.
+ */
+ bool operator< (const ColourGradient&) const noexcept;
+ bool operator<= (const ColourGradient&) const noexcept;
+ bool operator> (const ColourGradient&) const noexcept;
+ bool operator>= (const ColourGradient&) const noexcept;
private:
//==============================================================================
struct ColourPoint
{
- bool operator== (ColourPoint) const noexcept;
- bool operator!= (ColourPoint) const noexcept;
+ auto tie() const { return std::tuple (position, colour.getPixelARGB().getNativeARGB()); }
+
+ bool operator== (ColourPoint other) const noexcept { return tie() == other.tie(); }
+ bool operator!= (ColourPoint other) const noexcept { return tie() != other.tie(); }
+ bool operator< (ColourPoint other) const noexcept { return tie() < other.tie(); }
double position;
Colour colour;
};
+ struct ColourPointArrayComparisons;
+
+ auto tie() const;
+
Array colours;
JUCE_LEAK_DETECTOR (ColourGradient)
diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
index 4631b3cb59..68c49ab8e6 100644
--- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
+++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
@@ -128,6 +128,8 @@ Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
//==============================================================================
void Graphics::resetToDefaultState()
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::resetToDefaultState, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
context.setFill (FillType());
context.setFont (FontOptions{}.withMetricsKind (TypefaceMetricsKind::legacy));
@@ -141,6 +143,8 @@ bool Graphics::isVectorDevice() const
bool Graphics::reduceClipRegion (Rectangle area)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::reduceClipRegionRectangle, etw::graphicsKeyword, context.getFrameId(), area)
+
saveStateIfPending();
return context.clipToRectangle (area);
}
@@ -152,12 +156,16 @@ bool Graphics::reduceClipRegion (int x, int y, int w, int h)
bool Graphics::reduceClipRegion (const RectangleList& clipRegion)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::reduceClipRegionRectangleList, etw::graphicsKeyword, context.getFrameId(), clipRegion)
+
saveStateIfPending();
return context.clipToRectangleList (clipRegion);
}
bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::reduceClipRegionPath, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
context.clipToPath (path, transform);
return ! context.isClipEmpty();
@@ -165,6 +173,8 @@ bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transf
bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::reduceClipRegionImage, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
context.clipToImageAlpha (image, transform);
return ! context.isClipEmpty();
@@ -172,6 +182,8 @@ bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& tran
void Graphics::excludeClipRegion (Rectangle rectangleToExclude)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::excludeClipRegion, etw::graphicsKeyword, context.getFrameId(), rectangleToExclude);
+
saveStateIfPending();
context.excludeClipRectangle (rectangleToExclude);
}
@@ -188,12 +200,16 @@ Rectangle Graphics::getClipBounds() const
void Graphics::saveState()
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::saveState, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
saveStatePending = true;
}
void Graphics::restoreState()
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::restoreState, etw::graphicsKeyword, context.getFrameId());
+
if (saveStatePending)
saveStatePending = false;
else
@@ -204,6 +220,8 @@ void Graphics::saveStateIfPending()
{
if (saveStatePending)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::saveState, etw::graphicsKeyword, context.getFrameId());
+
saveStatePending = false;
context.saveState();
}
@@ -222,6 +240,8 @@ void Graphics::setOrigin (int x, int y)
void Graphics::addTransform (const AffineTransform& transform)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::addTransform, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
context.addTransform (transform);
}
@@ -233,12 +253,16 @@ bool Graphics::clipRegionIntersects (Rectangle area) const
void Graphics::beginTransparencyLayer (float layerOpacity)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::beginTransparencyLayer, etw::graphicsKeyword, context.getFrameId());
+
saveStateIfPending();
context.beginTransparencyLayer (layerOpacity);
}
void Graphics::endTransparencyLayer()
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endTransparencyLayer, etw::graphicsKeyword, context.getFrameId());
+
context.endTransparencyLayer();
}
@@ -477,42 +501,62 @@ void Graphics::drawFittedText (const String& text, int x, int y, int width, int
//==============================================================================
void Graphics::fillRect (Rectangle r) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::fillRect, etw::graphicsKeyword, context.getFrameId(), r)
+
context.fillRect (r, false);
}
void Graphics::fillRect (Rectangle r) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_F32 (etw::fillRect, etw::graphicsKeyword, context.getFrameId(), r)
+
context.fillRect (r);
}
void Graphics::fillRect (int x, int y, int width, int height) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::fillRect, etw::graphicsKeyword, context.getFrameId(), (Rectangle { x, y, width, height }))
+
context.fillRect (coordsToRectangle (x, y, width, height), false);
}
void Graphics::fillRect (float x, float y, float width, float height) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_F32 (etw::fillRect, etw::graphicsKeyword, context.getFrameId(), (Rectangle { x, y, width, height }))
+
fillRect (coordsToRectangle (x, y, width, height));
}
void Graphics::fillRectList (const RectangleList& rectangles) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_F32 (etw::fillRectList, etw::graphicsKeyword, context.getFrameId(), rectangles)
+
context.fillRectList (rectangles);
}
void Graphics::fillRectList (const RectangleList& rects) const
{
- for (auto& r : rects)
- context.fillRect (r, false);
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::fillRectList, etw::graphicsKeyword, context.getFrameId(), rects)
+
+ RectangleList converted;
+
+ for (const auto& r : rects)
+ converted.add (r.toFloat());
+
+ context.fillRectList (converted);
}
void Graphics::fillAll() const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::fillAll, etw::graphicsKeyword, context.getFrameId())
+
context.fillAll();
}
void Graphics::fillAll (Colour colourToUse) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::fillAll, etw::graphicsKeyword, context.getFrameId())
+
if (! colourToUse.isTransparent())
{
context.saveState();
@@ -522,16 +566,19 @@ void Graphics::fillAll (Colour colourToUse) const
}
}
-
//==============================================================================
void Graphics::fillPath (const Path& path) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::fillPath, etw::graphicsKeyword, context.getFrameId());
+
if (! (context.isClipEmpty() || path.isEmpty()))
context.fillPath (path, AffineTransform());
}
void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::fillPath, etw::graphicsKeyword, context.getFrameId())
+
if (! (context.isClipEmpty() || path.isEmpty()))
context.fillPath (path, transform);
}
@@ -540,9 +587,10 @@ void Graphics::strokePath (const Path& path,
const PathStrokeType& strokeType,
const AffineTransform& transform) const
{
- Path stroke;
- strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor());
- fillPath (stroke);
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::strokePath, etw::graphicsKeyword, context.getFrameId())
+
+ if (! (context.isClipEmpty() || path.isEmpty()))
+ context.strokePath (path, strokeType, transform);
}
//==============================================================================
@@ -563,22 +611,16 @@ void Graphics::drawRect (Rectangle r, int lineThickness) const
void Graphics::drawRect (Rectangle r, const float lineThickness) const
{
- jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_F32 (etw::drawRect, etw::graphicsKeyword, context.getFrameId(), r)
- RectangleList rects;
- rects.addWithoutMerging (r.removeFromTop (lineThickness));
- rects.addWithoutMerging (r.removeFromBottom (lineThickness));
- rects.addWithoutMerging (r.removeFromLeft (lineThickness));
- rects.addWithoutMerging (r.removeFromRight (lineThickness));
- context.fillRectList (rects);
+ jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
+ context.drawRect (r, lineThickness);
}
//==============================================================================
void Graphics::fillEllipse (Rectangle area) const
{
- Path p;
- p.addEllipse (area);
- fillPath (p);
+ context.fillEllipse (area);
}
void Graphics::fillEllipse (float x, float y, float w, float h) const
@@ -593,21 +635,7 @@ void Graphics::drawEllipse (float x, float y, float width, float height, float l
void Graphics::drawEllipse (Rectangle area, float lineThickness) const
{
- Path p;
-
- if (approximatelyEqual (area.getWidth(), area.getHeight()))
- {
- // For a circle, we can avoid having to generate a stroke
- p.addEllipse (area.expanded (lineThickness * 0.5f));
- p.addEllipse (area.reduced (lineThickness * 0.5f));
- p.setUsingNonZeroWinding (false);
- fillPath (p);
- }
- else
- {
- p.addEllipse (area);
- strokePath (p, PathStrokeType (lineThickness));
- }
+ context.drawEllipse (area, lineThickness);
}
void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const
@@ -617,9 +645,7 @@ void Graphics::fillRoundedRectangle (float x, float y, float width, float height
void Graphics::fillRoundedRectangle (Rectangle r, const float cornerSize) const
{
- Path p;
- p.addRoundedRectangle (r, cornerSize);
- fillPath (p);
+ context.fillRoundedRectangle (r, cornerSize);
}
void Graphics::drawRoundedRectangle (float x, float y, float width, float height,
@@ -630,9 +656,7 @@ void Graphics::drawRoundedRectangle (float x, float y, float width, float height
void Graphics::drawRoundedRectangle (Rectangle r, float cornerSize, float lineThickness) const
{
- Path p;
- p.addRoundedRectangle (r, cornerSize);
- strokePath (p, PathStrokeType (lineThickness));
+ context.drawRoundedRectangle (r, cornerSize, lineThickness);
}
void Graphics::drawArrow (Line line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
@@ -719,9 +743,7 @@ void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThick
void Graphics::drawLine (Line line, const float lineThickness) const
{
- Path p;
- p.addLineSegment (line, lineThickness);
- fillPath (p);
+ context.drawLineWithThickness (line, lineThickness);
}
void Graphics::drawDashedLine (Line line, const float* dashLengths,
diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h
index 132eaf7459..c685fcfbc7 100644
--- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h
+++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h
@@ -71,7 +71,7 @@ public:
*/
virtual void setOrigin (Point) = 0;
virtual void addTransform (const AffineTransform&) = 0;
- virtual float getPhysicalPixelScaleFactor() = 0;
+ virtual float getPhysicalPixelScaleFactor() const = 0;
virtual bool clipToRectangle (const Rectangle&) = 0;
virtual bool clipToRectangleList (const RectangleList&) = 0;
@@ -100,9 +100,35 @@ public:
virtual void fillRect (const Rectangle&) = 0;
virtual void fillRectList (const RectangleList&) = 0;
virtual void fillPath (const Path&, const AffineTransform&) = 0;
+
+ virtual void drawRect (const Rectangle& rect, float lineThickness)
+ {
+ auto r = rect;
+ RectangleList rects;
+ rects.addWithoutMerging (r.removeFromTop (lineThickness));
+ rects.addWithoutMerging (r.removeFromBottom (lineThickness));
+ rects.addWithoutMerging (r.removeFromLeft (lineThickness));
+ rects.addWithoutMerging (r.removeFromRight (lineThickness));
+ fillRectList (rects);
+ }
+
+ virtual void strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform)
+ {
+ Path stroke;
+ strokeType.createStrokedPath (stroke, path, transform, getPhysicalPixelScaleFactor());
+ fillPath (stroke, {});
+ }
+
virtual void drawImage (const Image&, const AffineTransform&) = 0;
virtual void drawLine (const Line&) = 0;
+ virtual void drawLineWithThickness (const Line& line, float lineThickness)
+ {
+ Path p;
+ p.addLineSegment (line, lineThickness);
+ fillPath (p, {});
+ }
+
virtual void setFont (const Font&) = 0;
virtual const Font& getFont() = 0;
@@ -110,6 +136,51 @@ public:
virtual void drawGlyphs (Span,
Span>,
const AffineTransform&) = 0;
+
+ virtual void drawRoundedRectangle (const Rectangle& r, float cornerSize, float lineThickness)
+ {
+ Path p;
+ p.addRoundedRectangle (r, cornerSize);
+ strokePath (p, PathStrokeType (lineThickness), {});
+ }
+
+ virtual void fillRoundedRectangle (const Rectangle& r, float cornerSize)
+ {
+ Path p;
+ p.addRoundedRectangle (r, cornerSize);
+ fillPath (p, {});
+ }
+
+ virtual void drawEllipse (const Rectangle& area, float lineThickness)
+ {
+ Path p;
+
+ if (approximatelyEqual (area.getWidth(), area.getHeight()))
+ {
+ // For a circle, we can avoid having to generate a stroke
+ p.addEllipse (area.expanded (lineThickness * 0.5f));
+ p.addEllipse (area.reduced (lineThickness * 0.5f));
+ p.setUsingNonZeroWinding (false);
+ fillPath (p, {});
+ }
+ else
+ {
+ p.addEllipse (area);
+ strokePath (p, PathStrokeType (lineThickness), {});
+ }
+ }
+
+ virtual void fillEllipse (const Rectangle& area)
+ {
+ Path p;
+ p.addEllipse (area);
+ fillPath (p, {});
+ }
+
+ /** Returns an integer that uniquely identifies the current frame.
+ Useful for debugging/logging.
+ */
+ virtual uint64_t getFrameId() const = 0;
};
} // namespace juce
diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp
index be7e39e215..fac4e0cb56 100644
--- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp
+++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp
@@ -114,7 +114,7 @@ void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /*
jassertfalse;
}
-float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() { return 1.0f; }
+float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() const { return 1.0f; }
bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& r)
{
diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h
index 8782d0e088..755a6538d8 100644
--- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h
+++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h
@@ -55,7 +55,7 @@ public:
bool isVectorDevice() const override;
void setOrigin (Point) override;
void addTransform (const AffineTransform&) override;
- float getPhysicalPixelScaleFactor() override;
+ float getPhysicalPixelScaleFactor() const override;
bool clipToRectangle (const Rectangle&) override;
bool clipToRectangleList (const RectangleList&) override;
diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp
index b6250e4f22..9af41bf287 100644
--- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp
+++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp
@@ -39,6 +39,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image&
: RenderingHelpers::StackBasedLowLevelGraphicsContext
(new RenderingHelpers::SoftwareRendererSavedState (image, image.getBounds()))
{
+ JUCE_TRACE_LOG_PAINT_CALL (etw::startGDIImage, getFrameId());
}
LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, Point origin,
@@ -46,8 +47,13 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image&
: RenderingHelpers::StackBasedLowLevelGraphicsContext
(new RenderingHelpers::SoftwareRendererSavedState (image, initialClip, origin))
{
+
+ JUCE_TRACE_EVENT_INT_RECT_LIST (etw::startGDIFrame, etw::softwareRendererKeyword, getFrameId(), initialClip);
}
-LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() {}
+LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer()
+{
+ JUCE_TRACE_LOG_PAINT_CALL (etw::endGDIFrame, getFrameId());
+}
} // namespace juce
diff --git a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp
index e865c8b380..91377f554a 100644
--- a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp
+++ b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp
@@ -35,47 +35,6 @@
namespace juce
{
-static void blurDataTriplets (uint8* d, int num, const int delta) noexcept
-{
- uint32 last = d[0];
- d[0] = (uint8) ((d[0] + d[delta] + 1) / 3);
- d += delta;
-
- num -= 2;
-
- do
- {
- const uint32 newLast = d[0];
- d[0] = (uint8) ((last + d[0] + d[delta] + 1) / 3);
- d += delta;
- last = newLast;
- }
- while (--num > 0);
-
- d[0] = (uint8) ((last + d[0] + 1) / 3);
-}
-
-static void blurSingleChannelImage (uint8* const data, const int width, const int height,
- const int lineStride, const int repetitions) noexcept
-{
- jassert (width > 2 && height > 2);
-
- for (int y = 0; y < height; ++y)
- for (int i = repetitions; --i >= 0;)
- blurDataTriplets (data + lineStride * y, width, 1);
-
- for (int x = 0; x < width; ++x)
- for (int i = repetitions; --i >= 0;)
- blurDataTriplets (data + x, height, lineStride);
-}
-
-static void blurSingleChannelImage (Image& image, int radius)
-{
- const Image::BitmapData bm (image, Image::BitmapData::readWrite);
- blurSingleChannelImage (bm.data, bm.width, bm.height, bm.lineStride, 2 * radius);
-}
-
-//==============================================================================
DropShadow::DropShadow (Colour shadowColour, const int r, Point o) noexcept
: colour (shadowColour), radius (r), offset (o)
{
@@ -86,16 +45,15 @@ void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const
{
jassert (radius > 0);
- if (srcImage.isValid())
- {
- Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel));
- shadowImage.duplicateIfShared();
+ if (! srcImage.isValid())
+ return;
- blurSingleChannelImage (shadowImage, radius);
+ Image blurred;
+ srcImage.convertedToFormat (Image::SingleChannel)
+ .applyGaussianBlurEffect ((float) radius, blurred);
- g.setColour (colour);
- g.drawImageAt (shadowImage, offset.x, offset.y, true);
- }
+ g.setColour (colour);
+ g.drawImageAt (blurred, offset.x, offset.y, true);
}
void DropShadow::drawForPath (Graphics& g, const Path& path) const
@@ -103,24 +61,25 @@ void DropShadow::drawForPath (Graphics& g, const Path& path) const
jassert (radius > 0);
auto area = (path.getBounds().getSmallestIntegerContainer() + offset)
- .expanded (radius + 1)
- .getIntersection (g.getClipBounds().expanded (radius + 1));
+ .expanded (radius + 1)
+ .getIntersection (g.getClipBounds().expanded (radius + 1));
if (area.getWidth() > 2 && area.getHeight() > 2)
{
- Image renderedPath (Image::SingleChannel, area.getWidth(), area.getHeight(), true);
+ Image pathImage { Image::SingleChannel, area.getWidth(), area.getHeight(), true };
{
- Graphics g2 (renderedPath);
+ Graphics g2 (pathImage);
g2.setColour (Colours::white);
g2.fillPath (path, AffineTransform::translation ((float) (offset.x - area.getX()),
(float) (offset.y - area.getY())));
}
- blurSingleChannelImage (renderedPath, radius);
+ Image blurred;
+ pathImage.applyGaussianBlurEffect ((float) radius, blurred);
g.setColour (colour);
- g.drawImageAt (renderedPath, area.getX(), area.getY(), true);
+ g.drawImageAt (blurred, area.getX(), area.getY(), true);
}
}
@@ -167,8 +126,8 @@ void DropShadow::drawForRectangle (Graphics& g, const Rectangle& targetArea
}
//==============================================================================
-DropShadowEffect::DropShadowEffect() {}
-DropShadowEffect::~DropShadowEffect() {}
+DropShadowEffect::DropShadowEffect() = default;
+DropShadowEffect::~DropShadowEffect() = default;
void DropShadowEffect::setShadowProperties (const DropShadow& newShadow)
{
diff --git a/modules/juce_graphics/effects/juce_GlowEffect.cpp b/modules/juce_graphics/effects/juce_GlowEffect.cpp
index 7a7e271150..9c4ca4cc2d 100644
--- a/modules/juce_graphics/effects/juce_GlowEffect.cpp
+++ b/modules/juce_graphics/effects/juce_GlowEffect.cpp
@@ -35,8 +35,8 @@
namespace juce
{
-GlowEffect::GlowEffect() {}
-GlowEffect::~GlowEffect() {}
+GlowEffect::GlowEffect() = default;
+GlowEffect::~GlowEffect() = default;
void GlowEffect::setGlowProperties (float newRadius, Colour newColour, Point pos)
{
@@ -47,17 +47,10 @@ void GlowEffect::setGlowProperties (float newRadius, Colour newColour, Point offset;
+ Image cachedImage;
JUCE_LEAK_DETECTOR (GlowEffect)
};
diff --git a/modules/juce_graphics/fonts/juce_Font.cpp b/modules/juce_graphics/fonts/juce_Font.cpp
index 25607cf23e..2f92f6c3df 100644
--- a/modules/juce_graphics/fonts/juce_Font.cpp
+++ b/modules/juce_graphics/fonts/juce_Font.cpp
@@ -774,6 +774,10 @@ static bool characterNotRendered (uint32_t c)
static bool isFontSuitableForCodepoint (const Font& font, juce_wchar c)
{
const auto& hbFont = font.getNativeDetails().font;
+
+ if (hbFont == nullptr)
+ return false;
+
hb_codepoint_t glyph{};
return characterNotRendered ((uint32_t) c)
diff --git a/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp b/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp
index ebf85d0be2..c76421321f 100644
--- a/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp
+++ b/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp
@@ -322,12 +322,12 @@ private:
};
/* Returns glyphs in logical order as that favours wrapping. */
-static auto lowLevelShape (const String& string,
- Range range,
- const Font& font,
- TextScript script,
- const String& language,
- TextDirection direction)
+static std::vector lowLevelShape (const String& string,
+ Range range,
+ const Font& font,
+ TextScript script,
+ const String& language,
+ TextDirection direction)
{
HbBuffer buffer { hb_buffer_create() };
hb_buffer_clear_contents (buffer.get());
@@ -431,6 +431,12 @@ static auto lowLevelShape (const String& string,
auto nativeFont = font.getNativeDetails().font;
+ if (nativeFont == nullptr)
+ {
+ jassertfalse;
+ return {};
+ }
+
hb_shape (nativeFont.get(), buffer.get(), features.data(), (unsigned int) features.size());
const auto [infos, positions] = [&buffer]
diff --git a/modules/juce_graphics/geometry/juce_Path.cpp b/modules/juce_graphics/geometry/juce_Path.cpp
index 8ba3d61c37..5efd49da68 100644
--- a/modules/juce_graphics/geometry/juce_Path.cpp
+++ b/modules/juce_graphics/geometry/juce_Path.cpp
@@ -77,9 +77,7 @@ static bool isMarker (float value, float marker) noexcept
}
//==============================================================================
-Path::PathBounds::PathBounds() noexcept
-{
-}
+Path::PathBounds::PathBounds() noexcept = default;
Rectangle Path::PathBounds::getRectangle() const noexcept
{
@@ -88,7 +86,7 @@ Rectangle Path::PathBounds::getRectangle() const noexcept
void Path::PathBounds::reset() noexcept
{
- pathXMin = pathYMin = pathYMax = pathXMax = 0;
+ *this = {};
}
void Path::PathBounds::reset (float x, float y) noexcept
@@ -107,13 +105,9 @@ void Path::PathBounds::extend (float x, float y) noexcept
}
//==============================================================================
-Path::Path()
-{
-}
+Path::Path() = default;
-Path::~Path()
-{
-}
+Path::~Path() = default;
Path::Path (const Path& other)
: data (other.data),
@@ -124,32 +118,31 @@ Path::Path (const Path& other)
Path& Path::operator= (const Path& other)
{
- if (this != &other)
- {
- data = other.data;
- bounds = other.bounds;
- useNonZeroWinding = other.useNonZeroWinding;
- }
-
+ auto copy = other;
+ *this = std::move (copy);
return *this;
}
Path::Path (Path&& other) noexcept
- : data (std::move (other.data)),
- bounds (other.bounds),
- useNonZeroWinding (other.useNonZeroWinding)
+ : data (std::exchange (other.data, {})),
+ bounds (std::exchange (other.bounds, {})),
+ useNonZeroWinding (std::exchange (other.useNonZeroWinding, {}))
{
}
Path& Path::operator= (Path&& other) noexcept
{
- data = std::move (other.data);
- bounds = other.bounds;
- useNonZeroWinding = other.useNonZeroWinding;
+ auto copy = std::move (other);
+ swapWithPath (copy);
return *this;
}
-bool Path::operator== (const Path& other) const noexcept { return useNonZeroWinding == other.useNonZeroWinding && data == other.data; }
+bool Path::operator== (const Path& other) const noexcept
+{
+ const auto tie = [] (const auto& x) { return std::tie (x.useNonZeroWinding, x.data); };
+ return tie (*this) == tie (other);
+}
+
bool Path::operator!= (const Path& other) const noexcept { return ! operator== (other); }
void Path::clear() noexcept
@@ -161,10 +154,7 @@ void Path::clear() noexcept
void Path::swapWithPath (Path& other) noexcept
{
data.swapWith (other.data);
- std::swap (bounds.pathXMin, other.bounds.pathXMin);
- std::swap (bounds.pathXMax, other.bounds.pathXMax);
- std::swap (bounds.pathYMin, other.bounds.pathYMin);
- std::swap (bounds.pathYMax, other.bounds.pathYMax);
+ std::swap (bounds, other.bounds);
std::swap (useNonZeroWinding, other.useNonZeroWinding);
}
@@ -967,8 +957,8 @@ bool Path::contains (float x, float y, float tolerance) const
}
}
- return useNonZeroWinding ? (negativeCrossings != positiveCrossings)
- : ((negativeCrossings + positiveCrossings) & 1) != 0;
+ return isUsingNonZeroWinding() ? (negativeCrossings != positiveCrossings)
+ : ((negativeCrossings + positiveCrossings) & 1) != 0;
}
bool Path::contains (Point point, float tolerance) const
@@ -1279,11 +1269,11 @@ void Path::loadPathFromStream (InputStream& source)
break;
case 'n':
- useNonZeroWinding = true;
+ setUsingNonZeroWinding (true);
break;
case 'z':
- useNonZeroWinding = false;
+ setUsingNonZeroWinding (false);
break;
case 'e':
@@ -1304,7 +1294,7 @@ void Path::loadPathFromData (const void* const pathData, const size_t numberOfBy
void Path::writePathToStream (OutputStream& dest) const
{
- dest.writeByte (useNonZeroWinding ? 'n' : 'z');
+ dest.writeByte (isUsingNonZeroWinding() ? 'n' : 'z');
for (auto* i = data.begin(); i != data.end();)
{
@@ -1352,7 +1342,7 @@ void Path::writePathToStream (OutputStream& dest) const
String Path::toString() const
{
MemoryOutputStream s (2048);
- if (! useNonZeroWinding)
+ if (! isUsingNonZeroWinding())
s << 'a';
float lastMarker = 0.0f;
@@ -1489,10 +1479,6 @@ Path::Iterator::Iterator (const Path& p) noexcept
{
}
-Path::Iterator::~Iterator() noexcept
-{
-}
-
bool Path::Iterator::next() noexcept
{
if (index != path.data.end())
diff --git a/modules/juce_graphics/geometry/juce_Path.h b/modules/juce_graphics/geometry/juce_Path.h
index 1999bf6c77..3bcbe48958 100644
--- a/modules/juce_graphics/geometry/juce_Path.h
+++ b/modules/juce_graphics/geometry/juce_Path.h
@@ -724,7 +724,6 @@ public:
*/
bool isUsingNonZeroWinding() const { return useNonZeroWinding; }
-
//==============================================================================
/** Iterates the lines and curves that a path contains.
@@ -735,7 +734,6 @@ public:
public:
//==============================================================================
Iterator (const Path& path) noexcept;
- ~Iterator() noexcept;
//==============================================================================
/** Moves onto the next element in the path.
diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp
index 770ee158c0..e593d95392 100644
--- a/modules/juce_graphics/images/juce_Image.cpp
+++ b/modules/juce_graphics/images/juce_Image.cpp
@@ -35,6 +35,96 @@
namespace juce
{
+struct BitmapDataDetail
+{
+ BitmapDataDetail() = delete;
+
+ static void convert (const Image::BitmapData& src, Image::BitmapData& dest)
+ {
+ jassert (src.width == dest.width);
+ jassert (src.height == dest.height);
+
+ if (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat)
+ {
+ for (int y = 0; y < dest.height; ++y)
+ memcpy (dest.getLinePointer (y), src.getLinePointer (y), (size_t) dest.pixelStride * (size_t) dest.width);
+ }
+ else
+ {
+ for (int y = 0; y < dest.height; ++y)
+ for (int x = 0; x < dest.width; ++x)
+ dest.setPixelColour (x, y, src.getPixelColour (x, y));
+ }
+ }
+
+ static Image convert (const Image::BitmapData& src, const ImageType& type)
+ {
+ Image result (type.create (src.pixelFormat, src.width, src.height, false));
+
+ {
+ Image::BitmapData dest (result, Image::BitmapData::writeOnly);
+ BitmapDataDetail::convert (src, dest);
+ }
+
+ return result;
+ }
+};
+
+class SubsectionPixelData : public ImagePixelData
+{
+public:
+ SubsectionPixelData (ImagePixelData::Ptr source, Rectangle r)
+ : ImagePixelData (source->pixelFormat, r.getWidth(), r.getHeight()),
+ sourceImage (std::move (source)),
+ area (r)
+ {
+ }
+
+ std::unique_ptr createLowLevelContext() override
+ {
+ auto g = sourceImage->createLowLevelContext();
+ g->clipToRectangle (area);
+ g->setOrigin (area.getPosition());
+ return g;
+ }
+
+ void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
+ {
+ sourceImage->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
+
+ if (mode != Image::BitmapData::readOnly)
+ sendDataChangeMessage();
+ }
+
+ ImagePixelData::Ptr clone() override
+ {
+ jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting)
+ auto type = createType();
+
+ Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB));
+
+ {
+ Graphics g (newImage);
+ g.drawImageAt (Image (*this), 0, 0);
+ }
+
+ return *newImage.getPixelData();
+ }
+
+ std::unique_ptr createType() const override { return sourceImage->createType(); }
+
+ /* as we always hold a reference to image, don't double count */
+ int getSharedCount() const noexcept override { return getReferenceCount() + sourceImage->getSharedCount() - 1; }
+
+private:
+ friend class Image;
+ const ImagePixelData::Ptr sourceImage;
+ const Rectangle area;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData)
+};
+
+//==============================================================================
ImagePixelData::ImagePixelData (Image::PixelFormat format, int w, int h)
: pixelFormat (format), width (w), height (h)
{
@@ -57,9 +147,14 @@ int ImagePixelData::getSharedCount() const noexcept
return getReferenceCount();
}
+void ImagePixelData::applyGaussianBlurEffect ([[maybe_unused]] float radius, Image& result)
+{
+ result = {};
+}
+
//==============================================================================
-ImageType::ImageType() {}
-ImageType::~ImageType() {}
+ImageType::ImageType() = default;
+ImageType::~ImageType() = default;
Image ImageType::convert (const Image& source) const
{
@@ -68,26 +163,14 @@ Image ImageType::convert (const Image& source) const
const Image::BitmapData src (source, Image::BitmapData::readOnly);
- Image newImage (create (src.pixelFormat, src.width, src.height, false));
- Image::BitmapData dest (newImage, Image::BitmapData::writeOnly);
+ if (src.data == nullptr)
+ return {};
- if (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat)
- {
- for (int y = 0; y < dest.height; ++y)
- memcpy (dest.getLinePointer (y), src.getLinePointer (y), (size_t) dest.lineStride);
- }
- else
- {
- for (int y = 0; y < dest.height; ++y)
- for (int x = 0; x < dest.width; ++x)
- dest.setPixelColour (x, y, src.getPixelColour (x, y));
- }
-
- return newImage;
+ return BitmapDataDetail::convert (src, *this);
}
//==============================================================================
-class SoftwarePixelData final : public ImagePixelData
+class SoftwarePixelData : public ImagePixelData
{
public:
SoftwarePixelData (Image::PixelFormat formatToUse, int w, int h, bool clearImage)
@@ -133,8 +216,8 @@ private:
JUCE_LEAK_DETECTOR (SoftwarePixelData)
};
-SoftwareImageType::SoftwareImageType() {}
-SoftwareImageType::~SoftwareImageType() {}
+SoftwareImageType::SoftwareImageType() = default;
+SoftwareImageType::~SoftwareImageType() = default;
ImagePixelData::Ptr SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
{
@@ -147,15 +230,15 @@ int SoftwareImageType::getTypeID() const
}
//==============================================================================
-NativeImageType::NativeImageType() {}
-NativeImageType::~NativeImageType() {}
+NativeImageType::NativeImageType() = default;
+NativeImageType::~NativeImageType() = default;
int NativeImageType::getTypeID() const
{
return 1;
}
-#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD
+#if JUCE_LINUX || JUCE_BSD
ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const
{
return new SoftwarePixelData (format, width, height, clearImage);
@@ -163,58 +246,6 @@ ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int widt
#endif
//==============================================================================
-class SubsectionPixelData final : public ImagePixelData
-{
-public:
- SubsectionPixelData (ImagePixelData::Ptr source, Rectangle r)
- : ImagePixelData (source->pixelFormat, r.getWidth(), r.getHeight()),
- sourceImage (std::move (source)), area (r)
- {
- }
-
- std::unique_ptr createLowLevelContext() override
- {
- auto g = sourceImage->createLowLevelContext();
- g->clipToRectangle (area);
- g->setOrigin (area.getPosition());
- return g;
- }
-
- void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
- {
- sourceImage->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode);
-
- if (mode != Image::BitmapData::readOnly)
- sendDataChangeMessage();
- }
-
- ImagePixelData::Ptr clone() override
- {
- jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting)
- auto type = createType();
-
- Image newImage (type->create (pixelFormat, area.getWidth(), area.getHeight(), pixelFormat != Image::RGB));
-
- {
- Graphics g (newImage);
- g.drawImageAt (Image (*this), 0, 0);
- }
-
- return *newImage.getPixelData();
- }
-
- std::unique_ptr createType() const override { return sourceImage->createType(); }
-
- /* as we always hold a reference to image, don't double count */
- int getSharedCount() const noexcept override { return getReferenceCount() + sourceImage->getSharedCount() - 1; }
-
-private:
- friend class Image;
- const ImagePixelData::Ptr sourceImage;
- const Rectangle area;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData)
-};
Image Image::getClippedImage (const Rectangle& area) const
{
@@ -226,14 +257,11 @@ Image Image::getClippedImage (const Rectangle& area) const
if (validArea.isEmpty())
return {};
- return Image (*new SubsectionPixelData (image, validArea));
+ return Image { ImagePixelData::Ptr { new SubsectionPixelData { image, validArea } } };
}
-
//==============================================================================
-Image::Image() noexcept
-{
-}
+Image::Image() noexcept = default;
Image::Image (ReferenceCountedObjectPtr instance) noexcept
: image (std::move (instance))
@@ -272,11 +300,44 @@ Image& Image::operator= (Image&& other) noexcept
return *this;
}
-Image::~Image()
-{
-}
+Image::~Image() = default;
int Image::getReferenceCount() const noexcept { return image == nullptr ? 0 : image->getSharedCount(); }
+
+void Image::applyGaussianBlurEffect (float radius, Image& result) const
+{
+ if (image == nullptr)
+ {
+ result = {};
+ return;
+ }
+
+ auto copy = result;
+ image->applyGaussianBlurEffect (radius, copy);
+
+ if (copy.isValid())
+ {
+ result = std::move (copy);
+ return;
+ }
+
+ const auto tie = [] (const auto& x) { return std::tuple (x.getFormat(), x.getWidth(), x.getHeight()); };
+
+ if (tie (*this) != tie (result))
+ result = Image { getFormat(), getWidth(), getHeight(), false };
+
+ ImageConvolutionKernel blurKernel (roundToInt (radius * 2.0f));
+
+ blurKernel.createGaussianBlur (radius);
+
+ blurKernel.applyToImage (result, *this, result.getBounds());
+}
+
+bool Image::isValid() const noexcept
+{
+ return image != nullptr;
+}
+
int Image::getWidth() const noexcept { return image == nullptr ? 0 : image->width; }
int Image::getHeight() const noexcept { return image == nullptr ? 0 : image->height; }
Rectangle Image::getBounds() const noexcept { return image == nullptr ? Rectangle() : Rectangle (image->width, image->height); }
diff --git a/modules/juce_graphics/images/juce_Image.h b/modules/juce_graphics/images/juce_Image.h
index d419f08993..8d1fb59ad3 100644
--- a/modules/juce_graphics/images/juce_Image.h
+++ b/modules/juce_graphics/images/juce_Image.h
@@ -153,7 +153,7 @@ public:
The isNull() method is the opposite of isValid().
@see isNull
*/
- inline bool isValid() const noexcept { return image != nullptr; }
+ bool isValid() const noexcept;
/** Returns true if this image is not valid.
If you create an Image with the default constructor, it has no size or content, and is null
@@ -161,7 +161,7 @@ public:
The isNull() method is the opposite of isValid().
@see isValid
*/
- inline bool isNull() const noexcept { return image == nullptr; }
+ bool isNull() const noexcept { return ! isValid(); }
//==============================================================================
/** Returns the image's width (in pixels). */
@@ -416,6 +416,13 @@ public:
*/
int getReferenceCount() const noexcept;
+ /** Applies a blur to this image, placing the blurred image in the result out-parameter.
+
+ If result is already the correct size, then its storage will be reused directly.
+ Otherwise, new storage may be allocated for the blurred image.
+ */
+ void applyGaussianBlurEffect (float radius, Image& result) const;
+
//==============================================================================
/** @internal */
ImagePixelData* getPixelData() const noexcept { return image.get(); }
@@ -469,9 +476,19 @@ public:
virtual void initialiseBitmapData (Image::BitmapData&, int x, int y, Image::BitmapData::ReadWriteMode) = 0;
/** Returns the number of Image objects which are currently referring to the same internal
shared image data. This is different to the reference count as an instance of ImagePixelData
- can internally depend on another ImagePixelData via it's member variables. */
+ can internally depend on another ImagePixelData via it's member variables.
+ */
virtual int getSharedCount() const noexcept;
+ /** Applies a native blur effect to this image, if available.
+
+ Implementations should attempt to re-use the storage provided in the result out-parameter
+ when possible.
+
+ If native blurs are unsupported, or if creating a blur fails for any other reason,
+ the result out-parameter will be reset to an invalid image.
+ */
+ virtual void applyGaussianBlurEffect (float radius, Image& result);
/** The pixel format of the image data. */
const Image::PixelFormat pixelFormat;
diff --git a/modules/juce_graphics/juce_graphics.cpp b/modules/juce_graphics/juce_graphics.cpp
index a5e90a29cb..3c30c91eeb 100644
--- a/modules/juce_graphics/juce_graphics.cpp
+++ b/modules/juce_graphics/juce_graphics.cpp
@@ -55,19 +55,45 @@
#include
#elif JUCE_WINDOWS
- // get rid of some warnings in Window's own headers
+ // get rid of some warnings in Window's own headers
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4458)
- #include
- #include
+ /* If you hit a compile error trying to include these files, you may need to update
+ your version of the Windows SDK to the latest one. The DirectWrite and Direct2D
+ headers are in the version 8 SDKs.
- #if JUCE_MINGW
- #include
- #include
- #endif
+ Need Direct2D 1.3 for sprite batching
+ */
+
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #if JUCE_ETW_TRACELOGGING
+ #include
+ #include
+
+ TRACELOGGING_DEFINE_PROVIDER (JUCETraceLogProvider,
+ "JUCETraceLogProvider",
+ // {6A612E78-284D-4DDB-877A-5F521EB33132}
+ (0x6a612e78, 0x284d, 0x4ddb, 0x87, 0x7a, 0x5f, 0x52, 0x1e, 0xb3, 0x31, 0x32));
+
+#endif
JUCE_END_IGNORE_WARNINGS_MSVC
+ #if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
+ #pragma comment(lib, "Dwrite.lib")
+ #pragma comment(lib, "D2d1.lib")
+ #pragma comment(lib, "DXGI.lib")
+ #pragma comment(lib, "D3D11.lib")
+ #pragma comment(lib, "DComp.lib")
+ #pragma comment(lib, "dxguid.lib")
+ #endif
+
#elif JUCE_IOS
#import
#import
@@ -120,6 +146,7 @@
//==============================================================================
#include "fonts/juce_FunctionPointerDestructor.h"
+#include "native/juce_EventTracing.h"
#include "unicode/juce_UnicodeScript.h"
#include "unicode/juce_Unicode.h"
@@ -179,11 +206,18 @@
#include "native/juce_IconHelpers_mac.cpp"
#elif JUCE_WINDOWS
+ #include "native/juce_DirectX_windows.h"
#include "native/juce_DirectWriteTypeface_windows.cpp"
#include "native/juce_IconHelpers_windows.cpp"
- #if JUCE_DIRECT2D
- #include "native/juce_Direct2DGraphicsContext_windows.cpp"
- #endif
+ #include "native/juce_Direct2DHelpers_windows.cpp"
+ #include "native/juce_Direct2DResources_windows.cpp"
+ #include "native/juce_Direct2DImage_windows.h"
+ #include "native/juce_Direct2DGraphicsContext_windows.cpp"
+ #include "native/juce_Direct2DHwndContext_windows.cpp"
+ #include "native/juce_Direct2DImageContext_windows.h"
+ #include "native/juce_Direct2DImageContext_windows.cpp"
+ #include "native/juce_Direct2DImage_windows.cpp"
+ #include "native/juce_Direct2DMetrics_windows.cpp"
#elif JUCE_LINUX || JUCE_BSD
#include "native/juce_Fonts_linux.cpp"
diff --git a/modules/juce_graphics/juce_graphics.h b/modules/juce_graphics/juce_graphics.h
index 2bc4ff8652..571de617c2 100644
--- a/modules/juce_graphics/juce_graphics.h
+++ b/modules/juce_graphics/juce_graphics.h
@@ -78,15 +78,6 @@
#define JUCE_USE_COREIMAGE_LOADER 1
#endif
-/** Config: JUCE_USE_DIRECTWRITE
-
- Enabling this flag means that DirectWrite will be used when available for font
- management and layout.
-*/
-#ifndef JUCE_USE_DIRECTWRITE
- #define JUCE_USE_DIRECTWRITE 1
-#endif
-
/** Config: JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
Setting this flag will turn off CoreGraphics font smoothing on macOS, which some people
@@ -165,6 +156,8 @@ namespace juce
#include "native/juce_CoreGraphicsContext_mac.h"
#endif
-#if JUCE_DIRECT2D && JUCE_WINDOWS
-#include "native/juce_Direct2DGraphicsContext_windows.h"
+#if JUCE_WINDOWS
+ #include "native/juce_Direct2DMetrics_windows.h"
+ #include "native/juce_Direct2DGraphicsContext_windows.h"
+ #include "native/juce_Direct2DHwndContext_windows.h"
#endif
diff --git a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h
index 7113ceac24..31007602af 100644
--- a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h
+++ b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.h
@@ -88,7 +88,7 @@ public:
void setOrigin (Point) override;
void addTransform (const AffineTransform&) override;
- float getPhysicalPixelScaleFactor() override;
+ float getPhysicalPixelScaleFactor() const override;
bool clipToRectangle (const Rectangle&) override;
bool clipToRectangleList (const RectangleList&) override;
void excludeClipRectangle (const Rectangle&) override;
@@ -125,6 +125,8 @@ public:
Span>,
const AffineTransform&) override;
+ uint64_t getFrameId() const override { return 0; }
+
private:
//==============================================================================
detail::ContextPtr context;
diff --git a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm
index aede46f7f6..6a6ea8d0a8 100644
--- a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm
+++ b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm
@@ -272,7 +272,7 @@ void CoreGraphicsContext::addTransform (const AffineTransform& transform)
jassert (getPhysicalPixelScaleFactor() > 0.0f);
}
-float CoreGraphicsContext::getPhysicalPixelScaleFactor()
+float CoreGraphicsContext::getPhysicalPixelScaleFactor() const
{
auto t = CGContextGetUserSpaceToDeviceSpaceTransform (context.get());
auto determinant = (t.a * t.d) - (t.c * t.b);
diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp
index cb2a1ca51b..5bc70185bb 100644
--- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp
+++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.cpp
@@ -35,742 +35,1599 @@
namespace juce
{
-template
-D2D1_RECT_F rectangleToRectF (const Rectangle& r)
-{
- return { (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom() };
-}
+#if JUCE_DIRECT2D_METRICS
+JUCE_IMPLEMENT_SINGLETON (Direct2DMetricsHub)
+#endif
-static D2D1_COLOR_F colourToD2D (Colour c)
-{
- return { c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha() };
-}
-
-static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform)
-{
- Path::Iterator it (path);
-
- while (it.next())
- {
- switch (it.elementType)
- {
- case Path::Iterator::cubicTo:
- {
- transform.transformPoint (it.x1, it.y1);
- transform.transformPoint (it.x2, it.y2);
- transform.transformPoint (it.x3, it.y3);
-
- sink->AddBezier ({ { it.x1, it.y1 }, { it.x2, it.y2 }, { it.x3, it.y3 } });
- break;
- }
-
- case Path::Iterator::lineTo:
- {
- transform.transformPoint (it.x1, it.y1);
- sink->AddLine ({ it.x1, it.y1 });
- break;
- }
-
- case Path::Iterator::quadraticTo:
- {
- transform.transformPoint (it.x1, it.y1);
- transform.transformPoint (it.x2, it.y2);
- sink->AddQuadraticBezier ({ { it.x1, it.y1 }, { it.x2, it.y2 } });
- break;
- }
-
- case Path::Iterator::closePath:
- {
- sink->EndFigure (D2D1_FIGURE_END_CLOSED);
- break;
- }
-
- case Path::Iterator::startNewSubPath:
- {
- transform.transformPoint (it.x1, it.y1);
- sink->BeginFigure ({ it.x1, it.y1 }, D2D1_FIGURE_BEGIN_FILLED);
- break;
- }
- }
- }
-}
-
-static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform)
-{
- return { transform.mat00, transform.mat10, transform.mat01, transform.mat11, transform.mat02, transform.mat12 };
-}
-
-static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform)
-{
- transform.transformPoint (x, y);
- return { (FLOAT) x, (FLOAT) y };
-}
-
-static void rectToGeometrySink (const Rectangle& rect, ID2D1GeometrySink* sink, const AffineTransform& transform)
-{
- sink->BeginFigure (pointTransformed (rect.getX(), rect.getY(), transform), D2D1_FIGURE_BEGIN_FILLED);
- sink->AddLine (pointTransformed (rect.getRight(), rect.getY(), transform));
- sink->AddLine (pointTransformed (rect.getRight(), rect.getBottom(), transform));
- sink->AddLine (pointTransformed (rect.getX(), rect.getBottom(), transform));
- sink->EndFigure (D2D1_FIGURE_END_CLOSED);
-}
-
-//==============================================================================
-struct Direct2DLowLevelGraphicsContext::Pimpl
-{
- ID2D1PathGeometry* rectListToPathGeometry (const RectangleList& clipRegion)
- {
- ID2D1PathGeometry* p = nullptr;
- factories->d2dFactory->CreatePathGeometry (&p);
-
- ComSmartPtr sink;
- auto hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error
- sink->SetFillMode (D2D1_FILL_MODE_WINDING);
-
- for (int i = clipRegion.getNumRectangles(); --i >= 0;)
- rectToGeometrySink (clipRegion.getRectangle (i), sink, AffineTransform());
-
- hr = sink->Close();
- return p;
- }
-
- ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform)
- {
- ID2D1PathGeometry* p = nullptr;
- factories->d2dFactory->CreatePathGeometry (&p);
-
- ComSmartPtr sink;
- auto hr = p->Open (sink.resetAndGetPointerAddress());
- sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding()
-
- pathToGeometrySink (path, sink, transform);
-
- hr = sink->Close();
- return p;
- }
-
- SharedResourcePointer factories;
-
- ComSmartPtr renderingTarget;
- ComSmartPtr colourBrush;
-};
-
-//==============================================================================
-struct Direct2DLowLevelGraphicsContext::SavedState
+class PushedLayers
{
public:
- SavedState (Direct2DLowLevelGraphicsContext& owner_)
- : owner (owner_)
- {
- if (owner.currentState != nullptr)
- {
- // xxx seems like a very slow way to create one of these, and this is a performance
- // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write?
- setFill (owner.currentState->fillType);
- currentBrush = owner.currentState->currentBrush;
- clipRect = owner.currentState->clipRect;
- transform = owner.currentState->transform;
+ PushedLayers() { pushedLayers.reserve (32); }
+ PushedLayers (const PushedLayers&) { pushedLayers.reserve (32); }
- font = owner.currentState->font;
- currentFontFace = owner.currentState->currentFontFace;
- }
+ #if JUCE_DEBUG
+ ~PushedLayers()
+ {
+ jassert (pushedLayers.empty());
+ }
+ #endif
+
+ void push (ComSmartPtr context, const D2D1_LAYER_PARAMETERS& layerParameters)
+ {
+ // Clipping and transparency are all handled by pushing Direct2D layers. The SavedState creates an internal stack
+ // of Layer objects to keep track of how many layers need to be popped.
+ // Pass nullptr for the PushLayer layer parameter to allow Direct2D to manage the layers (Windows 8 or later)
+ context->PushLayer (layerParameters, nullptr);
+ pushedLayers.emplace_back (popLayerFlag);
+ }
+
+ void push (ComSmartPtr context, const Rectangle& r)
+ {
+ context->PushAxisAlignedClip (D2DUtilities::toRECT_F (r), D2D1_ANTIALIAS_MODE_ALIASED);
+ pushedLayers.emplace_back (popAxisAlignedLayerFlag);
+ }
+
+ void popOne (ComSmartPtr context)
+ {
+ if (pushedLayers.empty())
+ return;
+
+ if (pushedLayers.back() == popLayerFlag)
+ context->PopLayer();
else
- {
- const auto size = owner.pimpl->renderingTarget->GetPixelSize();
- clipRect.setSize (size.width, size.height);
- setFill (FillType (Colours::black));
- }
+ context->PopAxisAlignedClip();
+
+ pushedLayers.pop_back();
}
- ~SavedState()
+ bool isEmpty() const
{
- clearClip();
- clearFont();
- clearFill();
- clearPathClip();
- clearImageClip();
- complexClipLayer = nullptr;
- bitmapMaskLayer = nullptr;
+ return pushedLayers.empty();
}
- void clearClip()
+private:
+ //==============================================================================
+ // PushedLayer represents a Direct2D clipping or transparency layer
+ //
+ // D2D layers have to be pushed into the device context. Every push has to be
+ // matched with a pop.
+ //
+ // D2D has special layers called "axis aligned clip layers" which clip to an
+ // axis-aligned rectangle. Pushing an axis-aligned clip layer must be matched
+ // with a call to deviceContext->PopAxisAlignedClip() in the reverse order
+ // in which the layers were pushed.
+ //
+ // So if the pushed layer stack is built like this:
+ //
+ // PushLayer()
+ // PushLayer()
+ // PushAxisAlignedClip()
+ // PushLayer()
+ //
+ // the layer stack must be popped like this:
+ //
+ // PopLayer()
+ // PopAxisAlignedClip()
+ // PopLayer()
+ // PopLayer()
+ //
+ // PushedLayer, PushedAxisAlignedClipLayer, and LayerPopper all exist just to unwind the
+ // layer stack accordingly.
+ enum
+ {
+ popLayerFlag,
+ popAxisAlignedLayerFlag
+ };
+
+ std::vector pushedLayers;
+};
+
+struct Direct2DGraphicsContext::SavedState
+{
+public:
+ // Constructor for first stack entry
+ SavedState (Direct2DGraphicsContext& ownerIn,
+ Rectangle frameSizeIn,
+ ComSmartPtr& colourBrushIn,
+ DxgiAdapter::Ptr& adapterIn,
+ Direct2DDeviceResources& deviceResourcesIn)
+ : owner (ownerIn),
+ currentBrush (colourBrushIn),
+ colourBrush (colourBrushIn),
+ adapter (adapterIn),
+ deviceResources (deviceResourcesIn),
+ deviceSpaceClipList (frameSizeIn.toFloat())
{
- popClips();
- shouldClipRect = false;
}
- void clipToRectangle (const Rectangle& r)
+ void pushLayer (const D2D1_LAYER_PARAMETERS& layerParameters)
{
- clearClip();
- clipRect = r.toFloat().transformedBy (transform).getSmallestIntegerContainer();
- shouldClipRect = true;
- pushClips();
+ layers.push (deviceResources.deviceContext.context, layerParameters);
}
- void clearPathClip()
+ void pushGeometryClipLayer (ComSmartPtr geometry)
{
- popClips();
-
- if (shouldClipComplex)
- {
- complexClipGeometry = nullptr;
- shouldClipComplex = false;
- }
+ if (geometry != nullptr)
+ pushLayer (D2D1::LayerParameters (D2D1::InfiniteRect(), geometry));
}
- void Direct2DLowLevelGraphicsContext::SavedState::clipToPath (ID2D1Geometry* geometry)
+ void pushTransformedRectangleGeometryClipLayer (ComSmartPtr geometry, const AffineTransform& transform)
{
- clearPathClip();
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, pushGeometryLayerTime)
- if (complexClipLayer == nullptr)
- owner.pimpl->renderingTarget->CreateLayer (complexClipLayer.resetAndGetPointerAddress());
-
- complexClipGeometry = geometry;
- shouldClipComplex = true;
- pushClips();
+ jassert (geometry != nullptr);
+ auto layerParameters = D2D1::LayerParameters (D2D1::InfiniteRect(), geometry);
+ layerParameters.maskTransform = D2DUtilities::transformToMatrix (transform);
+ pushLayer (layerParameters);
}
- void clearRectListClip()
+ void pushAliasedAxisAlignedClipLayer (const Rectangle& r)
{
- popClips();
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, pushAliasedAxisAlignedLayerTime)
- if (shouldClipRectList)
- {
- rectListGeometry = nullptr;
- shouldClipRectList = false;
- }
+ layers.push (deviceResources.deviceContext.context, r);
}
- void clipToRectList (ID2D1Geometry* geometry)
+ void pushTransparencyLayer (float opacity)
{
- clearRectListClip();
-
- if (rectListLayer == nullptr)
- owner.pimpl->renderingTarget->CreateLayer (rectListLayer.resetAndGetPointerAddress());
-
- rectListGeometry = geometry;
- shouldClipRectList = true;
- pushClips();
+ pushLayer ({ D2D1::InfiniteRect(), nullptr, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, D2D1::IdentityMatrix(), opacity, {}, {} });
}
- void clearImageClip()
+ void popLayers()
{
- popClips();
-
- if (shouldClipBitmap)
- {
- maskBitmap = nullptr;
- bitmapMaskBrush = nullptr;
- shouldClipBitmap = false;
- }
+ while (! layers.isEmpty())
+ layers.popOne (deviceResources.deviceContext.context);
}
- void clipToImage (const Image& clipImage, const AffineTransform& clipTransform)
+ void popTopLayer()
{
- clearImageClip();
-
- if (bitmapMaskLayer == nullptr)
- owner.pimpl->renderingTarget->CreateLayer (bitmapMaskLayer.resetAndGetPointerAddress());
-
- D2D1_BRUSH_PROPERTIES brushProps = { 1, transformToMatrix (clipTransform) };
- auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
- D2D1_SIZE_U size = { (UINT32) clipImage.getWidth(), (UINT32) clipImage.getHeight() };
- auto bp = D2D1::BitmapProperties();
-
- maskImage = clipImage.convertedToFormat (Image::ARGB);
- Image::BitmapData bd (maskImage, Image::BitmapData::readOnly); // xxx should be maskImage?
- bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
- bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
-
- auto hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, maskBitmap.resetAndGetPointerAddress());
- hr = owner.pimpl->renderingTarget->CreateBitmapBrush (maskBitmap, bmProps, brushProps, bitmapMaskBrush.resetAndGetPointerAddress());
-
- imageMaskLayerParams = D2D1::LayerParameters();
- imageMaskLayerParams.opacityBrush = bitmapMaskBrush;
-
- shouldClipBitmap = true;
- pushClips();
- }
-
- void popClips()
- {
- if (clipsBitmap)
- {
- owner.pimpl->renderingTarget->PopLayer();
- clipsBitmap = false;
- }
-
- if (clipsComplex)
- {
- owner.pimpl->renderingTarget->PopLayer();
- clipsComplex = false;
- }
-
- if (clipsRectList)
- {
- owner.pimpl->renderingTarget->PopLayer();
- clipsRectList = false;
- }
-
- if (clipsRect)
- {
- owner.pimpl->renderingTarget->PopAxisAlignedClip();
- clipsRect = false;
- }
- }
-
- void pushClips()
- {
- if (shouldClipRect && !clipsRect)
- {
- owner.pimpl->renderingTarget->PushAxisAlignedClip (rectangleToRectF (clipRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
- clipsRect = true;
- }
-
- if (shouldClipRectList && !clipsRectList)
- {
- auto layerParams = D2D1::LayerParameters();
- rectListGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
- layerParams.geometricMask = rectListGeometry;
- owner.pimpl->renderingTarget->PushLayer (layerParams, rectListLayer);
- clipsRectList = true;
- }
-
- if (shouldClipComplex && !clipsComplex)
- {
- auto layerParams = D2D1::LayerParameters();
- complexClipGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
- layerParams.geometricMask = complexClipGeometry;
- owner.pimpl->renderingTarget->PushLayer (layerParams, complexClipLayer);
- clipsComplex = true;
- }
-
- if (shouldClipBitmap && !clipsBitmap)
- {
- owner.pimpl->renderingTarget->PushLayer (imageMaskLayerParams, bitmapMaskLayer);
- clipsBitmap = true;
- }
- }
-
- void setFill (const FillType& newFillType)
- {
- if (fillType != newFillType)
- {
- fillType = newFillType;
- clearFill();
- }
- }
-
- void clearFont()
- {
- currentFontFace = localFontFace = nullptr;
+ layers.popOne (deviceResources.deviceContext.context);
}
void setFont (const Font& newFont)
{
- if (font != newFont)
- {
- font = newFont;
- clearFont();
- }
- }
-
- void createFont()
- {
- if (currentFontFace == nullptr)
- {
- auto typefacePtr = font.getTypefacePtr();
- auto* typeface = dynamic_cast (typefacePtr.get());
- currentFontFace = typeface->getIDWriteFontFace();
- fontHeightToEmSizeFactor = typeface->getUnitsToHeightScaleFactor();
- }
+ font = newFont;
}
void setOpacity (float newOpacity)
{
fillType.setOpacity (newOpacity);
-
- if (currentBrush != nullptr)
- currentBrush->SetOpacity (newOpacity);
}
void clearFill()
{
- gradientStops = nullptr;
linearGradient = nullptr;
radialGradient = nullptr;
- bitmap = nullptr;
bitmapBrush = nullptr;
currentBrush = nullptr;
}
- void createBrush()
+ /** Translate a JUCE FillType to a Direct2D brush */
+ void updateCurrentBrush()
{
- if (currentBrush == nullptr)
+ if (fillType.isColour())
{
- if (fillType.isColour())
+ // Reuse the same colour brush
+ currentBrush = colourBrush;
+ }
+ else if (fillType.isTiledImage())
+ {
+ if (fillType.image.isNull())
+ return;
+
+ const auto d2d1Bitmap = [&]
{
- auto colour = colourToD2D (fillType.colour);
- owner.pimpl->colourBrush->SetColor (colour);
- currentBrush = owner.pimpl->colourBrush;
- }
- else if (fillType.isTiledImage())
+ if (auto direct2DPixelData = dynamic_cast (fillType.image.getPixelData()))
+ if (auto bitmap = direct2DPixelData->getAdapterD2D1Bitmap())
+ if (bitmap->GetPixelFormat().format == DXGI_FORMAT_B8G8R8A8_UNORM)
+ return bitmap;
+
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime);
+
+ return Direct2DBitmap::fromImage (fillType.image, deviceResources.deviceContext.context, Image::ARGB);
+ }();
+
+ if (d2d1Bitmap != nullptr)
{
- D2D1_BRUSH_PROPERTIES brushProps = { fillType.getOpacity(), transformToMatrix (fillType.transform) };
+ D2D1_BRUSH_PROPERTIES brushProps { fillType.getOpacity(), D2DUtilities::transformToMatrix (fillType.transform) };
auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
-
- image = fillType.image;
-
- D2D1_SIZE_U size = { (UINT32) image.getWidth(), (UINT32) image.getHeight() };
- auto bp = D2D1::BitmapProperties();
-
- this->image = image.convertedToFormat (Image::ARGB);
- Image::BitmapData bd (this->image, Image::BitmapData::readOnly);
- bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
- bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
-
- auto hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, bitmap.resetAndGetPointerAddress());
- hr = owner.pimpl->renderingTarget->CreateBitmapBrush (bitmap, bmProps, brushProps, bitmapBrush.resetAndGetPointerAddress());
-
- currentBrush = bitmapBrush;
- }
- else if (fillType.isGradient())
- {
- gradientStops = nullptr;
-
- D2D1_BRUSH_PROPERTIES brushProps = { fillType.getOpacity(), transformToMatrix (fillType.transform.followedBy (transform)) };
-
- const int numColors = fillType.gradient->getNumColours();
-
- HeapBlock stops (numColors);
-
- for (int i = fillType.gradient->getNumColours(); --i >= 0;)
+ if (const auto hr = deviceResources.deviceContext.context->CreateBitmapBrush (d2d1Bitmap,
+ bmProps,
+ brushProps,
+ bitmapBrush.resetAndGetPointerAddress()); SUCCEEDED (hr))
{
- stops[i].color = colourToD2D (fillType.gradient->getColour (i));
- stops[i].position = (FLOAT) fillType.gradient->getColourPosition (i);
- }
-
- owner.pimpl->renderingTarget->CreateGradientStopCollection (stops.getData(), numColors, gradientStops.resetAndGetPointerAddress());
-
- if (fillType.gradient->isRadial)
- {
- radialGradient = nullptr;
-
- const auto p1 = fillType.gradient->point1;
- const auto p2 = fillType.gradient->point2;
- const auto r = p1.getDistanceFrom (p2);
- const auto props = D2D1::RadialGradientBrushProperties ({ p1.x, p1.y }, {}, r, r);
-
- owner.pimpl->renderingTarget->CreateRadialGradientBrush (props, brushProps, gradientStops, radialGradient.resetAndGetPointerAddress());
- currentBrush = radialGradient;
- }
- else
- {
- linearGradient = 0;
-
- const auto p1 = fillType.gradient->point1;
- const auto p2 = fillType.gradient->point2;
- const auto props = D2D1::LinearGradientBrushProperties ({ p1.x, p1.y }, { p2.x, p2.y });
-
- owner.pimpl->renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress());
-
- currentBrush = linearGradient;
+ currentBrush = bitmapBrush;
}
}
}
+ else if (fillType.isGradient())
+ {
+ if (fillType.gradient->isRadial)
+ {
+ radialGradient = deviceResources.radialGradientCache.get (*fillType.gradient, deviceResources.deviceContext.context, owner.metrics.get());
+ currentBrush = radialGradient;
+ }
+ else
+ {
+ linearGradient = deviceResources.linearGradientCache.get (*fillType.gradient, deviceResources.deviceContext.context, owner.metrics.get());
+ currentBrush = linearGradient;
+ }
+ }
+
+ updateColourBrush();
}
- Direct2DLowLevelGraphicsContext& owner;
+ void updateColourBrush()
+ {
+ if (colourBrush && fillType.isColour())
+ {
+ auto colour = D2DUtilities::toCOLOR_F (fillType.colour);
+ colourBrush->SetColor (colour);
+ }
+ }
- AffineTransform transform;
+ enum BrushTransformFlags
+ {
+ noTransforms = 0,
+ applyWorldTransform = 1,
+ applyInverseWorldTransform = 2,
+ applyFillTypeTransform = 4,
+ applyWorldAndFillTypeTransforms = applyFillTypeTransform | applyWorldTransform
+ };
- Font font;
- float fontHeightToEmSizeFactor = 1.0f;
+ ComSmartPtr getBrush (int flags = applyWorldAndFillTypeTransforms)
+ {
+ if (fillType.isInvisible())
+ return nullptr;
- IDWriteFontFace* currentFontFace = nullptr;
- ComSmartPtr localFontFace;
+ if (! fillType.isGradient() && ! fillType.isTiledImage())
+ return currentBrush;
- Rectangle clipRect;
- bool clipsRect = false, shouldClipRect = false;
+ Point translation{};
+ AffineTransform transform{};
- Image image;
- ComSmartPtr bitmap; // xxx needs a better name - what is this for??
- bool clipsBitmap = false, shouldClipBitmap = false;
+ if ((flags & BrushTransformFlags::applyWorldTransform) != 0)
+ {
+ if (currentTransform.isOnlyTranslated)
+ translation = currentTransform.offset.toFloat();
+ else
+ transform = currentTransform.getTransform();
+ }
- ComSmartPtr complexClipGeometry;
- D2D1_LAYER_PARAMETERS complexClipLayerParams;
- ComSmartPtr complexClipLayer;
- bool clipsComplex = false, shouldClipComplex = false;
+ if ((flags & BrushTransformFlags::applyFillTypeTransform) != 0)
+ {
+ if (fillType.transform.isOnlyTranslation())
+ translation += Point (fillType.transform.getTranslationX(), fillType.transform.getTranslationY());
+ else
+ transform = transform.followedBy (fillType.transform);
+ }
- ComSmartPtr rectListGeometry;
- D2D1_LAYER_PARAMETERS rectListLayerParams;
- ComSmartPtr rectListLayer;
- bool clipsRectList = false, shouldClipRectList = false;
+ if ((flags & BrushTransformFlags::applyInverseWorldTransform) != 0)
+ {
+ if (currentTransform.isOnlyTranslated)
+ translation -= currentTransform.offset.toFloat();
+ else
+ transform = transform.followedBy (currentTransform.getTransform().inverted());
+ }
- Image maskImage;
- D2D1_LAYER_PARAMETERS imageMaskLayerParams;
- ComSmartPtr bitmapMaskLayer;
- ComSmartPtr maskBitmap;
- ComSmartPtr bitmapMaskBrush;
+ if (fillType.isGradient())
+ {
+ const auto p1 = fillType.gradient->point1 + translation;
+ const auto p2 = fillType.gradient->point2 + translation;
- ID2D1Brush* currentBrush = nullptr;
+ if (fillType.gradient->isRadial)
+ {
+ const auto radius = p2.getDistanceFrom (p1);
+ radialGradient->SetRadiusX (radius);
+ radialGradient->SetRadiusY (radius);
+ radialGradient->SetCenter ({ p1.x, p1.y });
+ }
+ else
+ {
+ linearGradient->SetStartPoint ({ p1.x, p1.y });
+ linearGradient->SetEndPoint ({ p2.x, p2.y });
+ }
+ }
+
+ currentBrush->SetTransform (D2DUtilities::transformToMatrix (transform));
+ currentBrush->SetOpacity (fillType.getOpacity());
+
+ return currentBrush;
+ }
+
+ bool doesIntersectClipList (Rectangle r) const noexcept
+ {
+ return deviceSpaceClipList.intersects (r.toFloat());
+ }
+
+ bool doesIntersectClipList (Rectangle r) const noexcept
+ {
+ return deviceSpaceClipList.intersects (r);
+ }
+
+ bool doesIntersectClipList (Line r) const noexcept
+ {
+ return doesIntersectClipList (Rectangle { r.getStart(), r.getEnd() });
+ }
+
+ bool doesIntersectClipList (const RectangleList& other) const noexcept
+ {
+ return deviceSpaceClipList.intersects (other);
+ }
+
+ bool isCurrentTransformAxisAligned() const noexcept
+ {
+ return currentTransform.isOnlyTranslated || (currentTransform.complexTransform.mat01 == 0.0f && currentTransform.complexTransform.mat10 == 0.0f);
+ }
+
+ static String toString (const RenderingHelpers::TranslationOrTransform& t)
+ {
+ String s;
+ s << "Offset " << t.offset.toString() << newLine;
+ s << "Transform " << t.complexTransform.mat00 << " " << t.complexTransform.mat01 << " " << t.complexTransform.mat02 << " / ";
+ s << " " << t.complexTransform.mat10 << " " << t.complexTransform.mat11 << " " << t.complexTransform.mat12 << newLine;
+ return s;
+ }
+
+ PushedLayers layers;
+
+ Direct2DGraphicsContext& owner;
+
+ ComSmartPtr currentBrush = nullptr;
+ ComSmartPtr& colourBrush; // reference to shared colour brush
ComSmartPtr bitmapBrush;
ComSmartPtr linearGradient;
ComSmartPtr radialGradient;
- ComSmartPtr gradientStops;
+
+ RenderingHelpers::TranslationOrTransform currentTransform;
+
+ DxgiAdapter::Ptr& adapter;
+ Direct2DDeviceResources& deviceResources;
+ RectangleList deviceSpaceClipList;
+
+ Font font { FontOptions {} };
FillType fillType;
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState)
+ D2D1_INTERPOLATION_MODE interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR;
+
+ JUCE_LEAK_DETECTOR (SavedState)
+};
+
+static Line operator+ (Line a, Point b)
+{
+ return { a.getStart() + b, a.getEnd() + b };
+}
+
+static RectangleList operator+ (RectangleList a, Point b)
+{
+ a.offsetAll (b);
+ return a;
+}
+
+//==============================================================================
+struct Direct2DGraphicsContext::Pimpl : private DxgiAdapterListener
+{
+protected:
+ Direct2DGraphicsContext& owner;
+ SharedResourcePointer directX;
+ SharedResourcePointer directWrite;
+ RectangleList paintAreas;
+
+ DxgiAdapter::Ptr adapter;
+ Direct2DDeviceResources deviceResources;
+
+ std::vector> savedClientStates;
+
+ virtual HRESULT prepare()
+ {
+ if (! deviceResources.canPaint (adapter))
+ {
+ if (auto hr = deviceResources.create (adapter); FAILED (hr))
+ return hr;
+ }
+
+ return S_OK;
+ }
+
+ virtual void teardown()
+ {
+ deviceResources.release();
+ }
+
+ virtual ComSmartPtr getDeviceContextTarget() const = 0;
+
+ virtual void updatePaintAreas() = 0;
+
+ virtual bool checkPaintReady()
+ {
+ return deviceResources.canPaint (adapter);
+ }
+
+public:
+ Pimpl (Direct2DGraphicsContext& ownerIn, bool opaqueIn)
+ : owner (ownerIn), opaque (opaqueIn)
+ {
+ setTargetAlpha (1.0f);
+
+ directX->adapters.addListener (*this);
+ }
+
+ ~Pimpl() override
+ {
+ directX->adapters.removeListener (*this);
+
+ popAllSavedStates();
+ }
+
+ void setTargetAlpha (float alpha)
+ {
+ backgroundColor = D2DUtilities::toCOLOR_F (Colours::black.withAlpha (opaque ? targetAlpha : 0.0f));
+ targetAlpha = alpha;
+ }
+
+ virtual void clearBackground()
+ {
+ deviceResources.deviceContext.context->Clear (backgroundColor);
+ }
+
+ virtual SavedState* startFrame()
+ {
+ prepare();
+
+ // Anything to paint?
+ updatePaintAreas();
+ auto paintBounds = paintAreas.getBounds();
+
+ if (! getFrameSize().intersects (paintBounds) || paintBounds.isEmpty())
+ return nullptr;
+
+ // Is Direct2D ready to paint?
+ if (! checkPaintReady())
+ return nullptr;
+
+ #if JUCE_DIRECT2D_METRICS
+ owner.metrics->startFrame();
+ #endif
+
+ JUCE_TRACE_EVENT_INT_RECT_LIST (etw::startD2DFrame, etw::direct2dKeyword, owner.getFrameId(), paintAreas);
+
+ // Init device context transform
+ deviceResources.deviceContext.resetTransform();
+
+ const auto effectiveDpi = USER_DEFAULT_SCREEN_DPI * owner.getPhysicalPixelScaleFactor();
+ deviceResources.deviceContext.context->SetDpi (effectiveDpi, effectiveDpi);
+
+ // Start drawing
+ deviceResources.deviceContext.context->SetTarget (getDeviceContextTarget());
+ deviceResources.deviceContext.context->BeginDraw();
+
+ // Init the save state stack and return the first saved state
+ return pushFirstSavedState (paintBounds);
+ }
+
+ virtual HRESULT finishFrame()
+ {
+ // Fully pop the state stack
+ popAllSavedStates();
+
+ // Finish drawing
+ // SetTarget(nullptr) so the device context doesn't hold a reference to the swap chain buffer
+ HRESULT hr = S_OK;
+ {
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (owner.metrics, endDrawDuration)
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endDraw, etw::direct2dKeyword, owner.getFrameId());
+
+ hr = deviceResources.deviceContext.context->EndDraw();
+ deviceResources.deviceContext.context->SetTarget (nullptr);
+ }
+
+ jassert (SUCCEEDED (hr));
+
+ if (FAILED (hr))
+ teardown();
+
+ #if JUCE_DIRECT2D_METRICS
+ owner.metrics->finishFrame();
+ #endif
+
+ return hr;
+ }
+
+ SavedState* getCurrentSavedState() const
+ {
+ return ! savedClientStates.empty() ? savedClientStates.back().get() : nullptr;
+ }
+
+ SavedState* pushFirstSavedState (Rectangle initialClipRegion)
+ {
+ jassert (savedClientStates.empty());
+
+ savedClientStates.push_back (std::make_unique (owner,
+ initialClipRegion,
+ deviceResources.colourBrush,
+ adapter,
+ deviceResources));
+
+ return getCurrentSavedState();
+ }
+
+ SavedState* pushSavedState()
+ {
+ jassert (! savedClientStates.empty());
+
+ savedClientStates.push_back (std::make_unique (*savedClientStates.back()));
+
+ return getCurrentSavedState();
+ }
+
+ SavedState* popSavedState()
+ {
+ savedClientStates.back()->popLayers();
+ savedClientStates.pop_back();
+
+ return getCurrentSavedState();
+ }
+
+ void popAllSavedStates()
+ {
+ while (! savedClientStates.empty())
+ popSavedState();
+ }
+
+ DxgiAdapter& getAdapter() const noexcept
+ {
+ return *adapter;
+ }
+
+ ComSmartPtr getDeviceContext() const noexcept
+ {
+ return deviceResources.deviceContext.context;
+ }
+
+ const auto& getPaintAreas() const noexcept
+ {
+ return paintAreas;
+ }
+
+ virtual Rectangle getFrameSize() = 0;
+
+ void setDeviceContextTransform (AffineTransform transform)
+ {
+ deviceResources.deviceContext.setTransform (transform);
+ }
+
+ void resetDeviceContextTransform()
+ {
+ deviceResources.deviceContext.setTransform ({});
+ }
+
+ auto getDirect2DFactory()
+ {
+ return directX->getD2DFactory();
+ }
+
+ auto getDirectWriteFactory()
+ {
+ return directWrite->getDWriteFactory();
+ }
+
+ auto getDirectWriteFactory4()
+ {
+ return directWrite->getDWriteFactory4();
+ }
+
+ auto& getFontCollection()
+ {
+ return directWrite->getFonts();
+ }
+
+ bool fillSpriteBatch (const RectangleList& list)
+ {
+ if (! owner.currentState->fillType.isColour())
+ return false;
+
+ auto* rectangleListSpriteBatch = deviceResources.rectangleListSpriteBatch.get();
+
+ if (rectangleListSpriteBatch == nullptr)
+ return false;
+
+ auto deviceContext = getDeviceContext();
+
+ if (deviceContext == nullptr)
+ return false;
+
+ owner.applyPendingClipList();
+
+ const auto& transform = owner.currentState->currentTransform;
+
+ if (transform.isOnlyTranslated)
+ {
+ auto translateRectangle = [&] (const Rectangle& r) -> Rectangle
+ {
+ return transform.translated (r);
+ };
+
+ rectangleListSpriteBatch->fillRectangles (deviceContext,
+ list,
+ owner.currentState->fillType.colour,
+ translateRectangle,
+ owner.metrics.get());
+ return true;
+ }
+
+ if (owner.currentState->isCurrentTransformAxisAligned())
+ {
+ auto transformRectangle = [&] (const Rectangle& r) -> Rectangle
+ {
+ return transform.boundsAfterTransform (r);
+ };
+
+ rectangleListSpriteBatch->fillRectangles (deviceContext,
+ list,
+ owner.currentState->fillType.colour,
+ transformRectangle,
+ owner.metrics.get());
+ return true;
+ }
+
+ auto checkRectangleWithoutTransforming = [&] (const Rectangle& r) -> Rectangle
+ {
+ return r;
+ };
+
+ ScopedTransform scopedTransform { *this, owner.currentState };
+ rectangleListSpriteBatch->fillRectangles (deviceContext,
+ list,
+ owner.currentState->fillType.colour,
+ checkRectangleWithoutTransforming,
+ owner.metrics.get());
+
+ return true;
+ }
+
+ template
+ void paintPrimitive (const Shape& shape, Fn&& primitiveOp)
+ {
+ const auto& transform = owner.currentState->currentTransform;
+
+ owner.applyPendingClipList();
+
+ auto deviceContext = deviceResources.deviceContext.context;
+
+ if (deviceContext == nullptr)
+ return;
+
+ const auto fillTransform = transform.isOnlyTranslated
+ ? SavedState::BrushTransformFlags::applyWorldAndFillTypeTransforms
+ : SavedState::BrushTransformFlags::applyFillTypeTransform;
+
+ const auto brush = owner.currentState->getBrush (fillTransform);
+
+ if (brush == nullptr)
+ return;
+
+ if (transform.isOnlyTranslated)
+ {
+ const auto translated = shape + transform.offset.toFloat();
+
+ if (owner.currentState->doesIntersectClipList (translated))
+ primitiveOp (translated, deviceContext, brush);
+ }
+ else if (owner.currentState->doesIntersectClipList (transform.boundsAfterTransform (shape)))
+ {
+ ScopedTransform scopedTransform { *this, owner.currentState };
+ primitiveOp (shape, deviceContext, brush);
+ }
+ }
+
+ DirectWriteGlyphRun glyphRun;
+ bool opaque = true;
+ float targetAlpha = 1.0f;
+ D2D1_COLOR_F backgroundColor{};
+
+private:
+ void adapterCreated (DxgiAdapter::Ptr newAdapter) override
+ {
+ if (! adapter || adapter->uniqueIDMatches (newAdapter))
+ {
+ teardown();
+
+ adapter = newAdapter;
+ }
+ }
+
+ void adapterRemoved (DxgiAdapter::Ptr expiringAdapter) override
+ {
+ if (adapter && adapter->uniqueIDMatches (expiringAdapter))
+ {
+ teardown();
+
+ adapter = nullptr;
+ }
+ }
+
+ HWND hwnd = nullptr;
+
+ #if JUCE_DIRECT2D_METRICS
+ int64 paintStartTicks = 0;
+ #endif
+
+ JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl)
};
//==============================================================================
-Direct2DLowLevelGraphicsContext::Direct2DLowLevelGraphicsContext (HWND hwnd_)
- : hwnd (hwnd_),
- currentState (nullptr),
- pimpl (new Pimpl())
-{
- RECT windowRect;
- GetClientRect (hwnd, &windowRect);
- D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
- bounds.setSize (size.width, size.height);
+Direct2DGraphicsContext::Direct2DGraphicsContext() = default;
+Direct2DGraphicsContext::~Direct2DGraphicsContext() = default;
- if (pimpl->factories->d2dFactory != nullptr)
+bool Direct2DGraphicsContext::startFrame()
+{
+ auto pimpl = getPimpl();
+ currentState = pimpl->startFrame();
+
+ if (currentState == nullptr)
+ return false;
+
+ if (auto deviceContext = pimpl->getDeviceContext())
{
- [[maybe_unused]] auto hr = pimpl->factories->d2dFactory->CreateHwndRenderTarget ({}, { hwnd, size }, pimpl->renderingTarget.resetAndGetPointerAddress());
- jassert (SUCCEEDED (hr));
- hr = pimpl->renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), pimpl->colourBrush.resetAndGetPointerAddress());
+ resetPendingClipList();
+
+ clipToRectangleList (pimpl->getPaintAreas());
+
+ // Clear the buffer *after* setting the clip region
+ clearTargetBuffer();
+
+ // Init font & brush
+ setFont (currentState->font);
+ currentState->updateCurrentBrush();
}
+
+ return true;
}
-Direct2DLowLevelGraphicsContext::~Direct2DLowLevelGraphicsContext()
+void Direct2DGraphicsContext::endFrame()
{
- states.clear();
-}
+ getPimpl()->finishFrame();
-void Direct2DLowLevelGraphicsContext::resized()
-{
- RECT windowRect;
- GetClientRect (hwnd, &windowRect);
- D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
-
- pimpl->renderingTarget->Resize (size);
- bounds.setSize (size.width, size.height);
-}
-
-void Direct2DLowLevelGraphicsContext::clear()
-{
- pimpl->renderingTarget->Clear (D2D1::ColorF (D2D1::ColorF::White, 0.0f)); // xxx why white and not black?
-}
-
-void Direct2DLowLevelGraphicsContext::start()
-{
- pimpl->renderingTarget->BeginDraw();
- saveState();
-}
-
-void Direct2DLowLevelGraphicsContext::end()
-{
- states.clear();
currentState = nullptr;
- pimpl->renderingTarget->EndDraw();
- pimpl->renderingTarget->CheckWindowState();
+ ++frame;
}
-void Direct2DLowLevelGraphicsContext::setOrigin (Point o)
+void Direct2DGraphicsContext::setOrigin (Point o)
{
- addTransform (AffineTransform::translation ((float) o.x, (float) o.y));
+ applyPendingClipList();
+
+ currentState->currentTransform.setOrigin (o);
}
-void Direct2DLowLevelGraphicsContext::addTransform (const AffineTransform& transform)
+void Direct2DGraphicsContext::addTransform (const AffineTransform& transform)
{
- currentState->transform = transform.followedBy (currentState->transform);
+ // The pending clip list is based on the transform stored in currentState, so apply the pending clip list before adding the transform
+ applyPendingClipList();
+
+ currentState->currentTransform.addTransform (transform);
+
+ resetPendingClipList();
}
-float Direct2DLowLevelGraphicsContext::getPhysicalPixelScaleFactor()
+bool Direct2DGraphicsContext::clipToRectangle (const Rectangle& r)
{
- return std::sqrt (std::abs (currentState->transform.getDeterminant()));
-}
+ const auto& transform = currentState->currentTransform;
+ auto& deviceSpaceClipList = currentState->deviceSpaceClipList;
-bool Direct2DLowLevelGraphicsContext::clipToRectangle (const Rectangle& r)
-{
- currentState->clipToRectangle (r);
- return ! isClipEmpty();
-}
-
-bool Direct2DLowLevelGraphicsContext::clipToRectangleList (const RectangleList& clipRegion)
-{
- currentState->clipToRectList (pimpl->rectListToPathGeometry (clipRegion));
- return ! isClipEmpty();
-}
-
-void Direct2DLowLevelGraphicsContext::excludeClipRectangle (const Rectangle&)
-{
- //xxx
-}
-
-void Direct2DLowLevelGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform)
-{
- currentState->clipToPath (pimpl->pathToPathGeometry (path, transform));
-}
-
-void Direct2DLowLevelGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
-{
- currentState->clipToImage (sourceImage, transform);
-}
-
-bool Direct2DLowLevelGraphicsContext::clipRegionIntersects (const Rectangle& r)
-{
- return currentState->clipRect.intersects (r.toFloat().transformedBy (currentState->transform).getSmallestIntegerContainer());
-}
-
-Rectangle Direct2DLowLevelGraphicsContext::getClipBounds() const
-{
- // xxx could this take into account complex clip regions?
- return currentState->clipRect.toFloat().transformedBy (currentState->transform.inverted()).getSmallestIntegerContainer();
-}
-
-bool Direct2DLowLevelGraphicsContext::isClipEmpty() const
-{
- return currentState->clipRect.isEmpty();
-}
-
-void Direct2DLowLevelGraphicsContext::saveState()
-{
- states.add (new SavedState (*this));
- currentState = states.getLast();
-}
-
-void Direct2DLowLevelGraphicsContext::restoreState()
-{
- jassert (states.size() > 1); //you should never pop the last state!
- states.removeLast (1);
- currentState = states.getLast();
-}
-
-void Direct2DLowLevelGraphicsContext::beginTransparencyLayer (float /*opacity*/)
-{
- jassertfalse; //xxx todo
-}
-
-void Direct2DLowLevelGraphicsContext::endTransparencyLayer()
-{
- jassertfalse; //xxx todo
-}
-
-void Direct2DLowLevelGraphicsContext::setFill (const FillType& fillType)
-{
- currentState->setFill (fillType);
-}
-
-void Direct2DLowLevelGraphicsContext::setOpacity (float newOpacity)
-{
- currentState->setOpacity (newOpacity);
-}
-
-void Direct2DLowLevelGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
-{
-}
-
-void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle& r, bool /*replaceExistingContents*/)
-{
- fillRect (r.toFloat());
-}
-
-void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle& r)
-{
- pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
- currentState->createBrush();
- pimpl->renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush);
- pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
-}
-
-void Direct2DLowLevelGraphicsContext::fillRectList (const RectangleList& list)
-{
- for (auto& r : list)
- fillRect (r);
-}
-
-void Direct2DLowLevelGraphicsContext::fillPath (const Path& p, const AffineTransform& transform)
-{
- currentState->createBrush();
- ComSmartPtr geometry (pimpl->pathToPathGeometry (p, transform.followedBy (currentState->transform)));
-
- if (pimpl->renderingTarget != nullptr)
- pimpl->renderingTarget->FillGeometry (geometry, currentState->currentBrush);
-}
-
-void Direct2DLowLevelGraphicsContext::drawImage (const Image& image, const AffineTransform& transform)
-{
- pimpl->renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform)));
-
- D2D1_SIZE_U size = { (UINT32) image.getWidth(), (UINT32) image.getHeight() };
- auto bp = D2D1::BitmapProperties();
-
- Image img (image.convertedToFormat (Image::ARGB));
- Image::BitmapData bd (img, Image::BitmapData::readOnly);
- bp.pixelFormat = pimpl->renderingTarget->GetPixelFormat();
- bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ JUCE_TRACE_EVENT_INT_RECT_LIST (etw::clipToRectangle, etw::direct2dKeyword, getFrameId(), r);
+ // The renderer needs to keep track of the aggregate clip rectangles in order to correctly report the
+ // clip region to the caller. The renderer also needs to push Direct2D clip layers to the device context
+ // to perform the actual clipping. The reported clip region will not necessarily match the Direct2D clip region
+ // if the clip region is transformed, or the clip region is an image or a path.
+ //
+ // Pushing Direct2D clip layers is expensive and there's no need to clip until something is actually drawn.
+ // So - pendingClipList is a list of the areas that need to actually be clipped. Each fill or
+ // draw method then applies any pending clip areas before drawing.
+ //
+ // Also - calling ID2D1DeviceContext::SetTransform is expensive, so check the current transform to see
+ // if the renderer can pre-transform the clip rectangle instead.
+ if (transform.isOnlyTranslated)
{
- ComSmartPtr tempBitmap;
- pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, tempBitmap.resetAndGetPointerAddress());
- if (tempBitmap != nullptr)
- pimpl->renderingTarget->DrawBitmap (tempBitmap);
+ // The current transform is only a translation, so save a few cycles by just adding the
+ // offset instead of transforming the rectangle; the software renderer does something similar.
+ auto translatedR = r.toFloat() + transform.offset.toFloat();
+ deviceSpaceClipList.clipTo (translatedR);
+
+ pendingClipList.clipTo (translatedR);
+ }
+ else if (currentState->isCurrentTransformAxisAligned())
+ {
+ // The current transform is a simple scale + translation, so pre-transform the rectangle
+ auto transformedR = transform.boundsAfterTransform (r.toFloat());
+ deviceSpaceClipList.clipTo (transformedR);
+
+ pendingClipList.clipTo (transformedR);
+ }
+ else
+ {
+ deviceSpaceClipList = getPimpl()->getFrameSize().toFloat();
+
+ // The current transform is too complex to pre-transform the rectangle, so just add the
+ // rectangle to the clip list. The renderer will need to call ID2D1DeviceContext::SetTransform
+ // before applying the clip layer.
+ pendingClipList.clipTo (r.toFloat());
}
- pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
+ return ! isClipEmpty();
}
-void Direct2DLowLevelGraphicsContext::drawLine (const Line& line)
+bool Direct2DGraphicsContext::clipToRectangleList (const RectangleList& newClipList)
{
- // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour
- pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
- currentState->createBrush();
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::clipToRectangleList, etw::direct2dKeyword, getFrameId(), newClipList)
- pimpl->renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()),
- D2D1::Point2F (line.getEndX(), line.getEndY()),
- currentState->currentBrush);
- pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
+ const auto& transform = currentState->currentTransform;
+ auto& deviceSpaceClipList = currentState->deviceSpaceClipList;
+
+ // This works a lot like clipToRect
+
+ // Just one rectangle?
+ if (newClipList.getNumRectangles() == 1)
+ return clipToRectangle (newClipList.getRectangle (0));
+
+ if (transform.isIdentity())
+ {
+ deviceSpaceClipList.clipTo (newClipList);
+
+ pendingClipList.clipTo (newClipList);
+ }
+ else if (currentState->currentTransform.isOnlyTranslated)
+ {
+ RectangleList offsetList (newClipList);
+ offsetList.offsetAll (transform.offset);
+ deviceSpaceClipList.clipTo (offsetList);
+
+ pendingClipList.clipTo (offsetList);
+ }
+ else if (currentState->isCurrentTransformAxisAligned())
+ {
+ RectangleList scaledList;
+
+ for (auto& i : newClipList)
+ scaledList.add (transform.boundsAfterTransform (i.toFloat()));
+
+ deviceSpaceClipList.clipTo (scaledList);
+ pendingClipList.clipTo (scaledList);
+ }
+ else
+ {
+ deviceSpaceClipList = getPimpl()->getFrameSize().toFloat();
+
+ pendingClipList.clipTo (newClipList);
+ }
+
+ return ! isClipEmpty();
}
-void Direct2DLowLevelGraphicsContext::setFont (const Font& newFont)
+void Direct2DGraphicsContext::excludeClipRectangle (const Rectangle& userSpaceExcludedRectangle)
{
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::excludeClipRectangle, etw::direct2dKeyword, getFrameId(), userSpaceExcludedRectangle)
+
+ applyPendingClipList();
+
+ auto& transform = currentState->currentTransform;
+ auto& deviceSpaceClipList = currentState->deviceSpaceClipList;
+ const auto frameSize = getPimpl()->getFrameSize().toFloat();
+
+ if (transform.isOnlyTranslated)
+ {
+ // Just a translation; pre-translate the exclusion area
+ auto translatedR = transform.translated (userSpaceExcludedRectangle.toFloat());
+
+ if (! translatedR.contains (frameSize))
+ {
+ deviceSpaceClipList.subtract (translatedR);
+ pendingClipList.subtract (translatedR);
+ }
+ }
+ else if (currentState->isCurrentTransformAxisAligned())
+ {
+ // Just a scale + translation; pre-transform the exclusion area
+ auto transformedR = transform.boundsAfterTransform (userSpaceExcludedRectangle.toFloat());
+
+ if (! transformedR.contains (frameSize))
+ {
+ deviceSpaceClipList.subtract (transformedR);
+ pendingClipList.subtract (transformedR);
+ }
+ }
+ else
+ {
+ deviceSpaceClipList = frameSize;
+ pendingClipList.subtract (userSpaceExcludedRectangle.toFloat());
+ }
+}
+
+void Direct2DGraphicsContext::setPhysicalPixelScaleFactor (float f)
+{
+ scale = f;
+}
+
+void Direct2DGraphicsContext::resetPendingClipList()
+{
+ auto& transform = currentState->currentTransform;
+
+ const auto frameSize = transform.isOnlyTranslated || currentState->isCurrentTransformAxisAligned()
+ ? getPimpl()->getFrameSize()
+ : getPimpl()->getFrameSize().transformedBy (transform.getTransform().inverted());
+
+ pendingClipList.reset (frameSize.toFloat());
+}
+
+void Direct2DGraphicsContext::applyPendingClipList()
+{
+ auto& transform = currentState->currentTransform;
+ bool const axisAligned = currentState->isCurrentTransformAxisAligned();
+ const auto list = pendingClipList.getList();
+
+ // Clip if the pending clip list is not empty and smaller than the frame size
+ if (! list.containsRectangle (getPimpl()->getFrameSize().toFloat()) && ! list.isEmpty())
+ {
+ if (list.getNumRectangles() == 1 && (transform.isOnlyTranslated || axisAligned))
+ {
+ auto r = list.getRectangle (0);
+ currentState->pushAliasedAxisAlignedClipLayer (r);
+ }
+ else
+ {
+ auto clipTransform = transform.isOnlyTranslated || axisAligned ? AffineTransform{} : transform.getTransform();
+ if (auto clipGeometry = D2DHelpers::rectListToPathGeometry (getPimpl()->getDirect2DFactory(),
+ list,
+ clipTransform,
+ D2D1_FILL_MODE_WINDING,
+ D2D1_FIGURE_BEGIN_FILLED,
+ metrics.get()))
+ {
+ currentState->pushGeometryClipLayer (clipGeometry);
+ }
+ }
+
+ resetPendingClipList();
+ }
+}
+
+void Direct2DGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::clipToPath, etw::direct2dKeyword, getFrameId());
+
+ applyPendingClipList();
+
+ // Set the clip list to the full size of the frame to match
+ // the software renderer
+ auto pathTransform = currentState->currentTransform.getTransformWith (transform);
+ auto transformedBounds = path.getBounds().transformedBy (pathTransform);
+ currentState->deviceSpaceClipList.clipTo (transformedBounds);
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ {
+ currentState->pushGeometryClipLayer (D2DHelpers::pathToPathGeometry (getPimpl()->getDirect2DFactory(),
+ path,
+ pathTransform,
+ D2D1_FIGURE_BEGIN_FILLED,
+ metrics.get()));
+ }
+}
+
+void Direct2DGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::clipToImageAlpha, etw::direct2dKeyword, getFrameId());
+
+ if (sourceImage.isNull())
+ return;
+
+ applyPendingClipList();
+
+ // Put a rectangle clip layer under the image clip layer
+ // The D2D bitmap brush will extend past the boundaries of sourceImage, so clip
+ // to the sourceImage bounds
+ auto brushTransform = currentState->currentTransform.getTransformWith (transform);
+ {
+ D2D1_RECT_F sourceImageRectF = D2DUtilities::toRECT_F (sourceImage.getBounds());
+ ComSmartPtr geometry;
+ getPimpl()->getDirect2DFactory()->CreateRectangleGeometry (sourceImageRectF, geometry.resetAndGetPointerAddress());
+
+ if (geometry)
+ currentState->pushTransformedRectangleGeometryClipLayer (geometry, brushTransform);
+ }
+
+ // Set the clip list to the full size of the frame to match
+ // the software renderer
+ currentState->deviceSpaceClipList = getPimpl()->getFrameSize().toFloat();
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ {
+ // Is this a Direct2D image already?
+ ComSmartPtr d2d1Bitmap;
+
+ if (auto direct2DPixelData = dynamic_cast (sourceImage.getPixelData()))
+ d2d1Bitmap = direct2DPixelData->getAdapterD2D1Bitmap();
+
+ if (! d2d1Bitmap)
+ {
+ // Convert sourceImage to single-channel alpha-only maskImage
+ d2d1Bitmap = Direct2DBitmap::fromImage (sourceImage, deviceContext, Image::SingleChannel);
+ }
+
+ if (d2d1Bitmap)
+ {
+ // Make a transformed bitmap brush using the bitmap
+ // As usual, apply the current transform first *then* the transform parameter
+ ComSmartPtr brush;
+ auto matrix = D2DUtilities::transformToMatrix (brushTransform);
+ D2D1_BRUSH_PROPERTIES brushProps = { 1.0f, matrix };
+
+ auto bitmapBrushProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
+ auto hr = deviceContext->CreateBitmapBrush (d2d1Bitmap, bitmapBrushProps, brushProps, brush.resetAndGetPointerAddress());
+
+ if (SUCCEEDED (hr))
+ {
+ // Push the clipping layer onto the layer stack
+ // Don't set maskTransform in the LayerParameters struct; that only applies to geometry clipping
+ // Do set the contentBounds member, transformed appropriately
+ auto layerParams = D2D1::LayerParameters();
+ auto transformedBounds = sourceImage.getBounds().toFloat().transformedBy (brushTransform);
+ layerParams.contentBounds = D2DUtilities::toRECT_F (transformedBounds);
+ layerParams.opacityBrush = brush;
+
+ currentState->pushLayer (layerParams);
+ }
+ }
+ }
+}
+
+bool Direct2DGraphicsContext::clipRegionIntersects (const Rectangle& r)
+{
+ const auto rect = currentState->currentTransform.isOnlyTranslated ? currentState->currentTransform.translated (r.toFloat())
+ : currentState->currentTransform.boundsAfterTransform (r.toFloat());
+ return currentState->deviceSpaceClipList.intersectsRectangle (rect);
+}
+
+Rectangle Direct2DGraphicsContext::getClipBounds() const
+{
+ return currentState->currentTransform.deviceSpaceToUserSpace (currentState->deviceSpaceClipList.getBounds()).getSmallestIntegerContainer();
+}
+
+bool Direct2DGraphicsContext::isClipEmpty() const
+{
+ return getClipBounds().isEmpty();
+}
+
+void Direct2DGraphicsContext::saveState()
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::saveState, etw::direct2dKeyword, getFrameId());
+
+ applyPendingClipList();
+
+ currentState = getPimpl()->pushSavedState();
+}
+
+void Direct2DGraphicsContext::restoreState()
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::restoreState, etw::direct2dKeyword, getFrameId());
+
+ currentState = getPimpl()->popSavedState();
+
+ currentState->updateColourBrush();
+ jassert (currentState);
+
+ resetPendingClipList();
+}
+
+void Direct2DGraphicsContext::beginTransparencyLayer (float opacity)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::beginTransparencyLayer, etw::direct2dKeyword, getFrameId());
+
+ applyPendingClipList();
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ currentState->pushTransparencyLayer (opacity);
+}
+
+void Direct2DGraphicsContext::endTransparencyLayer()
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::endTransparencyLayer, etw::direct2dKeyword, getFrameId());
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ currentState->popTopLayer();
+}
+
+void Direct2DGraphicsContext::setFill (const FillType& fillType)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::setFill, etw::direct2dKeyword, getFrameId());
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ {
+ currentState->fillType = fillType;
+ currentState->updateCurrentBrush();
+ }
+}
+
+void Direct2DGraphicsContext::setOpacity (float newOpacity)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::setOpacity, etw::direct2dKeyword, getFrameId());
+
+ currentState->setOpacity (newOpacity);
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ currentState->updateCurrentBrush();
+}
+
+void Direct2DGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality quality)
+{
+ switch (quality)
+ {
+ case Graphics::ResamplingQuality::lowResamplingQuality:
+ currentState->interpolationMode = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ break;
+
+ case Graphics::ResamplingQuality::mediumResamplingQuality:
+ currentState->interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR;
+ break;
+
+ case Graphics::ResamplingQuality::highResamplingQuality:
+ currentState->interpolationMode = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
+ break;
+ }
+}
+
+void Direct2DGraphicsContext::fillRect (const Rectangle& r, bool replaceExistingContents)
+{
+ if (replaceExistingContents)
+ {
+ JUCE_SCOPED_TRACE_EVENT_FRAME_RECT_I32 (etw::fillRectReplace, etw::direct2dKeyword, getFrameId(), r);
+
+ applyPendingClipList();
+ clipToRectangle (r);
+ getPimpl()->clearBackground();
+ currentState->popTopLayer();
+ }
+
+ auto fill = [] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ deviceContext->FillRectangle (D2DUtilities::toRECT_F (rect), brush);
+ };
+
+ getPimpl()->paintPrimitive (r.toFloat(), fill);
+}
+
+void Direct2DGraphicsContext::fillRect (const Rectangle& r)
+{
+ auto fill = [] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ deviceContext->FillRectangle (D2DUtilities::toRECT_F (rect), brush);
+ };
+
+ getPimpl()->paintPrimitive (r, fill);
+}
+
+void Direct2DGraphicsContext::fillRectList (const RectangleList& list)
+{
+ if (getPimpl()->fillSpriteBatch (list))
+ return;
+
+ auto fill = [] (const RectangleList& l, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ for (const auto& r : l)
+ deviceContext->FillRectangle (D2DUtilities::toRECT_F (r), brush);
+ };
+
+ getPimpl()->paintPrimitive (list, fill);
+}
+
+void Direct2DGraphicsContext::drawRect (const Rectangle& r, float lineThickness)
+{
+ // ID2D1DeviceContext::DrawRectangle centers the stroke around the edges of the specified rectangle, but
+ // the software renderer contains the stroke within the rectangle
+ // To match the software renderer, reduce the rectangle by half the stroke width
+ if (r.getWidth() * 0.5f < lineThickness || r.getHeight() * 0.5f < lineThickness)
+ return;
+
+ auto draw = [&] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ deviceContext->DrawRectangle (D2DUtilities::toRECT_F (rect), brush, lineThickness);
+ };
+
+ auto reducedR = r.reduced (lineThickness * 0.5f);
+
+ getPimpl()->paintPrimitive (reducedR, draw);
+}
+
+void Direct2DGraphicsContext::fillPath (const Path& p, const AffineTransform& transform)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::fillPath, etw::direct2dKeyword, getFrameId());
+
+ if (p.isEmpty())
+ return;
+
+ applyPendingClipList();
+
+ const auto deviceContext = getPimpl()->getDeviceContext();
+ const auto brush = currentState->getBrush (SavedState::applyFillTypeTransform);
+ const auto factory = getPimpl()->getDirect2DFactory();
+ const auto geometry = D2DHelpers::pathToPathGeometry (factory,
+ p,
+ transform,
+ D2D1_FIGURE_BEGIN_FILLED,
+ metrics.get());
+
+ if (deviceContext == nullptr || brush == nullptr || geometry == nullptr)
+ return;
+
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, fillGeometryTime)
+
+ ScopedTransform scopedTransform { *getPimpl(), currentState };
+ deviceContext->FillGeometry (geometry, brush);
+}
+
+void Direct2DGraphicsContext::strokePath (const Path& p, const PathStrokeType& strokeType, const AffineTransform& transform)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::drawPath, etw::direct2dKeyword, getFrameId());
+
+ if (p.isEmpty())
+ return;
+
+ applyPendingClipList();
+
+ const auto deviceContext = getPimpl()->getDeviceContext();
+ const auto brush = currentState->getBrush (SavedState::applyFillTypeTransform);
+ const auto factory = getPimpl()->getDirect2DFactory();
+ const auto strokeStyle = D2DHelpers::pathStrokeTypeToStrokeStyle (factory, strokeType);
+ const auto geometry = D2DHelpers::pathToPathGeometry (factory,
+ p,
+ transform,
+ D2D1_FIGURE_BEGIN_HOLLOW,
+ metrics.get());
+
+ if (deviceContext == nullptr || brush == nullptr || geometry == nullptr || strokeStyle == nullptr)
+ return;
+
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, drawGeometryTime)
+
+ ScopedTransform scopedTransform { *getPimpl(), currentState };
+ deviceContext->DrawGeometry (geometry, brush, strokeType.getStrokeThickness(), strokeStyle);
+}
+
+void Direct2DGraphicsContext::drawImage (const Image& image, const AffineTransform& transform)
+{
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, drawImageTime)
+
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::drawImage, etw::direct2dKeyword, getFrameId());
+
+ if (image.isNull())
+ return;
+
+ applyPendingClipList();
+
+ if (auto deviceContext = getPimpl()->getDeviceContext())
+ {
+ // Is this a Direct2D image already with the correct format?
+ ComSmartPtr d2d1Bitmap;
+ Rectangle imageClipArea;
+
+ if (auto direct2DPixelData = dynamic_cast (image.getPixelData()))
+ {
+ d2d1Bitmap = direct2DPixelData->getAdapterD2D1Bitmap();
+ imageClipArea = { direct2DPixelData->width, direct2DPixelData->height };
+ }
+
+ if (! d2d1Bitmap || d2d1Bitmap->GetPixelFormat().format != DXGI_FORMAT_B8G8R8A8_UNORM)
+ {
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (Direct2DMetricsHub::getInstance()->imageContextMetrics, createBitmapTime);
+
+ d2d1Bitmap = Direct2DBitmap::fromImage (image, deviceContext, Image::ARGB);
+ imageClipArea = image.getBounds();
+ }
+
+ if (d2d1Bitmap)
+ {
+ auto sourceRectF = D2DUtilities::toRECT_F (imageClipArea);
+
+ auto imageTransform = currentState->currentTransform.getTransformWith (transform);
+
+ if (imageTransform.isOnlyTranslation())
+ {
+ auto destinationRect = D2DUtilities::toRECT_F (imageClipArea.toFloat() + Point { imageTransform.getTranslationX(), imageTransform.getTranslationY() });
+
+ deviceContext->DrawBitmap (d2d1Bitmap,
+ &destinationRect,
+ currentState->fillType.getOpacity(),
+ currentState->interpolationMode,
+ &sourceRectF,
+ {});
+
+ return;
+ }
+
+ if (D2DHelpers::isTransformAxisAligned (imageTransform))
+ {
+ auto destinationRect = D2DUtilities::toRECT_F (imageClipArea.toFloat().transformedBy (imageTransform));
+
+ deviceContext->DrawBitmap (d2d1Bitmap,
+ &destinationRect,
+ currentState->fillType.getOpacity(),
+ currentState->interpolationMode,
+ &sourceRectF,
+ {});
+ return;
+ }
+
+ ScopedTransform scopedTransform { *getPimpl(), currentState, transform };
+ deviceContext->DrawBitmap (d2d1Bitmap,
+ nullptr,
+ currentState->fillType.getOpacity(),
+ currentState->interpolationMode,
+ &sourceRectF,
+ {});
+ }
+ }
+}
+
+void Direct2DGraphicsContext::drawLine (const Line& line)
+{
+ drawLineWithThickness (line, 1.0f);
+}
+
+void Direct2DGraphicsContext::drawLineWithThickness (const Line& line, float lineThickness)
+{
+ auto draw = [&] (Line l, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ const auto makePoint = [] (const auto& x) { return D2D1::Point2F (x.getX(), x.getY()); };
+ deviceContext->DrawLine (makePoint (l.getStart()),
+ makePoint (l.getEnd()),
+ brush,
+ lineThickness);
+ };
+
+ getPimpl()->paintPrimitive (line, draw);
+}
+
+void Direct2DGraphicsContext::setFont (const Font& newFont)
+{
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::setFont, etw::direct2dKeyword, getFrameId());
+
currentState->setFont (newFont);
}
-const Font& Direct2DLowLevelGraphicsContext::getFont()
+const Font& Direct2DGraphicsContext::getFont()
{
return currentState->font;
}
-void Direct2DLowLevelGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform)
+float Direct2DGraphicsContext::getPhysicalPixelScaleFactor() const
{
- currentState->createBrush();
- currentState->createFont();
+ return scale;
+}
- auto hScale = currentState->font.getHorizontalScale();
+void Direct2DGraphicsContext::drawRoundedRectangle (const Rectangle& area, float cornerSize, float lineThickness)
+{
+ auto draw = [&] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ D2D1_ROUNDED_RECT roundedRect { D2DUtilities::toRECT_F (rect), cornerSize, cornerSize };
+ deviceContext->DrawRoundedRectangle (roundedRect, brush, lineThickness);
+ };
- pimpl->renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f)
- .followedBy (transform)
- .followedBy (currentState->transform)));
+ getPimpl()->paintPrimitive (area, draw);
+}
- const auto glyphIndices = (UINT16) glyphNumber;
- const auto glyphAdvances = 0.0f;
- DWRITE_GLYPH_OFFSET offset = { 0.0f, 0.0f };
+void Direct2DGraphicsContext::fillRoundedRectangle (const Rectangle& area, float cornerSize)
+{
+ auto fill = [&] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ D2D1_ROUNDED_RECT roundedRect { D2DUtilities::toRECT_F (rect), cornerSize, cornerSize };
+ deviceContext->FillRoundedRectangle (roundedRect, brush);
+ };
- DWRITE_GLYPH_RUN glyphRun;
- glyphRun.fontFace = currentState->currentFontFace;
- glyphRun.fontEmSize = (FLOAT) (currentState->font.getHeight() * currentState->fontHeightToEmSizeFactor);
- glyphRun.glyphCount = 1;
- glyphRun.glyphIndices = &glyphIndices;
- glyphRun.glyphAdvances = &glyphAdvances;
- glyphRun.glyphOffsets = &offset;
- glyphRun.isSideways = FALSE;
- glyphRun.bidiLevel = 0;
+ getPimpl()->paintPrimitive (area, fill);
+}
- pimpl->renderingTarget->DrawGlyphRun ({}, &glyphRun, currentState->currentBrush);
- pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
+void Direct2DGraphicsContext::drawEllipse (const Rectangle& area, float lineThickness)
+{
+ auto draw = [&] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ auto centre = rect.getCentre();
+ D2D1_ELLIPSE ellipse { { centre.x, centre.y }, rect.proportionOfWidth (0.5f), rect.proportionOfHeight (0.5f) };
+ deviceContext->DrawEllipse (ellipse, brush, lineThickness);
+ };
+
+ getPimpl()->paintPrimitive (area, draw);
+}
+
+void Direct2DGraphicsContext::fillEllipse (const Rectangle& area)
+{
+ auto fill = [&] (Rectangle rect, ComSmartPtr deviceContext, ComSmartPtr brush)
+ {
+ auto centre = rect.getCentre();
+ D2D1_ELLIPSE ellipse { { centre.x, centre.y }, rect.proportionOfWidth (0.5f), rect.proportionOfHeight (0.5f) };
+ deviceContext->FillEllipse (ellipse, brush);
+ };
+
+ getPimpl()->paintPrimitive (area, fill);
+}
+
+void Direct2DGraphicsContext::drawGlyphs (Span glyphNumbers,
+ Span> positions,
+ const AffineTransform& transform)
+{
+ jassert (glyphNumbers.size() == positions.size());
+
+ JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, drawGlyphRunTime)
+
+ JUCE_SCOPED_TRACE_EVENT_FRAME (etw::drawGlyphRun, etw::direct2dKeyword, getFrameId());
+
+ if (currentState->fillType.isInvisible() || glyphNumbers.empty() || positions.empty())
+ return;
+
+ const auto& font = currentState->font;
+
+ const auto deviceContext = getPimpl()->getDeviceContext();
+
+ if (! deviceContext)
+ return;
+
+ const auto typeface = font.getTypefacePtr();
+ const auto fontFace = [&]() -> ComSmartPtr
+ {
+ if (auto* x = dynamic_cast (typeface.get()))
+ return x->getIDWriteFontFace();
+
+ return {};
+ }();
+
+ if (fontFace == nullptr)
+ return;
+
+ const auto brush = currentState->getBrush (SavedState::BrushTransformFlags::applyFillTypeTransform);
+
+ if (! brush)
+ return;
+
+ applyPendingClipList();
+
+ D2D1_POINT_2F baselineOrigin { 0.0f, 0.0f };
+
+ const auto fontScale = font.getHorizontalScale();
+ const auto scaledTransform = AffineTransform::scale (fontScale, 1.0f).followedBy (transform);
+ const auto glyphRunTransform = scaledTransform.followedBy (currentState->currentTransform.getTransform());
+ const auto onlyTranslated = glyphRunTransform.isOnlyTranslation();
+
+ if (onlyTranslated)
+ baselineOrigin = { glyphRunTransform.getTranslationX(), glyphRunTransform.getTranslationY() };
+ else
+ getPimpl()->setDeviceContextTransform (glyphRunTransform);
+
+ auto& run = getPimpl()->glyphRun;
+ run.replace (positions, fontScale);
+
+ DWRITE_GLYPH_RUN directWriteGlyphRun;
+ directWriteGlyphRun.fontFace = fontFace;
+ directWriteGlyphRun.fontEmSize = font.getHeightInPoints();
+ directWriteGlyphRun.glyphCount = (UINT32) glyphNumbers.size();
+ directWriteGlyphRun.glyphIndices = glyphNumbers.data();
+ directWriteGlyphRun.glyphAdvances = run.getAdvances();
+ directWriteGlyphRun.glyphOffsets = run.getOffsets();
+ directWriteGlyphRun.isSideways = FALSE;
+ directWriteGlyphRun.bidiLevel = 0;
+
+ const auto tryDrawColourGlyphs = [&]
+ {
+ // There's a helpful colour glyph rendering sample at
+ // https://github.com/microsoft/Windows-universal-samples/blob/main/Samples/DWriteColorGlyph/cpp/CustomTextRenderer.cpp
+ const auto factory = getPimpl()->getDirectWriteFactory4();
+
+ if (factory == nullptr)
+ return false;
+
+ const auto ctx = deviceContext.getInterface();
+
+ if (ctx == nullptr)
+ return false;
+
+ ComSmartPtr enumerator;
+
+ constexpr auto formats = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
+ | DWRITE_GLYPH_IMAGE_FORMATS_CFF
+ | DWRITE_GLYPH_IMAGE_FORMATS_COLR
+ | DWRITE_GLYPH_IMAGE_FORMATS_PNG
+ | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
+ | DWRITE_GLYPH_IMAGE_FORMATS_TIFF
+ | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
+
+ if (const auto hr = factory->TranslateColorGlyphRun (baselineOrigin,
+ &directWriteGlyphRun,
+ nullptr,
+ formats,
+ DWRITE_MEASURING_MODE_NATURAL,
+ nullptr,
+ 0,
+ enumerator.resetAndGetPointerAddress());
+ FAILED (hr) || enumerator == nullptr)
+ {
+ // NOCOLOR is expected if the font has no colour glyphs. Other errors are not expected.
+ jassert (hr == DWRITE_E_NOCOLOR && enumerator == nullptr);
+ return false;
+ }
+
+ for (BOOL gotRun = false; SUCCEEDED (enumerator->MoveNext (&gotRun)) && gotRun;)
+ {
+ const DWRITE_COLOR_GLYPH_RUN1* colourRun = nullptr;
+
+ if (FAILED (enumerator->GetCurrentRun (&colourRun)) || colourRun == nullptr)
+ break;
+
+ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wswitch-enum")
+ switch (colourRun->glyphImageFormat)
+ {
+ case DWRITE_GLYPH_IMAGE_FORMATS_PNG:
+ case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
+ case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
+ case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8:
+ ctx->DrawColorBitmapGlyphRun (colourRun->glyphImageFormat,
+ { colourRun->baselineOriginX, colourRun->baselineOriginY },
+ &colourRun->glyphRun,
+ colourRun->measuringMode);
+ break;
+
+ case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE:
+ case DWRITE_GLYPH_IMAGE_FORMATS_CFF:
+ case DWRITE_GLYPH_IMAGE_FORMATS_COLR:
+ default:
+ {
+ const auto useForeground = colourRun->paletteIndex == 0xffff;
+ const auto lastColour = currentState->colourBrush->GetColor();
+ const auto colourBrush = currentState->colourBrush;
+
+ if (! useForeground)
+ colourBrush->SetColor (colourRun->runColor);
+
+ const auto brushToUse = useForeground ? ComSmartPtr (brush)
+ : ComSmartPtr (colourBrush);
+
+ ctx->DrawGlyphRun ({ colourRun->baselineOriginX, colourRun->baselineOriginY },
+ &colourRun->glyphRun,
+ colourRun->glyphRunDescription,
+ brushToUse,
+ colourRun->measuringMode);
+
+ if (! useForeground)
+ colourBrush->SetColor (lastColour);
+
+ break;
+ }
+ }
+ JUCE_END_IGNORE_WARNINGS_GCC_LIKE
+ }
+
+ return true;
+ };
+
+ if (! tryDrawColourGlyphs())
+ deviceContext->DrawGlyphRun (baselineOrigin, &directWriteGlyphRun, brush);
+
+ if (! onlyTranslated)
+ getPimpl()->resetDeviceContextTransform();
+}
+
+Direct2DGraphicsContext::ScopedTransform::ScopedTransform (Pimpl& pimplIn, SavedState* stateIn)
+ : pimpl (pimplIn), state (stateIn)
+{
+ pimpl.setDeviceContextTransform (stateIn->currentTransform.getTransform());
+}
+
+Direct2DGraphicsContext::ScopedTransform::ScopedTransform (Pimpl& pimplIn, SavedState* stateIn, const AffineTransform& transform)
+ : pimpl (pimplIn), state (stateIn)
+{
+ pimpl.setDeviceContextTransform (stateIn->currentTransform.getTransformWith (transform));
+}
+
+Direct2DGraphicsContext::ScopedTransform::~ScopedTransform()
+{
+ pimpl.resetDeviceContextTransform();
}
} // namespace juce
diff --git a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h
index 52b0b1e2fc..5576700c3b 100644
--- a/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h
+++ b/modules/juce_graphics/native/juce_Direct2DGraphicsContext_windows.h
@@ -35,23 +35,18 @@
namespace juce
{
-#ifndef _WINDEF_
-class HWND__; // Forward or never
-typedef HWND__* HWND;
-#endif
-
-class Direct2DLowLevelGraphicsContext : public LowLevelGraphicsContext
+class Direct2DGraphicsContext : public LowLevelGraphicsContext
{
public:
- Direct2DLowLevelGraphicsContext (HWND);
- ~Direct2DLowLevelGraphicsContext();
+ Direct2DGraphicsContext();
+ ~Direct2DGraphicsContext() override;
//==============================================================================
bool isVectorDevice() const override { return false; }
void setOrigin (Point) override;
void addTransform (const AffineTransform&) override;
- float getPhysicalPixelScaleFactor() override;
+ float getPhysicalPixelScaleFactor() const override;
bool clipToRectangle (const Rectangle&) override;
bool clipToRectangleList (const RectangleList&) override;
void excludeClipRectangle (const Rectangle&) override;
@@ -83,29 +78,101 @@ public:
void drawLine (const Line&) override;
void setFont (const Font&) override;
const Font& getFont() override;
- void drawGlyph (int glyphNumber, const AffineTransform&) override;
-
- void resized();
- void clear();
-
- void start();
- void end();
+ void drawGlyphs (Span,
+ Span>,
+ const AffineTransform&) override;
//==============================================================================
-private:
+ // These methods were not originally part of the LowLevelGraphicsContext; they
+ // were added because Direct2D supports these drawing primitives directly.
+ // The specialised functions are more efficient than emulating the same behaviour, e.g.
+ // by drawing paths.
+ void drawLineWithThickness (const Line&, float) override;
+
+ void drawEllipse (const Rectangle& area, float lineThickness) override;
+ void fillEllipse (const Rectangle& area) override;
+
+ void drawRect (const Rectangle&, float) override;
+ void strokePath (const Path&, const PathStrokeType& strokeType, const AffineTransform&) override;
+
+ void drawRoundedRectangle (const Rectangle& area, float cornerSize, float lineThickness) override;
+ void fillRoundedRectangle (const Rectangle& area, float cornerSize) override;
+
+ //==============================================================================
+ bool startFrame();
+ void endFrame();
+
+ virtual Image createSnapshot() const { return {}; }
+
+ uint64_t getFrameId() const override { return frame; }
+
+ Direct2DMetrics::Ptr metrics;
+
+ //==============================================================================
+ // Min & max frame sizes; same as Direct3D texture size limits
+ static int constexpr minFrameSize = 1;
+ static int constexpr maxFrameSize = 16384;
+
+ //==============================================================================
+ void setPhysicalPixelScaleFactor (float);
+
+protected:
struct SavedState;
+ SavedState* currentState = nullptr;
- HWND hwnd;
+ class PendingClipList
+ {
+ public:
+ void clipTo (Rectangle i)
+ {
+ list.clipTo (i);
+ }
- SavedState* currentState;
- OwnedArray states;
+ template
+ void clipTo (const RectangleList& other)
+ {
+ list.clipTo (other);
+ }
- Rectangle bounds;
+ void subtract (Rectangle