Files
Toolkit2/Toolkit.UI.Controls.Avalonia/BlurBehind/BlurBehind.cs
T
2024-04-13 11:41:33 +01:00

116 lines
4.5 KiB
C#

using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Skia;
using Avalonia.Styling;
using SkiaSharp;
namespace Toolkit.UI.Controls.Avalonia;
public class BlurBehind :
Control
{
public static readonly StyledProperty<ExperimentalAcrylicMaterial> MaterialProperty =
AvaloniaProperty.Register<BlurBehind, ExperimentalAcrylicMaterial>("Material");
public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark =
(ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
{
MaterialOpacity = 0.25,
TintColor = Colors.Black,
TintOpacity = 0.7,
PlatformTransparencyCompensationLevel = 0
}.ToImmutable();
public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight =
(ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
{
MaterialOpacity = 0.0,
TintColor = Colors.White,
TintOpacity = 0.3,
PlatformTransparencyCompensationLevel = 0
}.ToImmutable();
static BlurBehind()
{
AffectsRender<BlurBehind>(MaterialProperty);
}
public ExperimentalAcrylicMaterial Material
{
get => GetValue(MaterialProperty);
set => SetValue(MaterialProperty, value);
}
public override void Render(DrawingContext context)
{
ImmutableExperimentalAcrylicMaterial material = Material is not null
? (ImmutableExperimentalAcrylicMaterial)Material.ToImmutable()
: Application.Current?.ActualThemeVariant == ThemeVariant.Dark ? DefaultAcrylicMaterialDark : DefaultAcrylicMaterialLight;
context.Custom(new BlurBehindRenderOperation(material, new Rect(default, Bounds.Size)));
}
private class BlurBehindRenderOperation(ImmutableExperimentalAcrylicMaterial material,
Rect bounds) : ICustomDrawOperation
{
private readonly Rect bounds = bounds;
private readonly ImmutableExperimentalAcrylicMaterial material = material;
public Rect Bounds => bounds.Inflate(4);
public void Dispose()
{
}
public bool Equals(ICustomDrawOperation? other) =>
other is BlurBehindRenderOperation behindRenderOperation &&
behindRenderOperation.bounds == bounds && behindRenderOperation.material.Equals(material);
public bool HitTest(Point point) => bounds.Contains(point);
public void Render(ImmediateDrawingContext context)
{
if (context.TryGetFeature<ISkiaSharpApiLeaseFeature>() is ISkiaSharpApiLeaseFeature leaseFeature)
{
using ISkiaSharpApiLease? lease = leaseFeature.Lease();
if (lease.SkCanvas is SKCanvas canvas)
{
if (canvas.TotalMatrix.TryInvert(out SKMatrix currentInvertedTransform))
{
if (lease.SkSurface is SKSurface surface)
{
using SKImage backgroundSnapshot = surface.Snapshot();
using SKShader backdropShader = SKShader.CreateImage(backgroundSnapshot, SKShaderTileMode.Clamp,
SKShaderTileMode.Clamp, currentInvertedTransform);
using SKSurface blurred = SKSurface.Create(lease.GrContext, false,
new SKImageInfo((int)Math.Ceiling(bounds.Width), (int)Math.Ceiling(bounds.Height),
SKImageInfo.PlatformColorType, SKAlphaType.Premul));
using (SKImageFilter filter = SKImageFilter.CreateBlur(8, 8, SKShaderTileMode.Clamp))
using (SKPaint blurPaint = new() { Shader = backdropShader, ImageFilter = filter })
blurred.Canvas.DrawRect(5, 5, (float)bounds.Width - 20, (float)bounds.Height - 20, blurPaint);
using SKImage blurSnap = blurred.Snapshot();
using SKShader blurSnapShader = SKShader.CreateImage(blurSnap);
using SKPaint blurSnapPaint = new()
{
Shader = blurSnapShader,
IsAntialias = true
};
canvas.DrawRect(0, 0, (float)bounds.Width, (float)bounds.Height, blurSnapPaint);
}
}
}
}
}
}
}