Skip to content

Commit

Permalink
Bitmap effects support
Browse files Browse the repository at this point in the history
  • Loading branch information
kekekeks committed Apr 14, 2023
1 parent 5ed9738 commit cf28998
Show file tree
Hide file tree
Showing 26 changed files with 1,069 additions and 20 deletions.
40 changes: 40 additions & 0 deletions samples/RenderDemo/Pages/AnimationsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,41 @@
</Animation>
</Style.Animations>
</Style>
<Style Selector="Border.Blur">
<Style.Animations>
<Animation Duration="0:0:3"
IterationCount="Infinite"
PlaybackDirection="Alternate">
<KeyFrame Cue="0%">
<Setter Property="Effect" Value="blur(0)"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Effect" Value="blur(10)"/>
</KeyFrame>
</Animation>
</Style.Animations>
<Setter Property="Child" Value="{StaticResource Acorn}"/>
</Style>
<Style Selector="Border.DropShadow">
<Style.Animations>
<Animation Duration="0:0:3"
IterationCount="Infinite"
PlaybackDirection="Alternate">
<KeyFrame Cue="0%">
<Setter Property="Effect" Value="drop-shadow(0 0 0)"/>
</KeyFrame>
<KeyFrame Cue="35%">
<Setter Property="Effect" Value="drop-shadow(5 5 0 Green)"/>
</KeyFrame>
<KeyFrame Cue="70%">
<Setter Property="Effect" Value="drop-shadow(5 5 5 Red)"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Effect" Value="drop-shadow(20 -5 5 Blue)"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Styles>
</UserControl.Styles>
<Grid>
Expand All @@ -332,6 +367,11 @@
<Border Classes="Test Rect8" Child="{x:Null}" />
<Border Classes="Test Rect9" Child="{x:Null}" />
<Border Classes="Test Rect10" Child="{x:Null}" />
<Border Classes="Test Blur" Background="#ffa0a0a0" BorderThickness="4" BorderBrush="Yellow" Padding="10"/>
<Border Classes="Test DropShadow" Background="Transparent" BorderThickness="4" BorderBrush="Yellow">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Drop
Shadow</TextBlock>
</Border>
</WrapPanel>
</StackPanel>
</Grid>
Expand Down
22 changes: 22 additions & 0 deletions src/Avalonia.Base/Media/Effects/BlurEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
// ReSharper disable CheckNamespace
namespace Avalonia.Media;

public class BlurEffect : Effect, IBlurEffect, IMutableEffect
{
public static readonly StyledProperty<double> RadiusProperty = AvaloniaProperty.Register<BlurEffect, double>(
nameof(Radius), 5);

public double Radius
{
get => GetValue(RadiusProperty);
set => SetValue(RadiusProperty, value);
}

static BlurEffect()
{
AffectsRender<BlurEffect>(RadiusProperty);
}

public IImmutableEffect ToImmutable() => new ImmutableBlurEffect(Radius);
}
104 changes: 104 additions & 0 deletions src/Avalonia.Base/Media/Effects/DropShadowEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// ReSharper disable once CheckNamespace

using System;
// ReSharper disable CheckNamespace

namespace Avalonia.Media;

public abstract class DropShadowEffectBase : Effect
{
public static readonly StyledProperty<double> BlurRadiusProperty =
AvaloniaProperty.Register<DropShadowEffectBase, double>(
nameof(BlurRadius), 5);

public double BlurRadius
{
get => GetValue(BlurRadiusProperty);
set => SetValue(BlurRadiusProperty, value);
}

public static readonly StyledProperty<Color> ColorProperty = AvaloniaProperty.Register<DropShadowEffectBase, Color>(
nameof(Color));

public Color Color
{
get => GetValue(ColorProperty);
set => SetValue(ColorProperty, value);
}

public static readonly StyledProperty<double> OpacityProperty =
AvaloniaProperty.Register<DropShadowEffectBase, double>(
nameof(Opacity), 1);

public double Opacity
{
get => GetValue(OpacityProperty);
set => SetValue(OpacityProperty, value);
}

static DropShadowEffectBase()
{
AffectsRender<DropShadowEffectBase>(BlurRadiusProperty, ColorProperty, OpacityProperty);
}
}

