Skip to content

Commit

Permalink
Merge pull request #310 from sirdoombox/main
Browse files Browse the repository at this point in the history
Rewrite internals of `EffectDrawBase`
  • Loading branch information
sirdoombox authored Oct 17, 2024
2 parents aaba8e2 + e8139a4 commit e0bed14
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 104 deletions.
43 changes: 28 additions & 15 deletions SukiUI.Demo/Features/Effects/ShaderToyRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.Rendering.Composition;
using SkiaSharp;
using SukiUI.Utilities.Effects;

namespace SukiUI.Demo.Features.Effects
{
public class ShaderToyRenderer : Control
{
private readonly ShaderToyDraw _draw;
private CompositionCustomVisual? _customVisual;
private SukiEffect? _sukiEffect;

protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var comp = ElementComposition.GetElementVisual(this)?.Compositor;
if (comp == null || _customVisual?.Compositor == comp) return;
var visualHandler = new ShaderToyDraw();
_customVisual = comp.CreateCustomVisual(visualHandler);
ElementComposition.SetElementChildVisual(this, _customVisual);
_customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations);
if(_sukiEffect != null) _customVisual.SendHandlerMessage(_sukiEffect);
Update();
}

public ShaderToyRenderer()
private void Update()
{
_draw = new ShaderToyDraw(Bounds);
if (_customVisual == null) return;
_customVisual.Size = new Vector(Bounds.Width, Bounds.Height);
}

public override void Render(DrawingContext context)
public void SetEffect(SukiEffect effect)
{
_draw.Bounds = Bounds;
context.Custom(_draw);
_sukiEffect = effect;
_customVisual?.SendHandlerMessage(effect);
}

public void SetEffect(SukiEffect effect)
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
_draw.Effect = effect;
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
base.OnPropertyChanged(change);
if(change.Property == BoundsProperty)
Update();
}

private class ShaderToyDraw : EffectDrawBase
{
public ShaderToyDraw(Rect bounds) : base(bounds)
public ShaderToyDraw()
{
AnimationEnabled = true;
AnimationSpeedScale = 2f;
}

protected override void Render(SKCanvas canvas, SKRect rect)
{
canvas.Scale(1,-1);
canvas.Translate(0, (float)-Bounds.Height);
using var mainShaderPaint = new SKPaint();

if (Effect is not null)
Expand All @@ -48,7 +62,6 @@ protected override void Render(SKCanvas canvas, SKRect rect)
mainShaderPaint.Shader = shader;
canvas.DrawRect(rect, mainShaderPaint);
}
canvas.Restore();
}

protected override void RenderSoftware(SKCanvas canvas, SKRect rect)
Expand Down
63 changes: 43 additions & 20 deletions SukiUI/Controls/Loading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition;
using SkiaSharp;
using SukiUI.Extensions;
using SukiUI.Utilities.Effects;
Expand Down Expand Up @@ -37,62 +38,77 @@ public IBrush? Foreground
{ LoadingStyle.Glow, SukiEffect.FromEmbeddedResource("glow") },
{ LoadingStyle.Pellets, SukiEffect.FromEmbeddedResource("pellets") },
};

private readonly LoadingEffectDraw _draw;

private CompositionCustomVisual? _customVisual;
public Loading()
{
Width = 50;
Height = 50;
_draw = new LoadingEffectDraw(Bounds);
}

public override void Render(DrawingContext context)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_draw.Bounds = Bounds;
_draw.Effect = Effects[LoadingStyle];
base.OnAttachedToVisualTree(e);
var comp = ElementComposition.GetElementVisual(this)?.Compositor;
if (comp == null || _customVisual?.Compositor == comp) return;
var visualHandler = new LoadingEffectDraw();
_customVisual = comp.CreateCustomVisual(visualHandler);
ElementComposition.SetElementChildVisual(this, _customVisual);
_customVisual.SendHandlerMessage(EffectDrawBase.StartAnimations);
if (Foreground is null)
this[!ForegroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
if (Foreground is ImmutableSolidColorBrush brush)
brush.Color.ToFloatArrayNonAlloc(_draw.Color);
context.Custom(_draw);
brush.Color.ToFloatArrayNonAlloc(_color);
_customVisual.SendHandlerMessage(_color);
_customVisual.SendHandlerMessage(Effects[LoadingStyle]);
Update();
}

private void Update()
{
if (_customVisual == null) return;
_customVisual.Size = new Vector(Bounds.Width, Bounds.Height);
}

private readonly float[] _color = new float[3];

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property != ForegroundProperty) return;
if (Foreground is ImmutableSolidColorBrush brush)
brush.Color.ToFloatArrayNonAlloc(_draw.Color);
if (change.Property == BoundsProperty)
Update();
else if (change.Property == ForegroundProperty && Foreground is ImmutableSolidColorBrush brush)
{
brush.Color.ToFloatArrayNonAlloc(_color);
_customVisual?.SendHandlerMessage(_color);
}
else if (change.Property == LoadingStyleProperty)
_customVisual?.SendHandlerMessage(Effects[LoadingStyle]);
}

