EffectDrawBase.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. using System;
  2. using System.Diagnostics;
  3. using Avalonia;
  4. using Avalonia.Media;
  5. using Avalonia.Platform;
  6. using Avalonia.Rendering.Composition;
  7. using Avalonia.Rendering.SceneGraph;
  8. using Avalonia.Skia;
  9. using Avalonia.Styling;
  10. using SkiaSharp;
  11. using SukiUI.Models;
  12. namespace SukiUI.Utilities.Effects
  13. {
  14. public abstract class EffectDrawBase : CompositionCustomVisualHandler
  15. {
  16. public static readonly object StartAnimations = new(), StopAnimations = new(),
  17. EnableForceSoftwareRendering = new(), DisableForceSoftwareRendering = new();
  18. private SukiEffect? _effect;
  19. public SukiEffect? Effect
  20. {
  21. get => _effect;
  22. set
  23. {
  24. var old = _effect;
  25. if (Equals(old, value)) return;
  26. _effect = value;
  27. EffectChanged(old, _effect);
  28. }
  29. }
  30. private bool _animationEnabled;
  31. public bool AnimationEnabled
  32. {
  33. get => _animationEnabled;
  34. set
  35. {
  36. if (value) _animationTick.Start();
  37. else _animationTick.Stop();
  38. _animationEnabled = value;
  39. }
  40. }
  41. public bool ForceSoftwareRendering { get; set; }
  42. protected float AnimationSpeedScale { get; set; } = 0.1f;
  43. protected ThemeVariant ActiveVariant { get; private set; }
  44. protected SukiColorTheme ActiveTheme { get; private set; }
  45. protected float AnimationSeconds => (float)_animationTick.Elapsed.TotalSeconds;
  46. private readonly Stopwatch _animationTick = new();
  47. private readonly bool _invalidateRect;
  48. protected EffectDrawBase(bool invalidateRect = true)
  49. {
  50. _invalidateRect = invalidateRect;
  51. var sTheme = SukiTheme.GetInstance();
  52. sTheme.OnBaseThemeChanged += v => ActiveVariant = v;
  53. ActiveVariant = sTheme.ActiveBaseTheme;
  54. sTheme.OnColorThemeChanged += t => ActiveTheme = t;
  55. ActiveTheme = sTheme.ActiveColorTheme!;
  56. }
  57. public override void OnRender(ImmediateDrawingContext context)
  58. {
  59. var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
  60. if (leaseFeature is null) throw new InvalidOperationException("Unable to lease Skia API");
  61. using var lease = leaseFeature.Lease();
  62. var rect = SKRect.Create((float)EffectiveSize.X, (float)EffectiveSize.Y);
  63. if(lease.GrContext is null || ForceSoftwareRendering) // GrContext is null whenever there is no hardware acceleration available
  64. RenderSoftware(lease.SkCanvas, rect);
  65. else
  66. Render(lease.SkCanvas, rect);
  67. }
  68. public override void OnMessage(object message)
  69. {
  70. if (message == StartAnimations)
  71. {
  72. AnimationEnabled = true;
  73. RegisterForNextAnimationFrameUpdate();
  74. }
  75. else if (message == StopAnimations)
  76. {
  77. AnimationEnabled = false;
  78. }
  79. else if (message is SukiEffect effect)
  80. {
  81. Effect = effect;
  82. }
  83. }
  84. public override void OnAnimationFrameUpdate()
  85. {
  86. if (!AnimationEnabled) return;
  87. if(_invalidateRect)
  88. Invalidate(GetRenderBounds());
  89. else
  90. Invalidate();
  91. RegisterForNextAnimationFrameUpdate();
  92. }
  93. //protected abstract void InvalidateInternal();
  94. /// <summary>
  95. /// Called every frame to render content.
  96. /// </summary>
  97. protected abstract void Render(SKCanvas canvas, SKRect rect);
  98. /// <summary>
  99. /// Called every frame whenever the app falls back to software rendering (or <see cref="ForceSoftwareRendering"/> is enabled)
  100. /// </summary>
  101. protected abstract void RenderSoftware(SKCanvas canvas, SKRect rect);
  102. protected SKShader? EffectWithUniforms(float alpha = 1f) =>
  103. EffectWithUniforms(Effect, alpha);
  104. protected SKShader? EffectWithUniforms(SukiEffect? effect, float alpha = 1f) =>
  105. effect?.ToShaderWithUniforms(AnimationSeconds, ActiveVariant, GetRenderBounds(), AnimationSpeedScale, alpha);
  106. protected SKShader? EffectWithCustomUniforms(Func<SKRuntimeEffect,SKRuntimeEffectUniforms> uniformFactory, float alpha = 1f) =>
  107. EffectWithCustomUniforms(Effect, uniformFactory, alpha);
  108. protected SKShader? EffectWithCustomUniforms(SukiEffect? effect, Func<SKRuntimeEffect,SKRuntimeEffectUniforms> uniformFactory, float alpha = 1f) =>
  109. effect?.ToShaderWithCustomUniforms(uniformFactory, AnimationSeconds, GetRenderBounds(), AnimationSpeedScale, alpha);
  110. protected virtual void EffectChanged(SukiEffect? oldValue, SukiEffect? newValue)
  111. {
  112. // no-op
  113. }
  114. public virtual void Dispose()
  115. {
  116. // no-op
  117. }
  118. public virtual bool Equals(ICustomDrawOperation other) => false;
  119. public virtual bool HitTest(Point p) => false;
  120. }
  121. }