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)