Skip to content

Commit

Permalink
feat(composition): Implement ColorMatrixEffect + Sample + Performance…
Browse files Browse the repository at this point in the history
… improvements
  • Loading branch information
ahmed605 committed Mar 2, 2024
1 parent 93643a3 commit 2f4b39d
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
<Grid x:Name="t2dGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="Transform2DEffect"/>
<Grid x:Name="sepiaGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="SepiaEffect"/>
<Grid x:Name="tempTintGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="TemperatureAndTintEffect"/>
<Grid x:Name="matrixGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="ColorMatrixEffect"/>
</GridView>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,21 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
var effectBrush20 = factory20.CreateBrush();

tempTintGrid.Background = new EffectTesterBrush(effectBrush20);

var swapRedAndBlue = new SimpleMatrix5x4
{
M11 = 0, M12 = 0, M13 = 1, M14 = 0,
M21 = 0, M22 = 1, M23 = 0, M24 = 0,
M31 = 1, M32 = 0, M33 = 0, M34 = 0,
M41 = 0, M42 = 0, M43 = 0, M44 = 1,
M51 = 0, M52 = 0, M53 = 0, M54 = 0
};

var effect21 = new SimpleColorMatrixEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), ColorMatrix = swapRedAndBlue };
var factory21 = compositor.CreateEffectFactory(effect21);
var effectBrush21 = factory21.CreateBrush();

matrixGrid.Background = new EffectTesterBrush(effectBrush21);
#endif
}

Expand Down Expand Up @@ -1497,6 +1512,107 @@ public object GetProperty(uint index)
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}

private struct SimpleMatrix5x4
{
public float M11;
public float M12;
public float M13;
public float M14;
public float M21;
public float M22;
public float M23;
public float M24;
public float M31;
public float M32;
public float M33;
public float M34;
public float M41;
public float M42;
public float M43;
public float M44;
public float M51;
public float M52;
public float M53;
public float M54;

public static SimpleMatrix5x4 Identity
{
get => new SimpleMatrix5x4()
{
M11 = 1, M12 = 0, M13 = 0, M14 = 0,
M21 = 0, M22 = 1, M23 = 0, M24 = 0,
M31 = 0, M32 = 0, M33 = 1, M34 = 0,
M41 = 0, M42 = 0, M43 = 0, M44 = 1,
M51 = 0, M52 = 0, M53 = 0, M54 = 0
};
}

public float[] ToArray()
{
return new float[20]
{
M11, M12, M13, M14,
M21, M22, M23, M24,
M31, M32, M33, M34,
M41, M42, M43, M44,
M51, M52, M53, M54
};
}
}

[Guid("921F03D6-641C-47DF-852D-B4BB6153AE11")]
private class SimpleColorMatrixEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
private string _name = "SimpleColorMatrixEffect";
private Guid _id = new Guid("921F03D6-641C-47DF-852D-B4BB6153AE11");

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

public SimpleMatrix5x4 ColorMatrix { get; set; } = SimpleMatrix5x4.Identity;

public IGraphicsEffectSource Source { get; set; }

public Guid GetEffectId() => _id;

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

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

public uint GetPropertyCount() => 1;
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}
#endif
}
}
97 changes: 44 additions & 53 deletions src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public partial class CompositionEffectBrush : CompositionBrush
private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds)
{
// TODO: https://user-images.githubusercontent.com/34550324/264485558-d7ee5062-b0e0-4f6e-a8c7-0620ec561d3d.png
// TODO: Cache pixel shaders (see dwmcore.dll!CCompiledEffectCache)
// TODO: Cache pixel shaders (see dwmcore.dll!CCompiledEffectCache), needed in order to implement animations and online rendering

switch (effect)
{
Expand Down Expand Up @@ -45,7 +45,6 @@ private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds)
effectInterop.GetNamedPropertyMapping("Optimization", out uint optProp, out _);
effectInterop.GetNamedPropertyMapping("BorderMode", out uint borderProp, out _);

// TODO: Implement support for other GraphicsEffectPropertyMapping values than Direct
float sigma = (float)effectInterop.GetProperty(sigmaProp);
_ = (uint)effectInterop.GetProperty(optProp); // TODO
_ = (uint)effectInterop.GetProperty(borderProp); // TODO
Expand Down Expand Up @@ -460,7 +459,7 @@ half4 main()

return null;
}
case EffectType.ExposureEffect: // TODO: We can probably replace the pixel shader with a color matrix filter instead?
case EffectType.ExposureEffect:
{
if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() == 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source)
{
Expand All @@ -473,31 +472,16 @@ half4 main()
float exposure = (float)effectInterop.GetProperty(exposureProp);
float multiplier = MathF.Pow(2.0f, exposure);

string shader = $@"
uniform shader input;
uniform half multiplier;
half4 main()
{{
half4 inputColor = sample(input);
return half4(inputColor.rgb * multiplier, inputColor.a);
}}
";

SKRuntimeEffect runtimeEffect = SKRuntimeEffect.Create(shader, out string errors);
if (errors is not null)
return null;

SKRuntimeEffectUniforms uniforms = new(runtimeEffect)
{
{ "multiplier", multiplier }
};
SKRuntimeEffectChildren children = new(runtimeEffect)
{
{ "input", null }
};

return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds));
return SKImageFilter.CreateColorFilter(
SKColorFilter.CreateColorMatrix(
new float[] // Exposure Matrix
{
multiplier, 0, 0, 0, 0,
0, multiplier, 0, 0, 0,
0, 0, multiplier, 0, 0,
0, 0, 0, 1, 0,
}),
sourceFilter, new(bounds));

// Reference (wuceffects.dll):
/*
Expand Down Expand Up @@ -1077,7 +1061,7 @@ half4 main()
0,0,0
};

return SKImageFilter.CreateMatrixConvolution(new SKSizeI(3, 3), identityKernel, 1f, 0f, new(1, 1), mode, true, sourceFilter, new(bounds));
return SKImageFilter.CreateMatrixConvolution(new SKSizeI(3, 3), identityKernel, 1f, 0f, new(0, 0), mode, true, sourceFilter, new(bounds));
}

return null;
Expand Down Expand Up @@ -1124,34 +1108,41 @@ half4 main()

var gains = TempAndTintUtils.NormalizedTempTintToGains(temp, tint);

string shader = $@"
uniform shader input;
uniform half redGain;
uniform half blueGain;
half4 main()
{{
half4 inputColor = sample(input);
return half4(inputColor.r * redGain, inputColor.g, inputColor.b * blueGain, inputColor.a);
}}
";
return SKImageFilter.CreateColorFilter(
SKColorFilter.CreateColorMatrix(
new float[] // TemperatureAndTint Matrix
{
gains.RedGain, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, gains.BlueGain, 0, 0,
0, 0, 0, 1, 0,
}),
sourceFilter, new(bounds));
}

SKRuntimeEffect runtimeEffect = SKRuntimeEffect.Create(shader, out string errors);
if (errors is not null)
return null;
}
case EffectType.ColorMatrixEffect: // TODO: support "AlphaMode" and "ClampOutput" properties
{
if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() >= 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source)
{
SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds);
if (sourceFilter is null)
return null;

SKRuntimeEffectUniforms uniforms = new(runtimeEffect)
{
{ "redGain", gains.RedGain },
{ "blueGain", gains.BlueGain }
};
SKRuntimeEffectChildren children = new(runtimeEffect)
{
{ "input", null }
};
effectInterop.GetNamedPropertyMapping("ColorMatrix", out uint matrixProp, out _);
float[] matrix = (float[])effectInterop.GetProperty(matrixProp);

return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds));
return SKImageFilter.CreateColorFilter(
SKColorFilter.CreateColorMatrix(
new float[]
{
matrix[0], matrix[1], matrix[2], matrix[3], matrix[16],
matrix[4], matrix[5], matrix[6], matrix[7], matrix[17],
matrix[8], matrix[9], matrix[10], matrix[11], matrix[18],
matrix[12], matrix[13], matrix[14], matrix[15], matrix[19],
}),
sourceFilter, new(bounds));
}

return null;
Expand Down

0 comments on commit 2f4b39d

Please sign in to comment.