Skip to content

Commit

Permalink
feat(composition): Implement ContrastEffect (+ Sample) and Arithmetic…
Browse files Browse the repository at this point in the history
…CompositeEffect
  • Loading branch information
ahmed605 committed Mar 2, 2024
1 parent 4bf9313 commit faf0efd
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
<Grid x:Name="compositeGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="CompositeEffect"/>
<Grid x:Name="colorGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="ColorSourceEffect"/>
<Grid x:Name="opacityGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="OpacityEffect"/>
<Grid x:Name="contrastGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="ContrastEffect"/>
</GridView>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
var effectBrush10 = factory10.CreateBrush();

opacityGrid.Background = new EffectTesterBrush(effectBrush10);

var effect11 = new SimpleContrastEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), Contrast = 2.0f };
var factory11 = compositor.CreateEffectFactory(effect11);
var effectBrush11 = factory11.CreateBrush();

contrastGrid.Background = new EffectTesterBrush(effectBrush11);
#endif
}

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

[Guid("B648A78A-0ED5-4F80-A94A-8E825ACA6B77")]
private class SimpleContrastEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
private string _name = "SimpleContrastEffect";
private Guid _id = new Guid("B648A78A-0ED5-4F80-A94A-8E825ACA6B77");

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

public float Contrast { get; set; } = 0.0f;

public IGraphicsEffectSource Source { get; set; }

public Guid GetEffectId() => _id;

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

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

public uint GetPropertyCount() => 1;
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}
#endif
}
}
165 changes: 165 additions & 0 deletions src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,171 @@ half4 main()
sourceFilter, new(bounds));
}

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

effectInterop.GetNamedPropertyMapping("Contrast", out uint contrastProp, out _);
effectInterop.GetNamedPropertyMapping("ClampSource", out uint clampProp, out _);

float contrast = (float)effectInterop.GetProperty(contrastProp);
bool clamp = clampProp != 0xFF ? (bool)effectInterop.GetProperty(clampProp) : false;

string shader = $@"
uniform shader input;
uniform half contrastValue;
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 Contrast(half4 color, half contrast)
{{
color = UnPremultiply(color);
half s = 1 - (3.0 / 4.0) * contrast;
half c2 = s - 1;
half b2 = 4 - 3 * s;
half a2 = 2 * c2;
half b1 = s;
half a1 = -a2;
half3 lowResult = color.rgb * (color.rgb * a1 + b1);
half3 highResult = color.rgb * (color.rgb * a2 + b2) + c2;
half3 comparisonResult = half3(0.0);
comparisonResult.r = (color.rgb.r < 0.5) ? 1.0 : 0.0;
comparisonResult.g = (color.rgb.g < 0.5) ? 1.0 : 0.0;
comparisonResult.b = (color.rgb.b < 0.5) ? 1.0 : 0.0;
color.rgb = mix(lowResult, highResult, comparisonResult);
return Premultiply(color);
}}
half4 main()
{{
return Contrast({(clamp ? "clamp(" : String.Empty)}sample(input){(clamp ? ", 0.0, 1.0)" : String.Empty)}, contrastValue);
}}
";

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

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

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

// Reference (wuceffects.dll):
/*
void Windows::UI::Composition::ContrastEffectType::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 strContrastProperty;
strInputPixel = pGenerator->GetInputPixelName(node, 0);
strContrastProperty = pGenerator->DeclareShaderVariableForProperty(0); // Contrast
pGenerator->AddPSInclude("D2DContrast.hlsl");
pStringBuilder = pGenerator->BeginPSLine();
pStringBuilder->Append(" ");
pStringBuilder->(pszOutputPixelName);
pStringBuilder->(" = ");
pStringBuilder->(strInputPixel.c_str(), strInputPixel.size());
pStringBuilder->(";");
pStringBuilder->('\n');
if (*(bool*)&node->m_uprgbDefaultProperties[4]) // ClampSource, 4 = GetPropertyMetadata(1, &metatdata) ==> metatdata.cbStructOffset
{
Windows::UI::Composition::StringBuilder* builder = pGenerator->BeginPSLine();
builder->(pszOutputPixelName);
builder->(" = saturate(");
builder->(pszOutputPixelName);
builder->(");");
builder->('\n');
}
Windows::UI::Composition::StringBuilder* builder = pGenerator->BeginPSLine();
builder->(pszOutputPixelName);
builder->(" = D2DContrast(");
builder->(pszOutputPixelName);
builder->(", ");
builder->(strContrastProperty.c_str(), strContrastProperty.size());
builder->(");");
builder->('\n');
}
*/
}

return null;
}
case EffectType.ArithmeticCompositeEffect: // TODO: support "ClampOutput" property
{
if (effectInterop.GetSourceCount() == 2 && effectInterop.GetPropertyCount() >= 4 && effectInterop.GetSource(0) is IGraphicsEffectSource bg && effectInterop.GetSource(1) is IGraphicsEffectSource fg)
{
SKImageFilter bgFilter = GenerateEffectFilter(bg, bounds);
if (bgFilter is null)
return null;

SKImageFilter fgFilter = GenerateEffectFilter(fg, bounds);
if (fgFilter is null)
return null;

float? multiplyAmount = effectInterop.GetProperty(0) as float?;
float? source1Amount = effectInterop.GetProperty(1) as float?;
float? source2Amount = effectInterop.GetProperty(2) as float?;
float? offset = effectInterop.GetProperty(3) as float?;

if (multiplyAmount is null || source1Amount is null || source2Amount is null || offset is null)
{
float[] coefficients = effectInterop.GetProperty(0) as float[];

if (coefficients is null)
{
effectInterop.GetNamedPropertyMapping("Coefficients", out uint coefficientsProp, out GraphicsEffectPropertyMapping coefficientsMapping);
if (coefficientsMapping == GraphicsEffectPropertyMapping.Direct)
coefficients = effectInterop.GetProperty(coefficientsProp) as float[];
else
return null;
}

if (coefficients is not null && coefficients.Length == 4)
{
multiplyAmount = coefficients[0];
source1Amount = coefficients[1];
source2Amount = coefficients[2];
offset = coefficients[3];
}
else
return null;
}

return SKImageFilter.CreateArithmetic(multiplyAmount.Value, source1Amount.Value, source2Amount.Value, offset.Value, false, bgFilter, fgFilter, new(bounds));
}

return null;
}
case EffectType.Unsupported:
Expand Down

0 comments on commit faf0efd

Please sign in to comment.