EffectBackgroundDraw.cs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System;
  2. using System.Diagnostics;
  3. using Avalonia;
  4. using Avalonia.Skia;
  5. using Avalonia.Styling;
  6. using SkiaSharp;
  7. namespace SukiUI.Utilities.Effects
  8. {
  9. internal class EffectBackgroundDraw : EffectDrawBase
  10. {
  11. public static readonly object EnableTransitions = new(), DisableTransitions = new();
  12. internal bool TransitionsEnabled { get; set; }
  13. internal double TransitionTime { get; set; }
  14. private float TransitionSeconds => (float)CompositionNow.TotalSeconds;
  15. private SukiEffect? _oldEffect;
  16. private float _transitionStartTime;
  17. private float _transitionEndTime;
  18. public EffectBackgroundDraw() : base(false)
  19. {
  20. }
  21. protected override void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue)
  22. {
  23. if (!TransitionsEnabled) return;
  24. if (oldValue is null || Equals(oldValue, newValue)) return;
  25. _oldEffect = oldValue;
  26. _transitionStartTime = TransitionSeconds;
  27. _transitionEndTime = TransitionSeconds + (float)Math.Max(0, TransitionTime);
  28. }
  29. public override void OnMessage(object message)
  30. {
  31. base.OnMessage(message);
  32. if (message == EnableTransitions) TransitionsEnabled = true;
  33. else if (message == DisableTransitions) TransitionsEnabled = false;
  34. if (message is double time) TransitionTime = time;
  35. }
  36. protected override void Render(SKCanvas canvas, SKRect rect)
  37. {
  38. if (Effect is not null)
  39. {
  40. using var paint = new SKPaint();
  41. using var shader = EffectWithUniforms();
  42. paint.Shader = shader;
  43. canvas.DrawRect(rect, paint);
  44. }
  45. if (_oldEffect is not null)
  46. {
  47. using var paint = new SKPaint();
  48. // TODO: Investigate how to blend the shaders better - currently the only problem with this system.
  49. // Blend modes effect the transition quite heavily, only these 3 seem to work in any reasonable way.
  50. // paint.BlendMode = SKBlendMode.ColorBurn; // - Okay
  51. // paint.BlendMode = SKBlendMode.Overlay; // - Not Great
  52. paint.BlendMode = SKBlendMode.Darken; // - Best
  53. var lerped = InverseLerp(_transitionStartTime, _transitionEndTime, TransitionSeconds);
  54. using var shader = EffectWithUniforms(_oldEffect, (float)(1 - lerped));
  55. paint.Shader = shader;
  56. if (lerped < 1)
  57. {
  58. canvas.DrawRect(rect, paint);
  59. if(!AnimationEnabled) Invalidate();
  60. }
  61. else
  62. _oldEffect = null;
  63. }
  64. }
  65. protected override void RenderSoftware(SKCanvas canvas, SKRect rect)
  66. {
  67. if (ActiveVariant == ThemeVariant.Dark)
  68. canvas.Clear(ActiveTheme.Background.ToSKColor());
  69. else
  70. canvas.Clear(new SKColorF(0.95f, 0.95f, 0.95f, 1f));
  71. }
  72. private static double InverseLerp(double start, double end, double value) =>
  73. Math.Max(0, Math.Min(1, (value - start) / (end - start)));
  74. }
  75. }