From 57bcce9576df76bf857414d3e5ad241677ce2025 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 9 Sep 2023 01:50:52 +0200 Subject: [PATCH] feat(composition): Implement GammaTransferEffect + Sample --- .../EffectBrushTests.xaml | 1 + .../EffectBrushTests.xaml.cs | 224 ++++++++++++++++++ .../CompositionEffectBrush.skia.cs | 220 ++++++++++++++++- 3 files changed, 442 insertions(+), 3 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml index 212f65e7e51b..869008be8344 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml @@ -25,5 +25,6 @@ + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs index e2b8f7d9984f..40f979b2c437 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs @@ -153,6 +153,12 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e) var effectBrush15 = factory15.CreateBrush(); linearXferGrid.Background = new EffectTesterBrush(effectBrush15); + + var effect16 = new SimpleGammaTransferEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), RedExponent = 0.25f, GreenExponent = 0.25f, BlueExponent = 0.25f }; + var factory16 = compositor.CreateEffectFactory(effect16); + var effectBrush16 = factory16.CreateBrush(); + + gammaXferGrid.Background = new EffectTesterBrush(effectBrush16); #endif } @@ -1020,6 +1026,224 @@ public object GetProperty(uint index) public IGraphicsEffectSource GetSource(uint index) => Source; public uint GetSourceCount() => 1; } + + [Guid("409444C4-C419-41A0-B0C1-8CD0C0A18E42")] + private class SimpleGammaTransferEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop + { + private string _name = "SimpleGammaTransferEffect"; + private Guid _id = new Guid("409444C4-C419-41A0-B0C1-8CD0C0A18E42"); + + public string Name + { + get => _name; + set => _name = value; + } + + public float RedAmplitude { get; set; } = 1.0f; + + public float RedExponent { get; set; } = 1.0f; + + public float RedOffset { get; set; } = 0.0f; + + public bool RedDisable { get; set; } = false; + + + public float GreenAmplitude { get; set; } = 1.0f; + + public float GreenExponent { get; set; } = 1.0f; + + public float GreenOffset { get; set; } = 0.0f; + + public bool GreenDisable { get; set; } = false; + + + public float BlueAmplitude { get; set; } = 1.0f; + + public float BlueExponent { get; set; } = 1.0f; + + public float BlueOffset { get; set; } = 0.0f; + + public bool BlueDisable { get; set; } = false; + + + public float AlphaAmplitude { get; set; } = 1.0f; + + public float AlphaExponent { get; set; } = 1.0f; + + public float AlphaOffset { get; set; } = 0.0f; + + public bool AlphaDisable { get; set; } = false; + + + public bool ClampOutput { get; set; } = false; + + + public IGraphicsEffectSource Source { get; set; } + + public Guid GetEffectId() => _id; + + public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping) + { + switch (name) + { + case "RedAmplitude": + { + index = 0; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "RedExponent": + { + index = 1; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "RedOffset": + { + index = 2; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "RedDisable": + { + index = 3; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "GreenAmplitude": + { + index = 4; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "GreenExponent": + { + index = 5; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "GreenOffset": + { + index = 6; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "GreenDisable": + { + index = 7; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "BlueAmplitude": + { + index = 8; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "BlueExponent": + { + index = 9; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "BlueOffset": + { + index = 10; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "BlueDisable": + { + index = 11; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "AlphaAmplitude": + { + index = 12; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "AlphaExponent": + { + index = 13; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "AlphaOffset": + { + index = 14; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "AlphaDisable": + { + index = 15; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + case "ClampOutput": + { + index = 16; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + default: + { + index = 0xFF; + mapping = (GraphicsEffectPropertyMapping)0xFF; + break; + } + } + } + + public object GetProperty(uint index) + { + switch (index) + { + case 0: + return RedAmplitude; + case 1: + return RedExponent; + case 2: + return RedOffset; + case 3: + return RedDisable; + case 4: + return GreenAmplitude; + case 5: + return GreenExponent; + case 6: + return GreenOffset; + case 7: + return GreenDisable; + case 8: + return BlueAmplitude; + case 9: + return BlueExponent; + case 10: + return BlueOffset; + case 11: + return BlueDisable; + case 12: + return AlphaAmplitude; + case 13: + return AlphaExponent; + case 14: + return AlphaOffset; + case 15: + return AlphaDisable; + case 16: + return ClampOutput; + default: + return null; + } + } + + public uint GetPropertyCount() => 17; + public IGraphicsEffectSource GetSource(uint index) => Source; + public uint GetSourceCount() => 1; + } #endif } } diff --git a/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs b/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs index 3200278531c7..94f2a3887d7b 100644 --- a/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs @@ -731,10 +731,10 @@ half4 main() strInputPixel = pGenerator->GetInputPixelName(node, 0); - for ( int i = 0; i < 13; i += 3 ) + for ( int i,k = 0; i < 12; i += 3, k += 2 ) { - rgstrPropertyNames[i] = pGenerator->DeclareShaderVariableForProperty(i); // Offset - rgstrPropertyNames[i + 1] = pGenerator->DeclareShaderVariableForProperty(i + 1); // Slope + rgstrPropertyNames[k] = pGenerator->DeclareShaderVariableForProperty(i); // Offset + rgstrPropertyNames[k + 1] = pGenerator->DeclareShaderVariableForProperty(i + 1); // Slope } pStringBuilder = pGenerator->BeginPSLine(); @@ -801,6 +801,220 @@ half4 main() */ } + return null; + } + case EffectType.GammaTransferEffect: + { + if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() == 17 && effectInterop.GetSource(0) is IGraphicsEffectSource source) + { + SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds); + if (sourceFilter is null) + return null; + + effectInterop.GetNamedPropertyMapping("RedAmplitude", out uint redAmplitudeProp, out _); + effectInterop.GetNamedPropertyMapping("RedExponent", out uint redExponentProp, out _); + effectInterop.GetNamedPropertyMapping("RedOffset", out uint redOffsetProp, out _); + effectInterop.GetNamedPropertyMapping("RedDisable", out uint redDisableProp, out _); + + effectInterop.GetNamedPropertyMapping("GreenAmplitude", out uint greenAmplitudeProp, out _); + effectInterop.GetNamedPropertyMapping("GreenExponent", out uint greenExponentProp, out _); + effectInterop.GetNamedPropertyMapping("GreenOffset", out uint greenOffsetProp, out _); + effectInterop.GetNamedPropertyMapping("GreenDisable", out uint greenDisableProp, out _); + + effectInterop.GetNamedPropertyMapping("BlueAmplitude", out uint blueAmplitudeProp, out _); + effectInterop.GetNamedPropertyMapping("BlueExponent", out uint blueExponentProp, out _); + effectInterop.GetNamedPropertyMapping("BlueOffset", out uint blueOffsetProp, out _); + effectInterop.GetNamedPropertyMapping("BlueDisable", out uint blueDisableProp, out _); + + effectInterop.GetNamedPropertyMapping("AlphaAmplitude", out uint alphaAmplitudeProp, out _); + effectInterop.GetNamedPropertyMapping("AlphaExponent", out uint alphaExponentProp, out _); + effectInterop.GetNamedPropertyMapping("AlphaOffset", out uint alphaOffsetProp, out _); + effectInterop.GetNamedPropertyMapping("AlphaDisable", out uint alphaDisableProp, out _); + + effectInterop.GetNamedPropertyMapping("ClampOutput", out uint clampProp, out _); + + float redAmplitude = (float)effectInterop.GetProperty(redAmplitudeProp); + float redExponent = (float)effectInterop.GetProperty(redExponentProp); + float redOffset = (float)effectInterop.GetProperty(redOffsetProp); + bool redDisable = (bool)effectInterop.GetProperty(redDisableProp); + + float greenAmplitude = (float)effectInterop.GetProperty(greenAmplitudeProp); + float greenExponent = (float)effectInterop.GetProperty(greenExponentProp); + float greenOffset = (float)effectInterop.GetProperty(greenOffsetProp); + bool greenDisable = (bool)effectInterop.GetProperty(greenDisableProp); + + float blueAmplitude = (float)effectInterop.GetProperty(blueAmplitudeProp); + float blueExponent = (float)effectInterop.GetProperty(blueExponentProp); + float blueOffset = (float)effectInterop.GetProperty(blueOffsetProp); + bool blueDisable = (bool)effectInterop.GetProperty(blueDisableProp); + + float alphaAmplitude = (float)effectInterop.GetProperty(alphaAmplitudeProp); + float alphaExponent = (float)effectInterop.GetProperty(alphaExponentProp); + float alphaOffset = (float)effectInterop.GetProperty(alphaOffsetProp); + bool alphaDisable = (bool)effectInterop.GetProperty(alphaDisableProp); + + bool clamp = clampProp != 0xFF ? (bool)effectInterop.GetProperty(clampProp) : false; + + string shader = $@" + uniform shader input; + + uniform half redAmplitude; + uniform half redExponent; + uniform half redOffset; + + uniform half greenAmplitude; + uniform half greenExponent; + uniform half greenOffset; + + uniform half blueAmplitude; + uniform half blueExponent; + uniform half blueOffset; + + uniform half alphaAmplitude; + uniform half alphaExponent; + uniform half alphaOffset; + + half4 Premultiply(half4 color) + {{ + color.rgb *= color.a; + return color; + }} + + half4 UnPremultiply(half4 color) + {{ + color.rgb = (color.a == 0) ? half3(0, 0, 0) : (color.rgb / color.a); + return color; + }} + + half4 main() + {{ + half4 color = UnPremultiply(sample(input)); + color = half4( + {(redDisable ? "color.r" : "redAmplitude * pow(abs(color.r), redExponent) + redOffset")}, + {(greenDisable ? "color.g" : "greenAmplitude * pow(abs(color.g), greenExponent) + greenOffset")}, + {(blueDisable ? "color.b" : "blueAmplitude * pow(abs(color.b), blueExponent) + blueOffset")}, + {(alphaDisable ? "color.a" : "alphaAmplitude * pow(abs(color.a), alphaExponent) + alphaOffset")} + ); + + return {(clamp ? "clamp(" : String.Empty)}Premultiply(color){(clamp ? ", 0.0, 1.0)" : String.Empty)}; + }} + "; + + SKRuntimeEffect runtimeEffect = SKRuntimeEffect.Create(shader, out string errors); + if (errors is not null) + return null; + + SKRuntimeEffectUniforms uniforms = new(runtimeEffect) + { + { "redAmplitude", redAmplitude }, + { "redExponent", redExponent }, + { "redOffset", redOffset }, + + { "greenAmplitude", greenAmplitude }, + { "greenExponent", greenExponent }, + { "greenOffset", greenOffset }, + + { "blueAmplitude", blueAmplitude }, + { "blueExponent", blueExponent }, + { "blueOffset", blueOffset }, + + { "alphaAmplitude", alphaAmplitude }, + { "alphaExponent", alphaExponent }, + { "alphaOffset", alphaOffset } + }; + SKRuntimeEffectChildren children = new(runtimeEffect) + { + { "input", null } + }; + + return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds)); + + // Reference (wuceffects.dll): + /* + void Windows::UI::Composition::GammaTransferEffectType::GenerateCode(const Windows::UI::Composition::EffectNode *node, Windows::UI::Composition::EffectGenerator *pGenerator, const char *pszOutputPixelName) + { + bool rgfDisable[4]; + std::string strInputPixel; + std::string rgstrPropertyNames[12]; + Windows::UI::Composition::StringBuilder *pStringBuilder; + + strInputPixel = pGenerator->GetInputPixelName(node, 0); + + for ( int i = 2, k = 0; i < 15; i += 4, k += 3 ) + { + rgstrPropertyNames[k] = pGenerator->DeclareShaderVariableForProperty(i - 1); // Amplitude + rgstrPropertyNames[k + 1] = pGenerator->DeclareShaderVariableForProperty(i); // Exponent + rgstrPropertyNames[k + 2] = pGenerator->DeclareShaderVariableForProperty(i + 1); // Offset + } + + pStringBuilder = pGenerator->BeginPSLine(); + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(" = UnPremultiply("); + pStringBuilder->Append(strInputPixel.c_str(), strInputPixel.size()); + pStringBuilder->Append(");"); + pStringBuilder->Append('\n'); + + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(" = minfloat4("); + pStringBuilder->Append('\n'); + + rgfDisable[0] = *(bool*)&node->m_uprgbDefaultProperties[48]; // RedDisable + rgfDisable[1] = *(bool*)&node->m_uprgbDefaultProperties[49]; // GreenDisable + rgfDisable[2] = *(bool*)&node->m_uprgbDefaultProperties[50]; // BlueDisable + rgfDisable[3] = *(bool*)&node->m_uprgbDefaultProperties[51]; // AlphaDisable + + const char* RGBA = "rgba"; + for ( int i,k = 0; i < 4; i++, k += 3 ) + { + if (i) + { + pStringBuilder->Append(","); + pStringBuilder->Append('\n'); + } + + if ( rgfDisable[i] ) + { + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append('.'); + pStringBuilder->Append(RGBA[i]); + } + else + { + pStringBuilder->Append(rgstrPropertyNames[k]); // Amplitude + pStringBuilder->Append(" * pow("); + pStringBuilder->Append("abs("); + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append('.'); + pStringBuilder->Append(RGBA[i]); + pStringBuilder->Append(')'); + pStringBuilder->Append(", "); + pStringBuilder->Append(rgstrPropertyNames[k + 1]); // Exponent + pStringBuilder->Append(") + "); + pStringBuilder->Append(rgstrPropertyNames[k + 2]); // Offset + } + } + + pStringBuilder->Append(");"); + pStringBuilder->Append('\n'); + + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(" = Premultiply("); + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(");"); + pStringBuilder->Append('\n'); + + if (*(bool*)&node->m_uprgbDefaultProperties[52]) // ClampOutput + { + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(" = saturate("); + pStringBuilder->Append(pszOutputPixelName); + pStringBuilder->Append(");"); + pStringBuilder->Append('\n'); + } + } + */ + } + return null; } case EffectType.Unsupported: