diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
index 54c5c252c0..b1f642b909 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
@@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Input.States;
using osu.Framework.Platform;
using osu.Framework.Screens;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
@@ -323,7 +324,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
- public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
+ public bool IsUserBlurApplied() => Precision.AlmostEquals(background.CurrentBlur, new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR), 0.1f);
public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0);
@@ -331,9 +332,9 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
- public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR);
+ public bool IsBackgroundBlur() => Precision.AlmostEquals(background.CurrentBlur, new Vector2(BACKGROUND_BLUR), 0.1f);
- public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected;
+ public bool CheckBackgroundBlur(Vector2 expected) => Precision.AlmostEquals(background.CurrentBlur, expected, 0.1f);
///
/// Make sure every time a screen gets pushed, the background doesn't get replaced
diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs
index 353054a1f1..b09ec1d9b9 100644
--- a/osu.Game/Graphics/Backgrounds/Background.cs
+++ b/osu.Game/Graphics/Backgrounds/Background.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -17,8 +18,6 @@ namespace osu.Game.Graphics.Backgrounds
///
public class Background : CompositeDrawable, IEquatable
{
- private const float blur_scale = 0.5f;
-
public readonly Sprite Sprite;
private readonly string textureName;
@@ -46,7 +45,7 @@ namespace osu.Game.Graphics.Backgrounds
Sprite.Texture = textures.Get(textureName);
}
- public Vector2 BlurSigma => bufferedContainer?.BlurSigma / blur_scale ?? Vector2.Zero;
+ public Vector2 BlurSigma => Vector2.Divide(bufferedContainer?.BlurSigma ?? Vector2.Zero, blurScale);
///
/// Smoothly adjusts over time.
@@ -67,9 +66,48 @@ namespace osu.Game.Graphics.Backgrounds
}
if (bufferedContainer != null)
- bufferedContainer.FrameBufferScale = newBlurSigma == Vector2.Zero ? Vector2.One : new Vector2(blur_scale);
+ transformBlurSigma(newBlurSigma, duration, easing);
+ }
- bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
+ private void transformBlurSigma(Vector2 newBlurSigma, double duration, Easing easing)
+ => this.TransformTo(nameof(blurSigma), newBlurSigma, duration, easing);
+
+ private Vector2 blurSigmaBacking = Vector2.Zero;
+ private Vector2 blurScale = Vector2.One;
+
+ private Vector2 blurSigma
+ {
+ get => blurSigmaBacking;
+ set
+ {
+ Debug.Assert(bufferedContainer != null);
+
+ blurSigmaBacking = value;
+ blurScale = new Vector2(calculateBlurDownscale(value.X), calculateBlurDownscale(value.Y));
+
+ bufferedContainer.FrameBufferScale = blurScale;
+ bufferedContainer.BlurSigma = value * blurScale; // If the image is scaled down, the blur radius also needs to be reduced to cover the same pixel block.
+ }
+ }
+
+ ///
+ /// Determines a factor to downscale the background based on a given blur sigma, in order to reduce the computational complexity of blurs.
+ ///
+ /// The blur sigma.
+ /// The scale-down factor.
+ private float calculateBlurDownscale(float sigma)
+ {
+ // If we're blurring within one pixel, scaling down will always result in an undesirable loss of quality.
+ // The algorithm below would also cause this value to go above 1, which is likewise undesirable.
+ if (sigma <= 1)
+ return 1;
+
+ // A good value is one where the loss in quality as a result of downscaling the image is not easily perceivable.
+ // The constants here have been experimentally chosen to yield nice transitions by approximating a log curve through the points {{ 1, 1 }, { 4, 0.75 }, { 16, 0.5 }, { 32, 0.25 }}.
+ float scale = -0.18f * MathF.Log(0.004f * sigma);
+
+ // To reduce shimmering, the scaling transitions are limited to happen only in increments of 0.2.
+ return MathF.Round(scale / 0.2f, MidpointRounding.AwayFromZero) * 0.2f;
}
public virtual bool Equals(Background other)