public class LoadingEffectDraw : EffectDrawBase
{
public float[] Color { get; } = { 1.0f, 0f, 0f };
private float[] _color = { 1.0f, 0f, 0f };

public LoadingEffectDraw(Rect bounds) : base(bounds)
public LoadingEffectDraw()
{
AnimationEnabled = true;
AnimationSpeedScale = 2f;
}

protected override void Render(SKCanvas canvas, SKRect rect)
{
canvas.Scale(1, -1);
canvas.Translate(0, (float)-Bounds.Height);
using var mainShaderPaint = new SKPaint();

if (Effect is not null)
{
using var shader = EffectWithCustomUniforms(effect => new SKRuntimeEffectUniforms(effect)
{
{ "iForeground", Color }
{ "iForeground", _color }
});
mainShaderPaint.Shader = shader;
canvas.DrawRect(rect, mainShaderPaint);
}

canvas.Restore();
}

// I'm not really sure how to render this properly in software fallback scenarios.
Expand All @@ -102,6 +118,13 @@ protected override void RenderSoftware(SKCanvas canvas, SKRect rect)
{
throw new System.NotImplementedException();
}

public override void OnMessage(object message)
{
base.OnMessage(message);
if (message is float[] color)
_color = color;
}
}
}

Expand Down
74 changes: 41 additions & 33 deletions SukiUI/Controls/SukiBackground.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.Rendering.Composition;
using SukiUI.Enums;
using SukiUI.Utilities.Effects;

Expand Down Expand Up @@ -54,7 +51,7 @@ public string? ShaderCode
AvaloniaProperty.Register<SukiWindow, bool>(nameof(AnimationEnabled), defaultValue: false);

/// <summary>
/// Enables/disables animations - DEFAULT: False
/// [WARNING: This feature is experimental and has relatively high GPU utilisation] Enables/disables animations - DEFAULT: False
/// </summary>
public bool AnimationEnabled
{
Expand All @@ -65,8 +62,8 @@ public bool AnimationEnabled
public static readonly StyledProperty<bool> TransitionsEnabledProperty =
AvaloniaProperty.Register<SukiBackground, bool>(nameof(TransitionsEnabled), defaultValue: false);

/// <summary>
/// Enables/disables transition animations when switching backgrounds - DEFAULT: False
/// <summary>
/// Enables/disables transition animations when switching backgrounds, Currently non-functional - DEFAULT: False
/// </summary>
public bool TransitionsEnabled
{
Expand Down Expand Up @@ -94,54 +91,65 @@ public bool ForceSoftwareRendering
set => SetValue(ForceSoftwareRenderingProperty, value);
}

private readonly EffectBackgroundDraw _draw;

private CompositionCustomVisual? _customVisual;
public SukiBackground()
{
IsHitTestVisible = false;
_draw = new EffectBackgroundDraw(new Rect(0, 0, Bounds.Width, Bounds.Height));
}

protected override void OnLoaded(RoutedEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnLoaded(e);
_draw.ForceSoftwareRendering = ForceSoftwareRendering;
_draw.TransitionsEnabled = TransitionsEnabled;
_draw.TransitionTime = TransitionTime;
_draw.AnimationEnabled = AnimationEnabled;
base.OnAttachedToVisualTree(e);
var comp = ElementComposition.GetElementVisual(this)?.Compositor;
if (comp == null || _customVisual?.Compositor == comp) return;
var visualHandler = new EffectBackgroundDraw();
_customVisual = comp.CreateCustomVisual(visualHandler);
ElementComposition.SetElementChildVisual(this, _customVisual);
_customVisual.SendHandlerMessage(TransitionTime);
HandleBackgroundStyleChanges();
Update();
}

private void Update()
{
if (_customVisual == null) return;
_customVisual.Size = new Vector(Bounds.Width, Bounds.Height);
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ForceSoftwareRenderingProperty && change.NewValue is bool forceSoftwareRendering)
_draw.ForceSoftwareRendering = forceSoftwareRendering;
if (change.Property == BoundsProperty)
Update();
else if (change.Property == ForceSoftwareRenderingProperty && change.NewValue is bool forceSoftwareRendering)
_customVisual?.SendHandlerMessage(forceSoftwareRendering
? EffectDrawBase.EnableForceSoftwareRendering
: EffectDrawBase.DisableForceSoftwareRendering);
else if(change.Property == TransitionsEnabledProperty && change.NewValue is bool transitionEnabled)
_draw.TransitionsEnabled = transitionEnabled;
_customVisual?.SendHandlerMessage(transitionEnabled
? EffectBackgroundDraw.EnableTransitions
: EffectBackgroundDraw.DisableTransitions);
else if(change.Property == TransitionTimeProperty && change.NewValue is double transitionTime)
_draw.TransitionTime = transitionTime;
else if(change.Property == AnimationEnabledProperty && change.NewValue is bool animationEnabled)
_draw.AnimationEnabled = animationEnabled;
_customVisual?.SendHandlerMessage(transitionTime);
else if (change.Property == AnimationEnabledProperty && change.NewValue is bool animationEnabled)
_customVisual?.SendHandlerMessage(animationEnabled
? EffectDrawBase.StartAnimations
: EffectDrawBase.StopAnimations);
else if(change.Property == StyleProperty || change.Property == ShaderFileProperty || change.Property == ShaderCodeProperty)
HandleBackgroundStyleChanges();
}

public override void Render(DrawingContext context)
{
_draw.Bounds = Bounds;
context.Custom(_draw);
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
}


private void HandleBackgroundStyleChanges()
{
SukiEffect effect;
if (ShaderFile is not null)
_draw.Effect = SukiEffect.FromEmbeddedResource(ShaderFile);
effect = SukiEffect.FromEmbeddedResource(ShaderFile);
else if (ShaderCode is not null)
_draw.Effect = SukiEffect.FromString(ShaderCode);
effect = SukiEffect.FromString(ShaderCode);
else
_draw.Effect = SukiEffect.FromEmbeddedResource(Style.ToString());
effect = SukiEffect.FromEmbeddedResource(Style.ToString());
_customVisual?.SendHandlerMessage(effect);
}
}
}
Loading

0 comments on commit e0bed14

Please sign in to comment.