using System; using Avalonia; using Avalonia.Controls; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Skia; using SkiaSharp; namespace Nitrox.Launcher.Models.Controls; /// /// Draws a blur filter over the already rendered content. /// /// /// Based off of GrayscaleControl /// public sealed class BlurControl : Decorator { public static readonly StyledProperty BlurStrengthProperty = AvaloniaProperty.Register(nameof(BlurStrength), 5); /// /// Sets or gets how strong the blur should be. Defaults to 5. /// public float BlurStrength { get => GetValue(BlurStrengthProperty); set => SetValue(BlurStrengthProperty, value); } static BlurControl() { ClipToBoundsProperty.OverrideDefaultValue(true); AffectsRender(OpacityProperty); AffectsRender(BlurStrengthProperty); } public override void Render(DrawingContext context) { context.Custom(new BlurBehindRenderOperation((byte)Math.Round(byte.MaxValue * Opacity), BlurStrength, new Rect(default, Bounds.Size))); } private sealed record BlurBehindRenderOperation : ICustomDrawOperation { private readonly Rect bounds; private readonly byte opacity; private readonly float strength; public Rect Bounds => bounds; public BlurBehindRenderOperation(byte opacity, float strength, Rect bounds) { this.opacity = opacity; this.strength = strength; this.bounds = bounds; } public void Dispose() { } public bool HitTest(Point p) => bounds.Contains(p); public void Render(ImmediateDrawingContext context) { ISkiaSharpApiLeaseFeature leaseFeature = context.TryGetFeature(); if (leaseFeature == null) { return; } using ISkiaSharpApiLease skia = leaseFeature.Lease(); if (!skia.SkCanvas.TotalMatrix.TryInvert(out SKMatrix currentInvertedTransform)) { return; } if (skia.SkSurface == null) { return; } using SKImage backgroundSnapshot = skia.SkSurface.Snapshot(); using SKShader backdropShader = SKShader.CreateImage(backgroundSnapshot, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp, currentInvertedTransform); using SKImageFilter blurFilter = SKImageFilter.CreateBlur(strength, strength); using SKPaint paint = new(); paint.Shader = backdropShader; paint.ImageFilter = blurFilter; paint.Color = new SKColor(0, 0, 0, opacity); skia.SkCanvas.DrawRect(0, 0, (float)bounds.Width, (float)bounds.Height, paint); } public bool Equals(ICustomDrawOperation other) => Equals(other as BlurBehindRenderOperation); } }