diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj
index f968942ec..4fe90d87c 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj
@@ -35,8 +35,8 @@
Windows Store
10.0
true
- 10.0.17134.0
- 10.0.17134.0
+ 10.0.17763.0
+ 10.0.17763.0
D2DAdvancedColorImages
78009E8D6FA6B7A714605247C1C6E95F0CDF94CC
D2DAdvancedColorImages_StoreKey.pfx
@@ -144,14 +144,12 @@
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
@@ -160,10 +158,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -187,26 +185,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -230,14 +226,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
@@ -246,10 +240,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -273,26 +267,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -316,14 +308,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 /Od /Zi LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
@@ -332,10 +322,10 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -359,26 +349,24 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
:: Parse the Visual Studio macro into a form usable by fxc.exe.
set INCLUDEPATHS="$(WindowsSDK_IncludePath)"
set INCLUDEPATHS=%INCLUDEPATHS:;=" /I "%
-fxc /T lib_4_0 ReinhardEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)ReinhardEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 ReinhardEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)ReinhardEffect.fxlib" /Fo "$(OutDir)ReinhardEffect.cso" /Fh "$(OutDir)ReinhardEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 FilmicEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)FilmicEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 FilmicEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)FilmicEffect.fxlib" /Fo "$(OutDir)FilmicEffect.cso" /Fh "$(OutDir)FilmicEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SimpleTonemapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SimpleTonemapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SimpleTonemapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SimpleTonemapEffect.fxlib" /Fo "$(OutDir)SimpleTonemapEffect.cso" /Fh "$(OutDir)SimpleTonemapEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 SdrOverlayEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SdrOverlayEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 SdrOverlayEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SdrOverlayEffect.fxlib" /Fo "$(OutDir)SdrOverlayEffect.cso" /Fh "$(OutDir)SdrOverlayEffectShader.h" /nologo /I %INCLUDEPATHS%
fxc /T lib_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /nologo /I %INCLUDEPATHS%
fxc /T ps_4_0 LuminanceHeatmapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)LuminanceHeatmapEffect.fxlib" /Fo "$(OutDir)LuminanceHeatmapEffect.cso" /Fh "$(OutDir)LuminanceHeatmapEffectShader.h" /nologo /I %INCLUDEPATHS%
-fxc /T lib_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
-fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
+fxc /T lib_4_0 SphereMapEffect.hlsl /D D2D_FUNCTION /D D2D_ENTRY=main /Fl "$(ProjectDir)SphereMapEffect.fxlib" /nologo /I %INCLUDEPATHS%
+fxc /T ps_4_0 SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main /E main /setprivate "$(ProjectDir)SphereMapEffect.fxlib" /Fo "$(OutDir)SphereMapEffect.cso" /Fh "$(OutDir)SphereMapEffectShader.h" /nologo /I %INCLUDEPATHS%
Generate shader-linking compatible pixel shaders for D2D custom effects
- $(OutDir)ReinhardEffect.cso;$(OutDir)ReinhardEffectShader.h;$(OutDir)FilmicEffect.cso;$(OutDir)FilmicEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
+ $(OutDir)SimpleTonemapEffect.cso;$(OutDir)SimpleTonemapEffectShader.h;$(OutDir)SdrOverlayEffect.cso;$(OutDir)SdrOverlayEffectShader.h;$(OutDir)LuminanceHeatmapEffect.cso;$(OutDir)LuminanceHeatmapEffectShader.h;$(OutDir)SphereMapEffect.cso;$(OutDir)SphereMapEffectShader.h
- ReinhardEffect.hlsl,FilmicEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
+ SimpleTonemapEffect.hlsl,SdrOverlayEffect.hlsl,LuminanceHeatmapEffect.hlsl,SphereMapEffect.hlsl
true
@@ -432,12 +420,12 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
DirectXPage.xaml
-
-
+
+
@@ -451,7 +439,6 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
DirectXPage.xaml
-
Create
@@ -462,7 +449,7 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
Create
-
+
@@ -479,16 +466,6 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
Designer
-
-
- Document
-
-
-
-
- Document
-
-
Document
@@ -511,6 +488,11 @@ fxc /T ps_4_0 /Od /Zi SphereMapEffect.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=main
+
+
+ Document
+
+
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters
index e11b2e684..cacbc4119 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImages.vcxproj.filters
@@ -29,12 +29,6 @@
Common
-
- RenderEffects
-
-
- RenderEffects
-
RenderEffects
@@ -47,6 +41,9 @@
RenderEffects
+
+ RenderEffects
+
@@ -63,12 +60,6 @@
-
- RenderEffects
-
-
- RenderEffects
-
RenderEffects
@@ -81,6 +72,10 @@
RenderEffects
+
+
+ RenderEffects
+
@@ -196,12 +191,6 @@
-
- RenderEffects
-
-
- RenderEffects
-
RenderEffects
@@ -211,6 +200,9 @@
RenderEffects
+
+ RenderEffects
+
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp
index d6482c892..4506afa9f 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.cpp
@@ -14,6 +14,7 @@
#include "DirectXPage.xaml.h"
#include "DirectXHelper.h"
#include "DirectXTex.h"
+#include "SimpleTonemapEffect.h"
#include "DirectXTex\DirectXTexEXR.h"
using namespace D2DAdvancedColorImages;
@@ -31,6 +32,10 @@ using namespace Windows::Storage::Streams;
using namespace Windows::UI::Input;
using namespace Windows::UI::Xaml;
+static const float sc_DefaultHdrDispMaxNits = 1499.0f; // Experimentally chosen for compatibility with 2018 TVs.
+static const float sc_DefaultSdrDispMaxNits = 270.0f; // Experimentally chosen based on typical SDR displays.
+static const float sc_DefaultImageMaxCLL = 1000.0f; // Needs more tuning based on real world content.
+static const float sc_DefaultImageMedCLL = 200.0f; // Needs more tuning based on real world content.
static const float sc_MaxZoom = 1.0f; // Restrict max zoom to 1:1 scale.
static const unsigned int sc_MaxBytesPerPixel = 16; // Covers all supported image formats.
static const float sc_MinZoomSphereMap = 0.25f;
@@ -51,7 +56,7 @@ D2DAdvancedColorImagesRenderer::D2DAdvancedColorImagesRenderer(
m_minZoom(1.0f), // Dynamically calculated on window size.
m_imageOffset(),
m_pointerPos(),
- m_maxCLL(-1.0f),
+ m_imageCLL{ -1.0f, -1.0f },
m_brightnessAdjust(1.0f),
m_imageInfo{},
m_isComputeSupported(false)
@@ -74,11 +79,7 @@ void D2DAdvancedColorImagesRenderer::CreateDeviceIndependentResources()
{
// Register the custom render effects.
DX::ThrowIfFailed(
- ReinhardEffect::Register(m_deviceResources->GetD2DFactory())
- );
-
- DX::ThrowIfFailed(
- FilmicEffect::Register(m_deviceResources->GetD2DFactory())
+ SimpleTonemapEffect::Register(m_deviceResources->GetD2DFactory())
);
DX::ThrowIfFailed(
@@ -109,7 +110,8 @@ void D2DAdvancedColorImagesRenderer::ReleaseDeviceDependentResources()
// to update the app's sizing and advanced color state.
void D2DAdvancedColorImagesRenderer::CreateWindowSizeDependentResources()
{
- FitImageToWindow();
+ // Window size changes don't require recomputing image HDR metadata.
+ FitImageToWindow(false);
}
// White level scale is used to multiply the color values in the image; allows the user to
@@ -133,15 +135,20 @@ void D2DAdvancedColorImagesRenderer::SetRenderOptions(
// after the effect as their numerical output is affected by any luminance boost.
switch (m_renderEffectKind)
{
- // Effect graph: ImageSource > ColorManagement > WhiteScale > Reinhard
- case RenderEffectKind::ReinhardTonemap:
- m_finalOutput = m_reinhardEffect.Get();
- m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.Get());
- break;
+ // Effect graph: ImageSource > ColorManagement > WhiteScale > HDRTonemap > WhiteScale2*
+ case RenderEffectKind::HdrTonemap:
+ if (m_dispInfo->CurrentAdvancedColorKind != AdvancedColorKind::HighDynamicRange)
+ {
+ // *Second white scale is needed as an integral part of using the Direct2D HDR
+ // tonemapper on SDR/WCG displays to stay within [0, 1] numeric range.
+ m_finalOutput = m_sdrWhiteScaleEffect.Get();
+ }
+ else
+ {
+ m_finalOutput = m_hdrTonemapEffect.Get();
+ }
- // Effect graph: ImageSource > ColorManagement > WhiteScale > FilmicTonemap
- case RenderEffectKind::FilmicTonemap:
- m_finalOutput = m_filmicEffect.Get();
+ m_sdrWhiteScaleEffect->SetInputEffect(0, m_hdrTonemapEffect.Get());
m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.Get());
break;
@@ -174,11 +181,48 @@ void D2DAdvancedColorImagesRenderer::SetRenderOptions(
break;
}
+ float targetMaxNits = GetBestDispMaxLuminance();
+
+ // Update HDR tonemappers with display information.
+ // The 1803 custom tonemapper uses mostly the same property definitions as the 1809 Direct2D tonemapper, for simplicity.
+ DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_OUTPUT_MAX_LUMINANCE, targetMaxNits));
+
+ float maxCLL = m_imageCLL.maxNits != -1.0f ? m_imageCLL.maxNits : sc_DefaultImageMaxCLL;
+ maxCLL *= m_brightnessAdjust;
+
+ // Very low input max luminance can produce unexpected rendering behavior. Restrict to
+ // a reasonable level - the Direct2D tonemapper performs nearly a no-op if input < output max nits.
+ maxCLL = max(maxCLL, 0.5f * targetMaxNits);
+
+ DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_INPUT_MAX_LUMINANCE, maxCLL));
+
+ // The 1809 Direct2D tonemapper also optimizes for HDR or SDR displays; the 1803 custom tonemapper does not.
+ if (DX::CheckPlatformSupport(DX::Win1809))
+ {
+ D2D1_HDRTONEMAP_DISPLAY_MODE mode =
+ m_dispInfo->CurrentAdvancedColorKind == AdvancedColorKind::HighDynamicRange ?
+ D2D1_HDRTONEMAP_DISPLAY_MODE_HDR : D2D1_HDRTONEMAP_DISPLAY_MODE_SDR;
+
+ DX::ThrowIfFailed(m_hdrTonemapEffect->SetValue(D2D1_HDRTONEMAP_PROP_DISPLAY_MODE, mode));
+ }
+
+ // If an HDR tonemapper is used on an SDR or WCG display, perform additional white level correction.
+ if (m_dispInfo->CurrentAdvancedColorKind != AdvancedColorKind::HighDynamicRange)
+ {
+ // Both the D2D and custom HDR tonemappers output values in scRGB using scene-referred luminance - a typical SDR display will
+ // be around numeric range [0.0, 3.0] corresponding to [0, 240 nits]. To encode correctly for an SDR/WCG display
+ // output, we must reinterpret the scene-referred input content (80 nits) as display-referred (targetMaxNits).
+ DX::ThrowIfFailed(
+ m_sdrWhiteScaleEffect->SetValue(D2D1_WHITELEVELADJUSTMENT_PROP_INPUT_WHITE_LEVEL, D2D1_SCENE_REFERRED_SDR_WHITE_LEVEL));
+
+ DX::ThrowIfFailed(
+ m_sdrWhiteScaleEffect->SetValue(D2D1_WHITELEVELADJUSTMENT_PROP_OUTPUT_WHITE_LEVEL, targetMaxNits));
+ }
+
Draw();
}
-// Reads the provided data stream and decodes an image from it using WIC. These resources are device-
-// independent.
+// Reads the provided data stream and decodes an image from it using WIC. These resources are device-independent.
ImageInfo D2DAdvancedColorImagesRenderer::LoadImageFromWic(_In_ IStream* imageStream)
{
auto wicFactory = m_deviceResources->GetWicImagingFactory();
@@ -368,14 +412,31 @@ void D2DAdvancedColorImagesRenderer::CreateImageDependentResources()
// Set the actual matrix in SetRenderOptions.
// Instantiate and cache all of the tonemapping/render effects.
- // Each effect is implemented as a Direct2D custom effect; see the RenderEffects filter in the
+ // Some effects are implemented as Direct2D custom effects; see the RenderEffects filter in the
// Solution Explorer.
+
+ GUID sdrWhiteScale = {};
+ GUID tonemapper = {};
+ if (DX::CheckPlatformSupport(DX::Win1809))
+ {
+ // HDR tonemapper and white level adjust are only available in 1809 and above.
+ tonemapper = CLSID_D2D1HdrToneMap;
+ sdrWhiteScale = CLSID_D2D1WhiteLevelAdjustment;
+ }
+ else
+ {
+ tonemapper = CLSID_CustomSimpleTonemapEffect;
+
+ // For 1803, this effect should never actually be rendered. Invert is a good "sentinel".
+ sdrWhiteScale = CLSID_D2D1Invert;
+ }
+
DX::ThrowIfFailed(
- context->CreateEffect(CLSID_CustomReinhardEffect, &m_reinhardEffect)
+ context->CreateEffect(tonemapper, &m_hdrTonemapEffect)
);
DX::ThrowIfFailed(
- context->CreateEffect(CLSID_CustomFilmicEffect, &m_filmicEffect)
+ context->CreateEffect(sdrWhiteScale, &m_sdrWhiteScaleEffect)
);
DX::ThrowIfFailed(
@@ -401,8 +462,7 @@ void D2DAdvancedColorImagesRenderer::CreateImageDependentResources()
border->SetValue(D2D1_BORDER_PROP_EDGE_MODE_Y, D2D1_BORDER_EDGE_MODE_WRAP);
border->SetInputEffect(0, m_whiteScaleEffect.Get());
- m_reinhardEffect->SetInputEffect(0, m_whiteScaleEffect.Get());
- m_filmicEffect->SetInputEffect(0, m_whiteScaleEffect.Get());
+ m_hdrTonemapEffect->SetInputEffect(0, m_whiteScaleEffect.Get());
m_sphereMapEffect->SetInputEffect(0, border.Get());
// SphereMap needs to know the pixel size of the image.
@@ -460,8 +520,8 @@ void D2DAdvancedColorImagesRenderer::CreateHistogramResources()
// HDR10 image data is encoded as [0, 1] UNORM values, which represents [0, 10000] nits.
// This should be converted to scRGB [0, 125] FP16 values (10000 / 80 nits reference), but
// instead remains as scRGB [0, 1] FP16 values, or [0, 80] nits.
- // This is fixed in Windows 10 version 1809 (UniversalApiContract version 7).
- if (m_imageInfo.isXboxHdrScreenshot && !Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7))
+ // This is fixed in Windows 10 version 1809.
+ if (m_imageInfo.isXboxHdrScreenshot && !DX::CheckPlatformSupport(DX::Win1809))
{
scale /= 125.0f;
}
@@ -591,8 +651,8 @@ void D2DAdvancedColorImagesRenderer::ReleaseImageDependentResources()
m_scaledImage.Reset();
m_colorManagementEffect.Reset();
m_whiteScaleEffect.Reset();
- m_reinhardEffect.Reset();
- m_filmicEffect.Reset();
+ m_sdrWhiteScaleEffect.Reset();
+ m_hdrTonemapEffect.Reset();
m_sdrOverlayEffect.Reset();
m_heatmapEffect.Reset();
m_histogramPrescale.Reset();
@@ -658,8 +718,9 @@ void D2DAdvancedColorImagesRenderer::UpdateManipulationState(_In_ ManipulationUp
}
// Overrides any pan/zoom state set by the user to fit image to the window size.
-// Returns the computed MaxCLL of the image in nits.
-float D2DAdvancedColorImagesRenderer::FitImageToWindow()
+// Returns the computed content light level (CLL) of the image in nits.
+// Recomputing the HDR metadata is only needed when loading a new image.
+ImageCLL D2DAdvancedColorImagesRenderer::FitImageToWindow(bool computeMetadata)
{
if (m_imageSource)
{
@@ -691,7 +752,7 @@ float D2DAdvancedColorImagesRenderer::FitImageToWindow()
ComputeHdrMetadata();
}
- return m_maxCLL;
+ return m_imageCLL;
}
// After initial decode, obtain image information and do common setup.
@@ -905,8 +966,8 @@ void D2DAdvancedColorImagesRenderer::UpdateWhiteLevelScale(float brightnessAdjus
// HDR10 image data is encoded as [0, 1] UNORM values, which represents [0, 10000] nits.
// This should be converted to scRGB [0, 125] FP16 values (10000 / 80 nits reference), but
// instead remains as scRGB [0, 1] FP16 values, or [0, 80] nits.
- // This is fixed in Windows 10 version 1809 (UniversalApiContract version 7).
- if (m_imageInfo.isXboxHdrScreenshot && !Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7))
+ // This is fixed in Windows 10 version 1809.
+ if (m_imageInfo.isXboxHdrScreenshot && !DX::CheckPlatformSupport(DX::Win1809))
{
scale *= 125.0f;
}
@@ -957,14 +1018,15 @@ void D2DAdvancedColorImagesRenderer::UpdateImageTransformState()
}
}
-// Uses a histogram to compute a modified version of MaxCLL (ST.2086 max content light level).
+// Uses a histogram to compute a modified version of maximum content light level/ST.2086 MaxCLL
+// and average content light level.
// Performs Begin/EndDraw on the D2D context.
void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata()
{
// Initialize with a sentinel value.
- m_maxCLL = -1.0f;
+ m_imageCLL = { -1.0f, -1.0f };
- // MaxCLL is not meaningful for SDR or WCG images.
+ // HDR metadata is not meaningful for SDR or WCG images.
if ((!m_isComputeSupported) ||
(m_imageInfo.imageKind != AdvancedColorKind::HighDynamicRange))
{
@@ -999,23 +1061,41 @@ void D2DAdvancedColorImagesRenderer::ComputeHdrMetadata()
);
unsigned int maxCLLbin = 0;
+ unsigned int avgCLLbin = 0; // Average is defined as 50th percentile.
float runningSum = 0.0f; // Cumulative sum of values in histogram is 1.0.
for (int i = sc_histNumBins - 1; i >= 0; i--)
{
runningSum += histogramData[i];
- maxCLLbin = i;
- if (runningSum >= 1.0f - maxCLLPercent)
+ // Note the inequality (<) is the opposite of the next if block.
+ if (runningSum < 1.0f - maxCLLPercent)
{
+ maxCLLbin = i;
+ }
+
+ if (runningSum > 0.5f)
+ {
+ // Note if the entire histogram is 0, avgCLLbin remains at -1.
+ avgCLLbin = i;
break;
}
}
- float binNorm = static_cast(maxCLLbin) / static_cast(sc_histNumBins);
- m_maxCLL = powf(binNorm, 1 / sc_histGamma) * sc_histMaxNits;
+ float binNormMax = static_cast(maxCLLbin) / static_cast(sc_histNumBins);
+ m_imageCLL.maxNits = powf(binNormMax, 1 / sc_histGamma) * sc_histMaxNits;
+
+ float binNormAvg = static_cast(avgCLLbin) / static_cast(sc_histNumBins);
+ m_imageCLL.medNits = powf(binNormAvg, 1 / sc_histGamma) * sc_histMaxNits;
+
+ // Some drivers have a bug where histogram will always return 0. Or some images are pure black.
+ // Treat these cases as unknown.
+ if (m_imageCLL.maxNits == 0.0f)
+ {
+ m_imageCLL = { -1.0f, -1.0f };
+ }
- // Some drivers have a bug where histogram will always return 0. Treat this as unknown.
- m_maxCLL = (m_maxCLL == 0.0f) ? -1.0f : m_maxCLL;
+ // HDR metadata computation is completed before the app rendering options are known, so don't
+ // attempt to draw yet.
}
// Set HDR10 metadata to allow HDR displays to optimize behavior based on our content.
@@ -1046,7 +1126,7 @@ void D2DAdvancedColorImagesRenderer::EmitHdrMetadata()
// Currently only the "None" render effect results in pixel values that exceed
// the OS-specified SDR white level, as it just passes through HDR color values.
case RenderEffectKind::None:
- effectiveMaxCLL = max(m_maxCLL, 0.0f) * m_brightnessAdjust;
+ effectiveMaxCLL = max(m_imageCLL.maxNits, 0.0f) * m_brightnessAdjust;
break;
default:
@@ -1069,6 +1149,29 @@ void D2DAdvancedColorImagesRenderer::EmitHdrMetadata()
}
}
+// If AdvancedColorInfo does not have valid data, picks an appropriate default value.
+float D2DAdvancedColorImagesRenderer::GetBestDispMaxLuminance()
+{
+ float val = m_dispInfo->MaxLuminanceInNits;
+
+ if (val == 0.0f)
+ {
+ if (m_dispInfo->CurrentAdvancedColorKind == AdvancedColorKind::HighDynamicRange)
+ {
+ // HDR TVs generally don't report metadata, but monitors do.
+ val = sc_DefaultHdrDispMaxNits;
+ }
+ else
+ {
+ // Almost no SDR displays report HDR metadata. WCG displays generally should report HDR metadata.
+ // We assume both SDR and WCG displays have similar peak luminances and use the same constants.
+ val = sc_DefaultSdrDispMaxNits;
+ }
+ }
+
+ return val;
+}
+
// Renders the loaded image with user-specified options.
void D2DAdvancedColorImagesRenderer::Draw()
{
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h
index b9f7a38b4..2b1076b62 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/D2DAdvancedColorImagesRenderer.h
@@ -12,8 +12,6 @@
#pragma once
#include "DeviceResources.h"
-#include "ReinhardEffect.h"
-#include "FilmicEffect.h"
#include "SdrOverlayEffect.h"
#include "LuminanceHeatmapEffect.h"
#include "SphereMapEffect.h"
@@ -33,6 +31,12 @@ namespace D2DAdvancedColorImages
bool isValid;
};
+ struct ImageCLL
+ {
+ float maxNits;
+ float medNits;
+ };
+
class D2DAdvancedColorImagesRenderer : public DX::IDeviceNotify
{
public:
@@ -51,10 +55,11 @@ namespace D2DAdvancedColorImages
void UpdateManipulationState(_In_ Windows::UI::Input::ManipulationUpdatedEventArgs^ args);
- // Returns the computed MaxCLL of the image in nits. While HDR metadata is a
- // property of the image (and independent of rendering), our implementation
+ // Returns the computed MaxCLL and AvgCLL of the image in nits. While HDR metadata is a
+ // property of the image (and is independent of rendering), our implementation
// can't compute it until this point.
- float FitImageToWindow();
+ ImageCLL FitImageToWindow(bool computeMetadata);
+
void SetRenderOptions(
RenderEffectKind effect,
float brightnessAdjustment,
@@ -89,6 +94,8 @@ namespace D2DAdvancedColorImages
void ComputeHdrMetadata();
void EmitHdrMetadata();
+ float GetBestDispMaxLuminance();
+
// Cached pointer to device resources.
std::shared_ptr m_deviceResources;
@@ -99,8 +106,8 @@ namespace D2DAdvancedColorImages
Microsoft::WRL::ComPtr m_scaledImage;
Microsoft::WRL::ComPtr m_colorManagementEffect;
Microsoft::WRL::ComPtr m_whiteScaleEffect;
- Microsoft::WRL::ComPtr m_reinhardEffect;
- Microsoft::WRL::ComPtr m_filmicEffect;
+ Microsoft::WRL::ComPtr m_sdrWhiteScaleEffect;
+ Microsoft::WRL::ComPtr m_hdrTonemapEffect;
Microsoft::WRL::ComPtr m_sdrOverlayEffect;
Microsoft::WRL::ComPtr m_heatmapEffect;
Microsoft::WRL::ComPtr m_sphereMapEffect;
@@ -114,7 +121,7 @@ namespace D2DAdvancedColorImages
float m_minZoom;
D2D1_POINT_2F m_imageOffset;
D2D1_POINT_2F m_pointerPos;
- float m_maxCLL; // In nits.
+ ImageCLL m_imageCLL;
float m_brightnessAdjust;
Windows::Graphics::Display::AdvancedColorInfo^ m_dispInfo;
ImageInfo m_imageInfo;
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h
index bdfccd627..047613790 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXHelper.h
@@ -24,6 +24,37 @@ namespace DX
}
}
+ enum OSVer
+ {
+ Win1803,
+ Win1809
+ };
+
+ // Check for OS version. Common helper function makes it easier to do testing/mockups.
+ // DirectX doesn't allow us to check for a specific feature's presence, so instead
+ // we check the OS version (API contract).
+ inline bool CheckPlatformSupport(OSVer version)
+ {
+ int apiLevel = 0;
+ switch (version)
+ {
+ case Win1803:
+ apiLevel = 6;
+ break;
+
+ case Win1809:
+ apiLevel = 7;
+ break;
+
+ default:
+ throw ref new Platform::InvalidArgumentException();
+ break;
+ }
+
+ return Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent(
+ "Windows.Foundation.UniversalApiContract", apiLevel);
+ }
+
// Function that reads from a binary file asynchronously.
inline Concurrency::task> ReadDataAsync(const std::wstring& filename)
{
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml
index f88f64cff..0060c2a76 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml
@@ -39,6 +39,7 @@
+
-
+
@@ -68,7 +70,7 @@
-
+
Render Effect:
@@ -82,7 +84,18 @@
Adjust brightness:
-
+
Image information
@@ -91,6 +104,7 @@
Bit depth:
Floating point:
Estimated MaxCLL:
+ Estimated MedCLL:
Display information
Kind:
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp
index 30b197c64..6fc399c79 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.cpp
@@ -41,7 +41,7 @@ DirectXPage::DirectXPage() :
m_isWindowVisible(true),
m_imageInfo{},
m_isImageValid(false),
- m_imageMaxCLL(-1.0f)
+ m_imageCLL{ -1.0f, -1.0f }
{
InitializeComponent();
@@ -194,7 +194,7 @@ void DirectXPage::LoadImage(_In_ StorageFile^ imageFile)
m_imageInfo = info;
m_renderer->CreateImageDependentResources();
- m_imageMaxCLL = m_renderer->FitImageToWindow();
+ m_imageCLL = m_renderer->FitImageToWindow(true); // On first load of image, need to generate HDR metadata.
ApplicationView::GetForCurrentView()->Title = imageFile->Name;
ImageACKind->Text = L"Kind: " + ConvertACKindToString(m_imageInfo.imageKind);
@@ -204,17 +204,30 @@ void DirectXPage::LoadImage(_In_ StorageFile^ imageFile)
std::wstringstream cllStr;
cllStr << L"Estimated MaxCLL: ";
- if (m_imageMaxCLL < 0.0f)
+ if (m_imageCLL.maxNits < 0.0f)
{
cllStr << L"N/A";
}
else
{
- cllStr << std::to_wstring(static_cast(m_imageMaxCLL)) << L" nits";
+ cllStr << std::to_wstring(static_cast(m_imageCLL.maxNits)) << L" nits";
}
ImageMaxCLL->Text = ref new String(cllStr.str().c_str());
+ std::wstringstream avgStr;
+ avgStr << L"Estimated MedCLL: ";
+ if (m_imageCLL.medNits < 0.0f)
+ {
+ avgStr << L"N/A";
+ }
+ else
+ {
+ avgStr << std::to_wstring(static_cast(m_imageCLL.medNits)) << L" nits";
+ }
+
+ ImageAvgCLL->Text = ref new String(avgStr.str().c_str());
+
// Image loading is done at this point.
m_isImageValid = true;
BrightnessAdjustSlider->IsEnabled = true;
@@ -323,32 +336,17 @@ void DirectXPage::UpdateDefaultRenderOptions()
case AdvancedColorKind::WideColorGamut:
default:
// SDR and WCG images don't need to be tonemapped.
- RenderEffectCombo->SelectedIndex = 2; // See RenderOptions.h for which value this indicates.
+ RenderEffectCombo->SelectedIndex = 0; // See RenderOptions.h for which value this indicates.
// Manual brightness adjustment is only useful for HDR content.
// SDR and WCG content is adjusted by the OS-provided AdvancedColorInfo::SdrWhiteLevel parameter.
- BrightnessAdjustSlider->Value = 1.0f;
+ BrightnessAdjustSlider->Value = SdrBrightnessFormatter::BrightnessToSlider(1.0f);
BrightnessAdjustPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
break;
case AdvancedColorKind::HighDynamicRange:
- auto acKind = m_dispInfo ? m_dispInfo->CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
-
- switch (acKind)
- {
- case AdvancedColorKind::StandardDynamicRange:
- case AdvancedColorKind::WideColorGamut:
- default:
- // HDR content + non-HDR display: HDR tonemapping is needed for correct rendering.
- RenderEffectCombo->SelectedIndex = 0; // See RenderOptions.h for which value this indicates.
- break;
-
- case AdvancedColorKind::HighDynamicRange:
- // HDR content + HDR display: HDR tonemapping is needed for best results, but this sample's
- // tonemappers are very simple and not suitable, so just disable it.
- RenderEffectCombo->SelectedIndex = 2; // See RenderOptions.h for which value this indicates.
- break;
- }
+ // HDR images need to be tonemapped regardless of display kind.
+ RenderEffectCombo->SelectedIndex = 1; // See RenderOptions.h for which value this indicates.
// Manual brightness adjustment is useful for any HDR content.
BrightnessAdjustPanel->Visibility = Windows::UI::Xaml::Visibility::Visible;
@@ -367,7 +365,7 @@ void DirectXPage::UpdateRenderOptions()
m_renderer->SetRenderOptions(
tm->Kind,
- static_cast(BrightnessAdjustSlider->Value),
+ SdrBrightnessFormatter::SliderToBrightness(BrightnessAdjustSlider->Value),
m_dispInfo
);
}
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h
index 2ef1060e1..fcba5f302 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/DirectXPage.xaml.h
@@ -16,6 +16,7 @@
#include "DeviceResources.h"
#include "D2DAdvancedColorImagesRenderer.h"
#include "RenderOptions.h"
+#include "SdrBrightnessFormatter.h"
namespace D2DAdvancedColorImages
{
@@ -88,7 +89,7 @@ namespace D2DAdvancedColorImages
// Cached information for UI.
D2DAdvancedColorImages::ImageInfo m_imageInfo;
- float m_imageMaxCLL;
+ D2DAdvancedColorImages::ImageCLL m_imageCLL;
bool m_isImageValid;
Windows::Graphics::Display::AdvancedColorInfo^ m_dispInfo;
RenderOptionsViewModel^ m_renderOptionsViewModel;
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp
deleted file mode 100644
index ef8bf8a6c..000000000
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-//*********************************************************
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-//*********************************************************
-
-#include "pch.h"
-#include
-#include "FilmicEffect.h"
-#include "BasicReaderWriter.h"
-
-#define XML(X) TEXT(#X)
-
-FilmicEffect::FilmicEffect() :
- m_refCount(1)
-{
-}
-
-HRESULT __stdcall FilmicEffect::CreateFilmicImpl(_Outptr_ IUnknown** ppEffectImpl)
-{
- // Since the object's refcount is initialized to 1, we don't need to AddRef here.
- *ppEffectImpl = static_cast(new (std::nothrow) FilmicEffect());
-
- if (*ppEffectImpl == nullptr)
- {
- return E_OUTOFMEMORY;
- }
- else
- {
- return S_OK;
- }
-}
-
-HRESULT FilmicEffect::Register(_In_ ID2D1Factory1* pFactory)
-{
- // The inspectable metadata of an effect is defined in XML. This can be passed in from an external source
- // as well, however for simplicity we just inline the XML.
- PCWSTR pszXml =
- XML(
-
-
-
-
-
-
-
-
-
-
-
-
- );
-
- // This registers the effect with the factory, which will make the effect
- // instantiatable.
- return pFactory->RegisterEffectFromString(
- CLSID_CustomFilmicEffect,
- pszXml,
- nullptr,
- 0,
- CreateFilmicImpl
- );
-}
-
-IFACEMETHODIMP FilmicEffect::Initialize(
- _In_ ID2D1EffectContext* pEffectContext,
- _In_ ID2D1TransformGraph* pTransformGraph
-)
-{
- // To maintain consistency across different DPIs, this effect needs to cover more pixels at
- // higher than normal DPIs. The context is saved here so the effect can later retrieve the DPI.
- m_effectContext = pEffectContext;
-
- BasicReaderWriter^ reader = ref new BasicReaderWriter();
- Platform::Array^ data;
-
- try
- {
- data = reader->ReadData("FilmicEffect.cso");
- }
- catch (Platform::Exception^ e)
- {
- // Return error if file can not be read.
- return e->HResult;
- }
-
- HRESULT hr = pEffectContext->LoadPixelShader(GUID_FilmicEffectPixelShader, data->Data, data->Length);
-
- // This loads the shader into the Direct2D image effects system and associates it with the GUID passed in.
- // If this method is called more than once (say by other instances of the effect) with the same GUID,
- // the system will simply do nothing, ensuring that only one instance of a shader is stored regardless of how
- // many time it is used.
- if (SUCCEEDED(hr))
- {
- // The graph consists of a single transform. In fact, this class is the transform,
- // reducing the complexity of implementing an effect when all we need to
- // do is use a single pixel shader.
- hr = pTransformGraph->SetSingleTransformNode(this);
- }
-
- return hr;
-}
-
-HRESULT FilmicEffect::UpdateConstants()
-{
- // Update the DPI if it has changed. This allows the effect to scale across different DPIs automatically.
- m_effectContext->GetDpi(&m_dpi, &m_dpi);
- m_constants.dpi = m_dpi;
-
- return m_drawInfo->SetPixelShaderConstantBuffer(reinterpret_cast(&m_constants), sizeof(m_constants));
-}
-
-IFACEMETHODIMP FilmicEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType)
-{
- return UpdateConstants();
-}
-
-// SetGraph is only called when the number of inputs changes. This never happens as we publish this effect
-// as a single input effect.
-IFACEMETHODIMP FilmicEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph)
-{
- return E_NOTIMPL;
-}
-
-// Called to assign a new render info class, which is used to inform D2D on
-// how to set the state of the GPU.
-IFACEMETHODIMP FilmicEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo)
-{
- m_drawInfo = pDrawInfo;
-
- return m_drawInfo->SetPixelShader(GUID_FilmicEffectPixelShader);
-}
-
-// Calculates the mapping between the output and input rects.
-IFACEMETHODIMP FilmicEffect::MapOutputRectToInputRects(
- _In_ const D2D1_RECT_L* pOutputRect,
- _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects,
- UINT32 inputRectCount
- ) const
-{
- // This effect has exactly one input, so if there is more than one input rect,
- // something is wrong.
- if (inputRectCount != 1)
- {
- return E_INVALIDARG;
- }
-
- pInputRects[0].left = pOutputRect->left;
- pInputRects[0].top = pOutputRect->top;
- pInputRects[0].right = pOutputRect->right;
- pInputRects[0].bottom = pOutputRect->bottom;
-
- return S_OK;
-}
-
-IFACEMETHODIMP FilmicEffect::MapInputRectsToOutputRect(
- _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects,
- _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects,
- UINT32 inputRectCount,
- _Out_ D2D1_RECT_L* pOutputRect,
- _Out_ D2D1_RECT_L* pOutputOpaqueSubRect
- )
-{
- // This effect has exactly one input, so if there is more than one input rect,
- // something is wrong.
- if (inputRectCount != 1)
- {
- return E_INVALIDARG;
- }
-
- *pOutputRect = pInputRects[0];
- m_inputRect = pInputRects[0];
-
- // Indicate that entire output might contain transparency.
- ZeroMemory(pOutputOpaqueSubRect, sizeof(*pOutputOpaqueSubRect));
-
- return S_OK;
-}
-
-IFACEMETHODIMP FilmicEffect::MapInvalidRect(
- UINT32 inputIndex,
- D2D1_RECT_L invalidInputRect,
- _Out_ D2D1_RECT_L* pInvalidOutputRect
- ) const
-{
- HRESULT hr = S_OK;
-
- // Indicate that the entire output may be invalid.
- *pInvalidOutputRect = m_inputRect;
-
- return hr;
-}
-
-IFACEMETHODIMP_(UINT32) FilmicEffect::GetInputCount() const
-{
- return 1;
-}
-
-// D2D ensures that that effects are only referenced from one thread at a time.
-// To improve performance, we simply increment/decrement our reference count
-// rather than use atomic InterlockedIncrement()/InterlockedDecrement() functions.
-IFACEMETHODIMP_(ULONG) FilmicEffect::AddRef()
-{
- m_refCount++;
- return m_refCount;
-}
-
-IFACEMETHODIMP_(ULONG) FilmicEffect::Release()
-{
- m_refCount--;
-
- if (m_refCount == 0)
- {
- delete this;
- return 0;
- }
- else
- {
- return m_refCount;
- }
-}
-
-// This enables the stack of parent interfaces to be queried.
-IFACEMETHODIMP FilmicEffect::QueryInterface(
- _In_ REFIID riid,
- _Outptr_ void** ppOutput
- )
-{
- *ppOutput = nullptr;
- HRESULT hr = S_OK;
-
- if (riid == __uuidof(ID2D1EffectImpl))
- {
- *ppOutput = reinterpret_cast(this);
- }
- else if (riid == __uuidof(ID2D1DrawTransform))
- {
- *ppOutput = static_cast(this);
- }
- else if (riid == __uuidof(ID2D1Transform))
- {
- *ppOutput = static_cast(this);
- }
- else if (riid == __uuidof(ID2D1TransformNode))
- {
- *ppOutput = static_cast(this);
- }
- else if (riid == __uuidof(IUnknown))
- {
- *ppOutput = this;
- }
- else
- {
- hr = E_NOINTERFACE;
- }
-
- if (*ppOutput != nullptr)
- {
- AddRef();
- }
-
- return hr;
-}
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl
deleted file mode 100644
index 8fc254b3f..000000000
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.hlsl
+++ /dev/null
@@ -1,63 +0,0 @@
-
-//*********************************************************
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-//*********************************************************
-
-// Custom effects using pixel shaders should use HLSL helper functions defined in
-// d2d1effecthelpers.hlsli to make use of effect shader linking.
-#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture.
-#define D2D_INPUT0_SIMPLE
-
-// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe.
-#include "d2d1effecthelpers.hlsli"
-
-cbuffer constants : register(b0)
-{
- float dpi : packoffset(c1.z);
-};
-
-static const float3x3 ACESInputMat =
-{
- { 0.59719, 0.35458, 0.04823 },
- { 0.07600, 0.90834, 0.01566 },
- { 0.02840, 0.13383, 0.83777 }
-};
-
-// ODT_SAT => XYZ => D60_2_D65 => sRGB
-static const float3x3 ACESOutputMat =
-{
- { 1.60475, -0.53108, -0.07367 },
- { -0.10208, 1.10813, -0.00605 },
- { -0.00327, -0.07276, 1.07602 }
-};
-
-float3 RRTAndODTFit(float3 v)
-{
- float3 a = v * (v + 0.0245786f) - 0.000090537f;
- float3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f;
- return a / b;
-}
-
-D2D_PS_ENTRY(main)
-{
- float4 color = D2DGetInput(0);
- float3 filmic = mul(ACESInputMat, color.rgb);
-
- // Apply RRT and ODT
- filmic = RRTAndODTFit(filmic);
-
- filmic = mul(ACESOutputMat, filmic);
-
- // Clamp to [0, 1]
- filmic = saturate(filmic);
-
- return float4(filmic, color.a);
-}
-
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h
deleted file mode 100644
index ace683248..000000000
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.h
+++ /dev/null
@@ -1,105 +0,0 @@
-//*********************************************************
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-//*********************************************************
-
-DEFINE_GUID(GUID_ReinhardEffectPixelShader, 0x6716FB29, 0x06A0, 0x460F, 0xAA, 0x9C, 0x8B, 0x8F, 0xF0, 0x33, 0x5E, 0xA6);
-DEFINE_GUID(CLSID_CustomReinhardEffect, 0xB7B36C92, 0x3498, 0x4A94, 0x9E, 0x95, 0x9F, 0x24, 0x6F, 0x92, 0x45, 0xBF);
-
-// Our effect contains one transform, which is simply a wrapper around a pixel shader. As such,
-// we can simply make the effect itself act as the transform.
-class ReinhardEffect : public ID2D1EffectImpl, public ID2D1DrawTransform
-{
-public:
- // Declare effect registration methods.
- static HRESULT Register(_In_ ID2D1Factory1* pFactory);
-
- static HRESULT __stdcall CreateReinhardImpl(_Outptr_ IUnknown** ppEffectImpl);
-
- // Declare ID2D1EffectImpl implementation methods.
- IFACEMETHODIMP Initialize(
- _In_ ID2D1EffectContext* pContextInternal,
- _In_ ID2D1TransformGraph* pTransformGraph
- );
-
- IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
-
- IFACEMETHODIMP SetGraph(_In_ ID2D1TransformGraph* pGraph);
-
- // Declare ID2D1DrawTransform implementation methods.
- IFACEMETHODIMP SetDrawInfo(_In_ ID2D1DrawInfo* pRenderInfo);
-
- // Declare ID2D1Transform implementation methods.
- IFACEMETHODIMP MapOutputRectToInputRects(
- _In_ const D2D1_RECT_L* pOutputRect,
- _Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects,
- UINT32 inputRectCount
- ) const;
-
- IFACEMETHODIMP MapInputRectsToOutputRect(
- _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects,
- _In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects,
- UINT32 inputRectCount,
- _Out_ D2D1_RECT_L* pOutputRect,
- _Out_ D2D1_RECT_L* pOutputOpaqueSubRect
- );
-
- IFACEMETHODIMP MapInvalidRect(
- UINT32 inputIndex,
- D2D1_RECT_L invalidInputRect,
- _Out_ D2D1_RECT_L* pInvalidOutputRect
- ) const;
-
- // Declare ID2D1TransformNode implementation methods.
- IFACEMETHODIMP_(UINT32) GetInputCount() const;
-
- // Declare IUnknown implementation methods.
- IFACEMETHODIMP_(ULONG) AddRef();
- IFACEMETHODIMP_(ULONG) Release();
- IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput);
-
-private:
- ReinhardEffect();
- HRESULT UpdateConstants();
-
- inline static float Clamp(float v, float low, float high)
- {
- return (v < low) ? low : (v > high) ? high : v;
- }
-
- inline static float Round(float v)
- {
- return floor(v + 0.5f);
- }
-
- // Prevents over/underflows when adding longs.
- inline static long SafeAdd(long base, long valueToAdd)
- {
- if (valueToAdd >= 0)
- {
- return ((base + valueToAdd) >= base) ? (base + valueToAdd) : LONG_MAX;
- }
- else
- {
- return ((base + valueToAdd) <= base) ? (base + valueToAdd) : LONG_MIN;
- }
- }
-
- // This struct defines the constant buffer of our pixel shader.
- struct
- {
- float dpi;
- } m_constants;
-
- Microsoft::WRL::ComPtr m_drawInfo;
- Microsoft::WRL::ComPtr m_effectContext;
- LONG m_refCount;
- D2D1_RECT_L m_inputRect;
- float m_dpi;
-};
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl
deleted file mode 100644
index b95fa80d0..000000000
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.hlsl
+++ /dev/null
@@ -1,30 +0,0 @@
-//*********************************************************
-//
-// Copyright (c) Microsoft. All rights reserved.
-// This code is licensed under the MIT License (MIT).
-// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
-// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
-// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
-// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
-//
-//*********************************************************
-
-// Custom effects using pixel shaders should use HLSL helper functions defined in
-// d2d1effecthelpers.hlsli to make use of effect shader linking.
-#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture.
-#define D2D_INPUT0_SIMPLE
-
-// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe.
-#include "d2d1effecthelpers.hlsli"
-
-// Implements an extremely rudimentary HDR-to-SDR tone mapping effect. This is a
-// simplified version of the Reinhard tone mapping algorithm, applied directly
-// to the RGB channels. It successfully maps any content into the SDR range
-// (i.e. 0.0f - 1.0f), but highlights will tend to be compressed, and color
-// shifting can occur.
-D2D_PS_ENTRY(main)
-{
- // Basic formula: for each color value x of each pixel, return (x / (x+1)).
- float4 color = D2DGetInput(0);
- return (color / (color + 1.0f));
-}
\ No newline at end of file
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h
index 5425423a2..cb95b7d74 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/RenderOptions.h
@@ -22,8 +22,7 @@ namespace D2DAdvancedColorImages
///
public enum class RenderEffectKind
{
- ReinhardTonemap,
- FilmicTonemap,
+ HdrTonemap,
None,
SdrOverlay,
LuminanceHeatmap,
@@ -68,11 +67,10 @@ namespace D2DAdvancedColorImages
// The first index is chosen by default. Ensure this is in sync with DirectXPage.
renderEffects = ref new Platform::Collections::VectorView
{
- ref new EffectOption(L"ACES Filmic Tonemap", RenderEffectKind::FilmicTonemap),
- ref new EffectOption(L"Reinhard Tonemap", RenderEffectKind::ReinhardTonemap),
ref new EffectOption(L"No effect", RenderEffectKind::None),
+ ref new EffectOption(L"HDR tonemap", RenderEffectKind::HdrTonemap),
ref new EffectOption(L"Draw SDR as grayscale", RenderEffectKind::SdrOverlay),
- ref new EffectOption(L"Draw luminance as heatmap", RenderEffectKind::LuminanceHeatmap),
+ ref new EffectOption(L"Luminance heatmap", RenderEffectKind::LuminanceHeatmap),
// TODO: Temporarily disable sphere map in UI for the upcoming app release.
//ref new EffectOption(L"Draw as spheremap", RenderEffectKind::SphereMap)
};
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h
new file mode 100644
index 000000000..e2c2bba00
--- /dev/null
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SdrBrightnessFormatter.h
@@ -0,0 +1,58 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+#pragma once
+
+#include "pch.h"
+
+namespace D2DAdvancedColorImages
+{
+ // The brightness adjustment slider maps the UI linear value to an exponential (power of 2) multiplier factor.
+ // The user sees the multiplier as the tooltip text, and the renderer uses this multiplier internally.
+ public ref class SdrBrightnessFormatter sealed : Windows::UI::Xaml::Data::IValueConverter
+ {
+ public:
+ virtual Platform::Object^ Convert(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType,
+ Platform::Object^ parameter, Platform::String^ language)
+ {
+ auto type = value->GetType()->FullName;
+
+ double dbl = static_cast(value);
+
+ float percent = SdrBrightnessFormatter::SliderToBrightness(dbl) * 100.0f;
+
+ Platform::String^ text = ref new Platform::String(std::to_wstring(static_cast(percent)).c_str()) + L"%";
+
+ return text;
+ }
+
+ // No need to implement converting back on a one-way binding
+ virtual Platform::Object^ ConvertBack(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType,
+ Platform::Object^ parameter, Platform::String^ language)
+ {
+ throw ref new Platform::NotImplementedException();
+ }
+
+ // Convert slider UI value (linear) to brightness multiplier (2^x)
+ static float SliderToBrightness(double slider)
+ {
+ return powf(2.0f, static_cast(slider));
+ }
+
+ // Convert brightness multiplier (2^x) to slider UI value (linear)
+ static double BrightnessToSlider(float multiplier)
+ {
+ multiplier = max(FLT_MIN, multiplier);
+
+ return logf(multiplier) / logf(2.0f);
+ }
+ };
+}
\ No newline at end of file
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp
similarity index 64%
rename from Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp
rename to Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp
index fc59c88f1..b48513c5b 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/ReinhardEffect.cpp
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.cpp
@@ -11,20 +11,20 @@
#include "pch.h"
#include
-#include "ReinhardEffect.h"
+#include "SimpleTonemapEffect.h"
#include "BasicReaderWriter.h"
#define XML(X) TEXT(#X)
-ReinhardEffect::ReinhardEffect() :
+SimpleTonemapEffect::SimpleTonemapEffect() :
m_refCount(1)
{
}
-HRESULT __stdcall ReinhardEffect::CreateReinhardImpl(_Outptr_ IUnknown** ppEffectImpl)
+HRESULT __stdcall SimpleTonemapEffect::CreateSimpleTonemapImpl(_Outptr_ IUnknown** ppEffectImpl)
{
// Since the object's refcount is initialized to 1, we don't need to AddRef here.
- *ppEffectImpl = static_cast(new (std::nothrow) ReinhardEffect());
+ *ppEffectImpl = static_cast(new (std::nothrow) SimpleTonemapEffect());
if (*ppEffectImpl == nullptr)
{
@@ -36,7 +36,41 @@ HRESULT __stdcall ReinhardEffect::CreateReinhardImpl(_Outptr_ IUnknown** ppEffec
}
}
-HRESULT ReinhardEffect::Register(_In_ ID2D1Factory1* pFactory)
+HRESULT SimpleTonemapEffect::SetInputMaxLuminance(float nits)
+{
+ if (nits < 0.0f)
+ {
+ return E_INVALIDARG;
+ }
+
+ m_constants.inputMaxLum = nits / 80.0f; // scRGB 1.0 == 80 nits.
+
+ return S_OK;
+}
+
+float SimpleTonemapEffect::GetInputMaxLuminance() const
+{
+ return m_constants.inputMaxLum * 80.0f;
+}
+
+HRESULT SimpleTonemapEffect::SetOutputMaxLuminance(float nits)
+{
+ if (nits < 0.0f || nits > 10000.0f)
+ {
+ return E_INVALIDARG;
+ }
+
+ m_constants.outputMaxLum = nits / 80.0f; // scRGB 1.0 == 80 nits.
+
+ return S_OK;
+}
+
+float SimpleTonemapEffect::GetOutputMaxLuminance() const
+{
+ return m_constants.outputMaxLum * 80.0f;
+}
+
+HRESULT SimpleTonemapEffect::Register(_In_ ID2D1Factory1* pFactory)
{
// The inspectable metadata of an effect is defined in XML. This can be passed in from an external source
// as well, however for simplicity we just inline the XML.
@@ -45,29 +79,51 @@ HRESULT ReinhardEffect::Register(_In_ ID2D1Factory1* pFactory)
-
+
-
+
+
+
+
+
+
+
+
+
+
);
+ // This defines the bindings from specific properties to the callback functions
+ // on the class that ID2D1Effect::SetValue() & GetValue() will call.
+ const D2D1_PROPERTY_BINDING bindings[] =
+ {
+ // When accessing by index, use D2D1_HDRTONEMAP_PROP_INPUT_MAX_LUMINANCE = 0.
+ D2D1_VALUE_TYPE_BINDING(L"InputMaxLuminance", &SetInputMaxLuminance, &GetInputMaxLuminance),
+
+ // When accessing by index, use D2D1_HDRTONEMAP_PROP_OUTPUT_MAX_LUMINANCE = 1.
+ D2D1_VALUE_TYPE_BINDING(L"OutputMaxLuminance", &SetOutputMaxLuminance, &GetOutputMaxLuminance),
+
+ // Note that D2D1_HDRTONEMAP_DISPLAY_MODE = 2 is NOT IMPLEMENTED by this effect.
+ };
+
// This registers the effect with the factory, which will make the effect
// instantiatable.
return pFactory->RegisterEffectFromString(
- CLSID_CustomReinhardEffect,
+ CLSID_CustomSimpleTonemapEffect,
pszXml,
- nullptr,
- 0,
- CreateReinhardImpl
+ bindings,
+ ARRAYSIZE(bindings),
+ CreateSimpleTonemapImpl
);
}
-IFACEMETHODIMP ReinhardEffect::Initialize(
+IFACEMETHODIMP SimpleTonemapEffect::Initialize(
_In_ ID2D1EffectContext* pEffectContext,
_In_ ID2D1TransformGraph* pTransformGraph
)
@@ -80,7 +136,7 @@ IFACEMETHODIMP ReinhardEffect::Initialize(
try
{
- data = reader->ReadData("ReinhardEffect.cso");
+ data = reader->ReadData("SimpleTonemapEffect.cso");
}
catch (Platform::Exception^ e)
{
@@ -88,7 +144,7 @@ IFACEMETHODIMP ReinhardEffect::Initialize(
return e->HResult;
}
- HRESULT hr = pEffectContext->LoadPixelShader(GUID_ReinhardEffectPixelShader, data->Data, data->Length);
+ HRESULT hr = pEffectContext->LoadPixelShader(GUID_SimpleTonemapEffectPixelShader, data->Data, data->Length);
// This loads the shader into the Direct2D image effects system and associates it with the GUID passed in.
// If this method is called more than once (say by other instances of the effect) with the same GUID,
@@ -105,38 +161,37 @@ IFACEMETHODIMP ReinhardEffect::Initialize(
return hr;
}
-HRESULT ReinhardEffect::UpdateConstants()
+HRESULT SimpleTonemapEffect::UpdateConstants()
{
// Update the DPI if it has changed. This allows the effect to scale across different DPIs automatically.
- m_effectContext->GetDpi(&m_dpi, &m_dpi);
- m_constants.dpi = m_dpi;
+ m_effectContext->GetDpi(&m_dpi, &m_dpi); // DPI is never used right now.
return m_drawInfo->SetPixelShaderConstantBuffer(reinterpret_cast(&m_constants), sizeof(m_constants));
}
-IFACEMETHODIMP ReinhardEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType)
+IFACEMETHODIMP SimpleTonemapEffect::PrepareForRender(D2D1_CHANGE_TYPE changeType)
{
return UpdateConstants();
}
// SetGraph is only called when the number of inputs changes. This never happens as we publish this effect
// as a single input effect.
-IFACEMETHODIMP ReinhardEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph)
+IFACEMETHODIMP SimpleTonemapEffect::SetGraph(_In_ ID2D1TransformGraph* pGraph)
{
return E_NOTIMPL;
}
// Called to assign a new render info class, which is used to inform D2D on
// how to set the state of the GPU.
-IFACEMETHODIMP ReinhardEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo)
+IFACEMETHODIMP SimpleTonemapEffect::SetDrawInfo(_In_ ID2D1DrawInfo* pDrawInfo)
{
m_drawInfo = pDrawInfo;
- return m_drawInfo->SetPixelShader(GUID_ReinhardEffectPixelShader);
+ return m_drawInfo->SetPixelShader(GUID_SimpleTonemapEffectPixelShader);
}
// Calculates the mapping between the output and input rects.
-IFACEMETHODIMP ReinhardEffect::MapOutputRectToInputRects(
+IFACEMETHODIMP SimpleTonemapEffect::MapOutputRectToInputRects(
_In_ const D2D1_RECT_L* pOutputRect,
_Out_writes_(inputRectCount) D2D1_RECT_L* pInputRects,
UINT32 inputRectCount
@@ -157,7 +212,7 @@ IFACEMETHODIMP ReinhardEffect::MapOutputRectToInputRects(
return S_OK;
}
-IFACEMETHODIMP ReinhardEffect::MapInputRectsToOutputRect(
+IFACEMETHODIMP SimpleTonemapEffect::MapInputRectsToOutputRect(
_In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputRects,
_In_reads_(inputRectCount) CONST D2D1_RECT_L* pInputOpaqueSubRects,
UINT32 inputRectCount,
@@ -181,7 +236,7 @@ IFACEMETHODIMP ReinhardEffect::MapInputRectsToOutputRect(
return S_OK;
}
-IFACEMETHODIMP ReinhardEffect::MapInvalidRect(
+IFACEMETHODIMP SimpleTonemapEffect::MapInvalidRect(
UINT32 inputIndex,
D2D1_RECT_L invalidInputRect,
_Out_ D2D1_RECT_L* pInvalidOutputRect
@@ -195,7 +250,7 @@ IFACEMETHODIMP ReinhardEffect::MapInvalidRect(
return hr;
}
-IFACEMETHODIMP_(UINT32) ReinhardEffect::GetInputCount() const
+IFACEMETHODIMP_(UINT32) SimpleTonemapEffect::GetInputCount() const
{
return 1;
}
@@ -203,13 +258,13 @@ IFACEMETHODIMP_(UINT32) ReinhardEffect::GetInputCount() const
// D2D ensures that that effects are only referenced from one thread at a time.
// To improve performance, we simply increment/decrement our reference count
// rather than use atomic InterlockedIncrement()/InterlockedDecrement() functions.
-IFACEMETHODIMP_(ULONG) ReinhardEffect::AddRef()
+IFACEMETHODIMP_(ULONG) SimpleTonemapEffect::AddRef()
{
m_refCount++;
return m_refCount;
}
-IFACEMETHODIMP_(ULONG) ReinhardEffect::Release()
+IFACEMETHODIMP_(ULONG) SimpleTonemapEffect::Release()
{
m_refCount--;
@@ -225,7 +280,7 @@ IFACEMETHODIMP_(ULONG) ReinhardEffect::Release()
}
// This enables the stack of parent interfaces to be queried.
-IFACEMETHODIMP ReinhardEffect::QueryInterface(
+IFACEMETHODIMP SimpleTonemapEffect::QueryInterface(
_In_ REFIID riid,
_Outptr_ void** ppOutput
)
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h
similarity index 79%
rename from Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h
rename to Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h
index a32415e5f..80d149af5 100644
--- a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/FilmicEffect.h
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.h
@@ -8,19 +8,29 @@
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
+// {4587A15E-425A-4311-A591-0B2E89D02E75}
+DEFINE_GUID(GUID_SimpleTonemapEffectPixelShader,
+0x4587a15e, 0x425a, 0x4311, 0xa5, 0x91, 0xb, 0x2e, 0x89, 0xd0, 0x2e, 0x75);
-DEFINE_GUID(GUID_FilmicEffectPixelShader, 0x4214db42, 0x3bc5, 0x4f17, 0xb8, 0x59, 0x9d, 0x29, 0x15, 0x27, 0x4a, 0x6c);
-DEFINE_GUID(CLSID_CustomFilmicEffect, 0x40e0e874, 0x562c, 0x480f, 0x9b, 0x3e, 0x69, 0xf8, 0x42, 0x25, 0x59, 0xc9);
+// {0273FF16-6CA6-4AC4-BD14-77704DAD5391}
+DEFINE_GUID(CLSID_CustomSimpleTonemapEffect, 0x273ff16, 0x6ca6, 0x4ac4, 0xbd, 0x14, 0x77, 0x70, 0x4d, 0xad, 0x53, 0x91);
// Our effect contains one transform, which is simply a wrapper around a pixel shader. As such,
// we can simply make the effect itself act as the transform.
-class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform
+class SimpleTonemapEffect : public ID2D1EffectImpl, public ID2D1DrawTransform
{
public:
// Declare effect registration methods.
static HRESULT Register(_In_ ID2D1Factory1* pFactory);
- static HRESULT __stdcall CreateFilmicImpl(_Outptr_ IUnknown** ppEffectImpl);
+ static HRESULT __stdcall CreateSimpleTonemapImpl(_Outptr_ IUnknown** ppEffectImpl);
+
+ // Declare property getter/setters
+ HRESULT SetInputMaxLuminance(float nits);
+ float GetInputMaxLuminance() const;
+
+ HRESULT SetOutputMaxLuminance(float nits);
+ float GetOutputMaxLuminance() const;
// Declare ID2D1EffectImpl implementation methods.
IFACEMETHODIMP Initialize(
@@ -65,7 +75,7 @@ class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform
IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput);
private:
- FilmicEffect();
+ SimpleTonemapEffect();
HRESULT UpdateConstants();
inline static float Clamp(float v, float low, float high)
@@ -94,7 +104,8 @@ class FilmicEffect : public ID2D1EffectImpl, public ID2D1DrawTransform
// This struct defines the constant buffer of our pixel shader.
struct
{
- float dpi;
+ float inputMaxLum; // In scRGB values.
+ float outputMaxLum; // In scRGB values.
} m_constants;
Microsoft::WRL::ComPtr m_drawInfo;
diff --git a/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl
new file mode 100644
index 000000000..ef4a84388
--- /dev/null
+++ b/Samples/D2DAdvancedColorImages/cpp/D2DAdvancedColorImages/SimpleTonemapEffect.hlsl
@@ -0,0 +1,96 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+// Custom effects using pixel shaders should use HLSL helper functions defined in
+// d2d1effecthelpers.hlsli to make use of effect shader linking.
+#define D2D_INPUT_COUNT 1 // The pixel shader takes 1 input texture.
+#define D2D_INPUT0_SIMPLE
+
+#define MIDTONE_MAX 0.8 // Fraction of outputMax reserved for passthrough linear section.
+#define DELTA 0.00001
+
+// Note that the custom build step must provide the correct path to find d2d1effecthelpers.hlsli when calling fxc.exe.
+#include "d2d1effecthelpers.hlsli"
+
+cbuffer constants : register(b0)
+{
+ float inputMax; // In scRGB values.
+ float outputMax; // In scRGB values.
+};
+
+// Rational, second-order, weighted bezier. t is defined over [0, 1].
+float rb2(float t, float p0, float p1, float p2, float w0, float w1, float w2)
+{
+ float tN1 = 1 - t;
+ float tN1_2 = tN1 * tN1;
+ float t_2 = t * t;
+
+ // w0 * (1 - t)^2 * p0 +
+ // w1 * 2t(1 - t) * p1 +
+ // w2 * t^2 * p2
+ float num = w0 * tN1_2 * p0 + w1 * 2 * t * tN1 * p1 + w2 * t_2 * p2;
+
+ // w0 * (1 - t)^2 +
+ // w1 * 2t(1 - t) +
+ // w2 * t^2
+ float den = w0 * tN1_2 + w1 * 2 * t * tN1 + w2 * t_2;
+
+ return num / den;
+}
+
+// This tonemapper operates directly on each R, G and B channel. A more sophisticated tonemapper
+// would first convert to a colorspace like ICtCp that isolates luminance.
+float channelTonemap(float input)
+{
+ // Tonemapper consists of 3 segments:
+ // 1: Midtones and shadows are passed through/preserved.
+ // 2: Highlights that exceed display max luminance are compressed using bezier.
+ // 3: Highlights that exceed content max (i.e. bad metadata) are clipped.
+ if (input < MIDTONE_MAX * outputMax)
+ {
+ return input;
+ }
+ else if (input < inputMax)
+ {
+ float midLimit = MIDTONE_MAX * outputMax;
+ float w0 = outputMax / midLimit;
+ float w1 = inputMax / outputMax;
+ float t = (input - midLimit) / (inputMax - midLimit + DELTA);
+
+ // Choose weights for smooth transition with linear segments.
+ return rb2(
+ t,
+ midLimit, outputMax, outputMax, // p0, p1, p2
+ w0 , w1 , w1);
+ }
+ else
+ {
+ return outputMax;
+ }
+}
+
+// Implements a rudimentary HDR tonemapper. In linear RGB (scRGB) space, preserve colors which
+// the display can reproduce, and use a rational bezier to provide a "soft knee" to compress
+// colors above the display (target) max luminance.
+
+// Like the 1809 Direct2D HDR tonemapper, this effect outputs values in scRGB scene-referred
+// luminance space. Therefore, when outputting to an SDR or WCG display, the app must perform
+// white level adjustment to bring the numeric range of the output to [0, 1].
+D2D_PS_ENTRY(main)
+{
+ float4 color = D2DGetInput(0);
+
+ color.r = channelTonemap(color.r);
+ color.g = channelTonemap(color.g);
+ color.b = channelTonemap(color.b);
+
+ return color;
+}
\ No newline at end of file