Skip to content

Commit

Permalink
feat(composition): Implement HueRotationEffect and TintEffect + Samples
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmed605 committed Mar 2, 2024
1 parent 82b8084 commit 1b3e7e3
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
d:DesignHeight="300"
d:DesignWidth="400">

<StackPanel Orientation="Horizontal" Spacing="25">
<GridView SelectionMode="None" Padding="20">
<Grid x:Name="blurGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Grid x:Name="grayGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Grid x:Name="invertGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</StackPanel>
<Grid x:Name="hueGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Grid x:Name="aggregationGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<Grid x:Name="tintGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</GridView>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Uno.Extensions;
using Uno.UI.Samples.Controls;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Graphics.Effects;

#if !WINDOWS_UWP // Making the sample buildable on UWP
using Windows.Graphics.Effects.Interop;
#endif

using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
Expand All @@ -34,6 +39,7 @@ public EffectBrushTests()

private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
{
#if !WINDOWS_UWP
var compositor = Window.Current.Compositor;

var effect = new SimpleBlurEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), BlurAmount = 5.0f };
Expand All @@ -53,6 +59,27 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
var effectBrush3 = factory3.CreateBrush();

invertGrid.Background = new EffectTesterBrush(effectBrush3);

var effect4 = new SimpleHueRotationEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), Angle = (float)MathEx.ToRadians(45) };
var factory4 = compositor.CreateEffectFactory(effect4);
var effectBrush4 = factory4.CreateBrush();

hueGrid.Background = new EffectTesterBrush(effectBrush4);

// Aggregation Test

var effect5 = new SimpleGrayscaleEffect() { Source = effect };
var factory5 = compositor.CreateEffectFactory(effect5);
var effectBrush5 = factory5.CreateBrush();

aggregationGrid.Background = new EffectTesterBrush(effectBrush5);

var effect6 = new SimpleTintEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), Color = Color.FromArgb(127, 66, 135, 245) };
var factory6 = compositor.CreateEffectFactory(effect6);
var effectBrush6 = factory6.CreateBrush();

tintGrid.Background = new EffectTesterBrush(effectBrush6);
#endif
}

private class EffectTesterBrush : XamlCompositionBrushBase
Expand All @@ -78,8 +105,9 @@ protected override void OnConnected()
}
}

#if WINDOWS_UWP
#if WINDOWS_UWP // Making the sample buildable on UWP
private interface IGraphicsEffectD2D1Interop { }
private enum GraphicsEffectPropertyMapping { Direct, RadiansToDegrees, ColorToVector4 }
#endif

[Guid("1FEB6D69-2FE6-4AC9-8C58-1D7F93E7A6A5")]
Expand Down Expand Up @@ -204,5 +232,111 @@ public string Name
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}

[Guid("0F4458EC-4B32-491B-9E85-BD73F44D3EB6")]
private class SimpleHueRotationEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
private string _name = "SimpleHueRotationEffect";
private Guid _id = new Guid("0F4458EC-4B32-491B-9E85-BD73F44D3EB6");

public string Name
{
get => _name;
set => _name = value;
}

public float Angle { get; set; } = 0.0f; // Radians

public IGraphicsEffectSource Source { get; set; }

public Guid GetEffectId() => _id;

public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
{
switch (name)
{
case "Angle":
{
index = 0;
mapping = GraphicsEffectPropertyMapping.RadiansToDegrees;
break;
}
default:
{
index = 0xFF;
mapping = (GraphicsEffectPropertyMapping)0xFF;
break;
}
}
}

public object GetProperty(uint index)
{
switch (index)
{
case 0:
return Angle;
default:
return null;
}
}

public uint GetPropertyCount() => 1;
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}

[Guid("36312B17-F7DD-4014-915D-FFCA768CF211")]
private class SimpleTintEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
private string _name = "SimpleHueRotationEffect";
private Guid _id = new Guid("36312B17-F7DD-4014-915D-FFCA768CF211");

public string Name
{
get => _name;
set => _name = value;
}

public Color Color { get; set; } = Color.FromArgb(255, 255, 255, 255);

public IGraphicsEffectSource Source { get; set; }

public Guid GetEffectId() => _id;

public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
{
switch (name)
{
case "Color":
{
index = 0;
mapping = GraphicsEffectPropertyMapping.ColorToVector4;
break;
}
default:
{
index = 0xFF;
mapping = (GraphicsEffectPropertyMapping)0xFF;
break;
}
}
}

public object GetProperty(uint index)
{
switch (index)
{
case 0:
return Color;
default:
return null;
}
}

public uint GetPropertyCount() => 1;
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}
}
}
106 changes: 106 additions & 0 deletions src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Windows.Graphics.Effects.Interop;
using System;
using SkiaSharp;
using System.Reflection.Emit;

namespace Windows.UI.Composition
{
Expand Down Expand Up @@ -37,6 +38,7 @@ private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds)
SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds);
if (sourceFilter is null)
return null;