public class DropShadowEffect : DropShadowEffectBase, IDropShadowEffect, IMutableEffect
{
public static readonly StyledProperty<double> OffsetXProperty = AvaloniaProperty.Register<DropShadowEffect, double>(
nameof(OffsetX), 3.5355);

public double OffsetX
{
get => GetValue(OffsetXProperty);
set => SetValue(OffsetXProperty, value);
}

public static readonly StyledProperty<double> OffsetYProperty = AvaloniaProperty.Register<DropShadowEffect, double>(
nameof(OffsetY), 3.5355);

public double OffsetY
{
get => GetValue(OffsetYProperty);
set => SetValue(OffsetYProperty, value);
}

static DropShadowEffect()
{
AffectsRender<DropShadowEffect>(OffsetXProperty, OffsetYProperty);
}

public IImmutableEffect ToImmutable()
{
return new ImmutableDropShadowEffect(OffsetX, OffsetY, BlurRadius, Color, Opacity);
}
}

/// <summary>
/// This class is compatible with WPF's DropShadowEffect and provides Direction and ShadowDepth properties instead of OffsetX/OffsetY
/// </summary>
public class DropShadowDirectionEffect : DropShadowEffectBase, IDirectionDropShadowEffect, IMutableEffect
{
public static readonly StyledProperty<double> ShadowDepthProperty =
AvaloniaProperty.Register<DropShadowDirectionEffect, double>(
nameof(ShadowDepth), 5);

public double ShadowDepth
{
get => GetValue(ShadowDepthProperty);
set => SetValue(ShadowDepthProperty, value);
}

public static readonly StyledProperty<double> DirectionProperty = AvaloniaProperty.Register<DropShadowDirectionEffect, double>(
nameof(Direction), 315);

public double Direction
{
get => GetValue(DirectionProperty);
set => SetValue(DirectionProperty, value);
}

public double OffsetX => Math.Cos(Direction) * ShadowDepth;
public double OffsetY => Math.Sin(Direction) * ShadowDepth;

public IImmutableEffect ToImmutable() => new ImmutableDropShadowDirectionEffect(OffsetX, OffsetY, BlurRadius, Color, Opacity);
}
93 changes: 93 additions & 0 deletions src/Avalonia.Base/Media/Effects/Effect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.Reactive;
using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Utilities;

// ReSharper disable once CheckNamespace
namespace Avalonia.Media;

public class Effect : Animatable, IAffectsRender
{
/// <summary>
/// Marks a property as affecting the brush's visual representation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a brush's static constructor, any change to the
/// property will cause the <see cref="Invalidated"/> event to be raised on the brush.
/// </remarks>
protected static void AffectsRender<T>(params AvaloniaProperty[] properties)
where T : Effect
{
var invalidateObserver = new AnonymousObserver<AvaloniaPropertyChangedEventArgs>(
static e => (e.Sender as T)?.RaiseInvalidated(EventArgs.Empty));

foreach (var property in properties)
{
property.Changed.Subscribe(invalidateObserver);
}
}

/// <summary>
/// Raises the <see cref="Invalidated"/> event.
/// </summary>
/// <param name="e">The event args.</param>
protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);

/// <inheritdoc />
public event EventHandler? Invalidated;


static Exception ParseError(string s) => throw new ArgumentException("Unable to parse effect: " + s);
public static IEffect Parse(string s)
{
var span = s.AsSpan();
var r = new TokenParser(span);
if (r.TryConsume("blur"))
{
if (!r.TryConsume('(') || !r.TryParseDouble(out var radius) || !r.TryConsume(')') || !r.IsEofWithWhitespace())
throw ParseError(s);
return new ImmutableBlurEffect(radius);
}


if (r.TryConsume("drop-shadow"))
{
if (!r.TryConsume('(') || !r.TryParseDouble(out var offsetX)
|| !r.TryParseDouble(out var offsetY))
throw ParseError(s);
double blurRadius = 0;
var color = Colors.Black;
if (!r.TryConsume(')'))
{
if (!r.TryParseDouble(out blurRadius) || blurRadius < 0)
throw ParseError(s);
if (!r.TryConsume(')'))
{
var endOfExpression = s.LastIndexOf(")", StringComparison.Ordinal);
if (endOfExpression == -1)
throw ParseError(s);

if (!new TokenParser(span.Slice(endOfExpression + 1)).IsEofWithWhitespace())
throw ParseError(s);

if (!Color.TryParse(span.Slice(r.Position, endOfExpression - r.Position).TrimEnd(), out color))
throw ParseError(s);
return new ImmutableDropShadowEffect(offsetX, offsetY, blurRadius, color, 1);
}
}
if (!r.IsEofWithWhitespace())
throw ParseError(s);
return new ImmutableDropShadowEffect(offsetX, offsetY, blurRadius, color, 1);
}

throw ParseError(s);
}

static Effect()
{
EffectAnimator.EnsureRegistered();
}
}
Loading

0 comments on commit cf28998

Please sign in to comment.