Skip to content

Commit

Permalink
feat(composition): Implement ExposureEffect + Sample
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmed605 committed Mar 2, 2024
1 parent faf0efd commit 85b301b
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
<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"/>
<Grid x:Name="exposureGrid" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" ToolTipService.ToolTip="ExposureEffect (Negative Value, -1)"/>
</GridView>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using ShimSkiaSharp;
using SkiaSharp;
using Uno.Extensions;
using Uno.UI.Samples.Controls;
using Windows.Foundation;
Expand Down Expand Up @@ -88,7 +86,7 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)

blendGrid.Background = new EffectTesterBrushWithSecondaryBrush(effectBrush7, compositor.CreateColorBrush(Colors.LightBlue));

var surface = LoadedImageSurface.StartLoadFromUri(new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Logo-winui.svg/200px-Logo-winui.svg.png"));
var surface = LoadedImageSurface.StartLoadFromUri(new Uri("https://user-images.githubusercontent.com/34550324/266135095-71c9ce0a-4e49-408f-b2ff-670a53adef10.png"));
surface.LoadCompleted += (s, o) =>
{
if (o.Status == LoadedImageSourceLoadStatus.Success)
Expand Down Expand Up @@ -120,6 +118,12 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
var effectBrush11 = factory11.CreateBrush();

contrastGrid.Background = new EffectTesterBrush(effectBrush11);

var effect12 = new SimpleExposureEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), Exposure = -1.0f };
var factory12 = compositor.CreateEffectFactory(effect12);
var effectBrush12 = factory12.CreateBrush();

exposureGrid.Background = new EffectTesterBrush(effectBrush12);
#endif
}

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

[Guid("B56C8CFA-F634-41EE-BEE0-FFA617106004")]
private class SimpleExposureEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
private string _name = "SimpleExposureEffect";
private Guid _id = new Guid("B56C8CFA-F634-41EE-BEE0-FFA617106004");

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

public float Exposure { 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 "Exposure":
case "ExposureValue":
{
index = 0;
mapping = GraphicsEffectPropertyMapping.Direct;
break;
}
default:
{
index = 0xFF;
mapping = (GraphicsEffectPropertyMapping)0xFF;
break;
}
}
}

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

public uint GetPropertyCount() => 1;
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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)

switch (effect)
{
Expand All @@ -22,7 +23,7 @@ private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds)
SKPaint paint = new SKPaint() { IsAntialias = true, IsAutohinted = true, FilterQuality = SKFilterQuality.High };
brush.UpdatePaint(paint, bounds);

return SKImageFilter.CreatePaint(paint, new SKImageFilter.CropRect(bounds));
return SKImageFilter.CreatePaint(paint, new(bounds));
}

return null;
Expand Down Expand Up @@ -454,6 +455,81 @@ half4 main()
return SKImageFilter.CreateArithmetic(multiplyAmount.Value, source1Amount.Value, source2Amount.Value, offset.Value, false, bgFilter, fgFilter, new(bounds));
}

return null;
}
case EffectType.ExposureEffect: // TODO: We can probably replace the pixel shader with a color matrix filter instead?
{
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("Exposure", out uint exposureProp, out _);

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));

// Reference (wuceffects.dll):
/*
void Windows::UI::Composition::ExposureEffectType::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 strMultiplierProperty;
bool isPropertyDynamic;
strInputPixel = pGenerator->GetInputPixelName(node, 0);
isPropertyDynamic = node->IsPropertyDynamic(0); // ExposureValue
pGenerator->DeclareShaderVariable(
&strMultiplierProperty,
DCOMPOSITION_EXPRESSION_TYPE_SCALAR,
"Multiplier",
isPropertyDynamic,
[](void** defaultProperties, void** output) { *(float*)*output = pow(2.0, *(float*)(defaultProperties[0])); }
);
pStringBuilder = pGenerator->BeginPSLine();
pStringBuilder->(pszOutputPixelName);
pStringBuilder->(" = minfloat4(");
pStringBuilder->(strInputPixel.c_str(), strInputPixel.size());
pStringBuilder->(".rgb * ");
pStringBuilder->(strMultiplierProperty.c_str(), strMultiplierProperty.size());
pStringBuilder->(", ");
pStringBuilder->(strInputPixel.c_str(), strInputPixel.size());
pStringBuilder->(".a);");
pStringBuilder->('\n');
}
*/
}

return null;
}
case EffectType.Unsupported:
Expand Down

0 comments on commit 85b301b

Please sign in to comment.