effectInterop.GetNamedPropertyMapping("BlurAmount", out uint sigmaProp, out _);
effectInterop.GetNamedPropertyMapping("Optimization", out uint optProp, out _);
effectInterop.GetNamedPropertyMapping("BorderMode", out uint borderProp, out _);
Expand Down Expand Up @@ -93,6 +95,110 @@ private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds)
sourceFilter, new(bounds));
}

return null;
}
case EffectType.HueRotationEffect:
{
if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() == 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source)
{
SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds);
if (sourceFilter is null)
return null;

effectInterop.GetNamedPropertyMapping("Angle", out uint angleProp, out GraphicsEffectPropertyMapping angleMapping);
float angle = (float)effectInterop.GetProperty(angleProp);

if (angleMapping == GraphicsEffectPropertyMapping.RadiansToDegrees)
angle *= 180.0f / MathF.PI;

return SKImageFilter.CreateColorFilter(
SKColorFilter.CreateColorMatrix(
new float[] // Hue Rotation Matrix
{
(0.2127f + MathF.Cos(angle) * 0.7873f - MathF.Sin(angle) * 0.2127f), (0.715f - MathF.Cos(angle) * 0.715f - MathF.Sin(angle) * 0.715f), (0.072f - MathF.Cos(angle) * 0.072f + MathF.Sin(angle) * 0.928f), 0, 0,
(0.2127f - MathF.Cos(angle) * 0.213f + MathF.Sin(angle) * 0.143f), (0.715f + MathF.Cos(angle) * 0.285f + MathF.Sin(angle) * 0.140f), (0.072f - MathF.Cos(angle) * 0.072f - MathF.Sin(angle) * 0.283f), 0, 0,
(0.2127f - MathF.Cos(angle) * 0.213f - MathF.Sin(angle) * 0.787f), (0.715f - MathF.Cos(angle) * 0.715f + MathF.Sin(angle) * 0.715f), (0.072f + MathF.Cos(angle) * 0.928f + MathF.Sin(angle) * 0.072f), 0, 0,
0, 0, 0, 1, 0
}),
sourceFilter, new(bounds));
}

return null;
}
case EffectType.TintEffect:
{
if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() >= 1 /* only the Color property is required */ && effectInterop.GetSource(0) is IGraphicsEffectSource source)
{
SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds);
if (sourceFilter is null)
return null;

// Note: ColorHdr isn't supported by Composition (as of 10.0.25941.1000)
effectInterop.GetNamedPropertyMapping("Color", out uint colorProp, out _);
effectInterop.GetNamedPropertyMapping("ClampOutput", out uint clampProp, out _);

Color color = (Color)effectInterop.GetProperty(colorProp);
bool clamp = clampProp != 0xFF ? (bool)effectInterop.GetProperty(clampProp) : false;

string shader = $@"
uniform shader input;
uniform vec4 color;
half4 main()
{{
return {(clamp ? "clamp(" : String.Empty)}sample(input) * 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)
{
{ "color", new float[] { color.R * (1.0f / 255.0f), color.G * (1.0f / 255.0f), color.B * (1.0f / 255.0f), color.A * (1.0f / 255.0f) } }
};
SKRuntimeEffectChildren children = new(runtimeEffect)
{
{ "input", null }
};

return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds));

// Reference (wuceffects.dll):
/*
void Windows::UI::Composition::TintEffectType::GenerateCode(const Windows::UI::Composition::EffectNode *node, Windows::UI::Composition::EffectGenerator *pGenerator, const char *pszOutputPixelName)
{
Windows::UI::Composition::StringBuilder *pStringBuilder;
std::string strInputPixel;
std::string strColorProperty;
strInputPixel = pGenerator->GetInputPixelName(node, 0);
strColorProperty = pGenerator->DeclareShaderVariableForProperty(0); // Color
pStringBuilder = pGenerator->BeginPSLine();
pStringBuilder->Append(" ");
pStringBuilder->(pszOutputPixelName);
pStringBuilder->(" = ");
pStringBuilder->(strInputPixel.c_str(), strInputPixel.size());
pStringBuilder->(" * ");
pStringBuilder->(strColorProperty.c_str(), strColorProperty.size());
pStringBuilder->(";");
pStringBuilder->('\n');
if (*(bool*)&node->m_uprgbDefaultProperties[16]) // ClampOutput, 16 = GetPropertyMetadata(1, &metatdata) ==> metatdata.cbStructOffset
{
Windows::UI::Composition::StringBuilder* builder = pGenerator->BeginPSLine();
builder->(pszOutputPixelName);
builder->(" = saturate(");
builder->(pszOutputPixelName);
builder->(");");
builder->('\n');
}
}
*/
}

return null;
}
case EffectType.Unsupported:
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UWP/Graphics/Effects/Interop/EffectType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal enum EffectType
CompositeEffect,
ContrastEffect,
CrossFadeEffect,
//DisplacementMapEffect, // Note: DisplacementMapEffect is implemented by Composition but it's not enabled yet (as of 10.0.25941.1000), uncomment this member when it gets enabled
DistantDiffuseEffect,
DistantSpecularEffect,
ExposureEffect,
Expand Down

0 comments on commit 1b3e7e3

Please sign in to comment.