BlurBackground.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Media;
  4. using Avalonia.Platform;
  5. using Avalonia.Rendering.SceneGraph;
  6. using Avalonia.Skia;
  7. using Avalonia.Styling;
  8. using SkiaSharp;
  9. using System;
  10. namespace SukiUI.Controls.GlassMorphism;
  11. public class BlurBackground : Control
  12. {
  13. public static readonly StyledProperty<ExperimentalAcrylicMaterial> MaterialProperty =
  14. AvaloniaProperty.Register<BlurBackground, ExperimentalAcrylicMaterial>(
  15. "Material");
  16. public ExperimentalAcrylicMaterial Material
  17. {
  18. get => GetValue(MaterialProperty);
  19. set => SetValue(MaterialProperty, value);
  20. }
  21. private static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark =
  22. (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
  23. {
  24. MaterialOpacity = 0.25,
  25. TintColor = Colors.Black,
  26. TintOpacity = 0.7,
  27. PlatformTransparencyCompensationLevel = 0
  28. }.ToImmutable();
  29. private static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight =
  30. (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
  31. {
  32. MaterialOpacity = 0.0,
  33. TintColor = Colors.White,
  34. TintOpacity = 0.3,
  35. PlatformTransparencyCompensationLevel = 0
  36. }.ToImmutable();
  37. static BlurBackground()
  38. {
  39. AffectsRender<BlurBackground>(MaterialProperty);
  40. }
  41. public static SKBlendMode blendmodedark = SKBlendMode.Clear;
  42. private static SKShader s_acrylicNoiseShader;
  43. private class BlurBehindRenderOperation : ICustomDrawOperation
  44. {
  45. private readonly ImmutableExperimentalAcrylicMaterial _material;
  46. private readonly Rect _bounds;
  47. public BlurBehindRenderOperation(ImmutableExperimentalAcrylicMaterial material, Rect bounds)
  48. {
  49. _material = material;
  50. _bounds = bounds;
  51. }
  52. public void Dispose()
  53. {
  54. }
  55. public bool HitTest(Point p) => _bounds.Contains(p);
  56. static SKColorFilter CreateAlphaColorFilter(double opacity)
  57. {
  58. if (opacity > 1)
  59. opacity = 1;
  60. var c = new byte[256];
  61. var a = new byte[256];
  62. for (var i = 0; i < 256; i++)
  63. {
  64. c[i] = (byte)i;
  65. a[i] = (byte)(i * opacity);
  66. }
  67. return SKColorFilter.CreateTable(a, c, c, c);
  68. }
  69. public void Render(ImmediateDrawingContext context)
  70. {
  71. var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
  72. using var lease = leaseFeature.Lease();
  73. var canvas = lease.SkCanvas;
  74. if (!canvas.TotalMatrix.TryInvert(out var currentInvertedTransform))
  75. return;
  76. using var backgroundSnapshot = lease.SkSurface.Snapshot();
  77. using var backdropShader = SKShader.CreateImage(backgroundSnapshot, SKShaderTileMode.Clamp,
  78. SKShaderTileMode.Clamp, currentInvertedTransform);
  79. using var blurred = SKSurface.Create(lease.GrContext, false, new SKImageInfo(
  80. (int)Math.Ceiling(_bounds.Width),
  81. (int)Math.Ceiling(_bounds.Height), SKImageInfo.PlatformColorType, SKAlphaType.Premul));
  82. using (var filter = SKImageFilter.CreateBlur(3, 3))
  83. using (var blurPaint = new SKPaint
  84. {
  85. Shader = backdropShader,
  86. ImageFilter = filter
  87. })
  88. blurred.Canvas.DrawRect(0, 0, (float)_bounds.Width, (float)_bounds.Height, blurPaint);
  89. using (var blurSnap = blurred.Snapshot())
  90. using (var blurSnapShader = SKShader.CreateImage(blurSnap))
  91. {
  92. using var blurSnapPaint = new SKPaint
  93. {
  94. Shader = blurSnapShader,
  95. IsAntialias = false,
  96. };
  97. canvas.DrawRect(0, 0, (float)_bounds.Width, (float)_bounds.Height, blurSnapPaint);
  98. }
  99. //return;
  100. using var acrylliPaint = new SKPaint();
  101. acrylliPaint.IsAntialias = true;
  102. const double noiseOpacity = 0.01;
  103. var tintColor = _material.TintColor;
  104. var tint = new SKColor(tintColor.R, tintColor.G, tintColor.B, tintColor.A);
  105. if (s_acrylicNoiseShader == null)
  106. {
  107. using var stream =
  108. typeof(SkiaPlatform).Assembly.GetManifestResourceStream(
  109. "Avalonia.Skia.Assets.NoiseAsset_256X256_PNG.png");
  110. using var bitmap = SKBitmap.Decode(stream);
  111. s_acrylicNoiseShader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp)
  112. .WithColorFilter(CreateAlphaColorFilter(noiseOpacity));
  113. }
  114. using var backdrop = SKShader.CreateColor(new SKColor(_material.MaterialColor.R, _material.MaterialColor.G,
  115. _material.MaterialColor.B, _material.MaterialColor.A));
  116. using var tintShader = SKShader.CreateColor(tint);
  117. using var effectiveTint = SKShader.CreateCompose(backdrop, tintShader);
  118. using var compose = SKShader.CreateCompose(effectiveTint, s_acrylicNoiseShader);
  119. acrylliPaint.Shader = compose;
  120. acrylliPaint.IsAntialias = true;
  121. canvas.DrawRect(0, 0, (float)_bounds.Width, (float)_bounds.Height, acrylliPaint);
  122. }
  123. public Rect Bounds => _bounds.Inflate(4);
  124. public bool Equals(ICustomDrawOperation? other)
  125. {
  126. return other is BlurBehindRenderOperation op && op._bounds == _bounds && op._material.Equals(_material);
  127. }
  128. }
  129. public override void Render(DrawingContext context)
  130. {
  131. var mat = Material != null
  132. ? (ImmutableExperimentalAcrylicMaterial)Material.ToImmutable()
  133. : Application.Current.ActualThemeVariant == ThemeVariant.Dark
  134. ? DefaultAcrylicMaterialDark
  135. : DefaultAcrylicMaterialLight;
  136. context.Custom(new BlurBehindRenderOperation(mat, new Rect(default, Bounds.Size)));
  137. }
  138. }