2019-07-15 10:46:41 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 16:43:03 +08:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2021-06-09 04:26:15 +08:00
using System ;
2022-01-11 20:22:16 +08:00
using System.Diagnostics ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Sprites ;
2018-09-06 12:15:43 +08:00
using osu.Framework.Graphics.Textures ;
2019-07-02 16:45:46 +08:00
using osu.Framework.Graphics.Transforms ;
using osuTK ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Graphics.Backgrounds
{
2019-07-02 16:45:46 +08:00
/// <summary>
2019-07-02 16:47:19 +08:00
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
2019-07-02 16:45:46 +08:00
/// </summary>
2021-06-09 04:26:15 +08:00
public class Background : CompositeDrawable , IEquatable < Background >
2018-04-13 17:19:50 +08:00
{
2019-07-15 10:45:15 +08:00
public readonly Sprite Sprite ;
2018-04-13 17:19:50 +08:00
private readonly string textureName ;
2019-07-02 16:45:46 +08:00
private BufferedContainer bufferedContainer ;
2018-04-13 17:19:50 +08:00
public Background ( string textureName = @"" )
{
this . textureName = textureName ;
RelativeSizeAxes = Axes . Both ;
2019-07-02 16:45:46 +08:00
AddInternal ( Sprite = new Sprite
2018-04-13 17:19:50 +08:00
{
RelativeSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
FillMode = FillMode . Fill ,
} ) ;
}
[BackgroundDependencyLoader]
2018-08-31 06:04:40 +08:00
private void load ( LargeTextureStore textures )
2018-04-13 17:19:50 +08:00
{
if ( ! string . IsNullOrEmpty ( textureName ) )
2018-08-31 06:04:40 +08:00
Sprite . Texture = textures . Get ( textureName ) ;
2018-04-13 17:19:50 +08:00
}
2019-07-02 16:45:46 +08:00
2022-01-11 20:22:16 +08:00
public Vector2 BlurSigma = > Vector2 . Divide ( bufferedContainer ? . BlurSigma ? ? Vector2 . Zero , blurScale ) ;
2019-07-02 16:45:46 +08:00
/// <summary>
/// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
/// </summary>
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
public void BlurTo ( Vector2 newBlurSigma , double duration = 0 , Easing easing = Easing . None )
{
2019-07-15 10:46:41 +08:00
if ( bufferedContainer = = null & & newBlurSigma ! = Vector2 . Zero )
2019-07-02 16:45:46 +08:00
{
RemoveInternal ( Sprite ) ;
2021-11-05 14:54:27 +08:00
AddInternal ( bufferedContainer = new BufferedContainer ( cachedFrameBuffer : true )
2019-07-02 16:45:46 +08:00
{
RelativeSizeAxes = Axes . Both ,
2019-09-04 18:38:12 +08:00
RedrawOnScale = false ,
2019-07-02 16:45:46 +08:00
Child = Sprite
} ) ;
}
2019-11-27 14:04:09 +08:00
if ( bufferedContainer ! = null )
2022-01-11 20:22:16 +08:00
transformBlurSigma ( newBlurSigma , 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.
}
}
/// <summary>
/// Determines a factor to downscale the background based on a given blur sigma, in order to reduce the computational complexity of blurs.
/// </summary>
/// <param name="sigma">The blur sigma.</param>
/// <returns>The scale-down factor.</returns>
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 ) ;
2019-11-27 14:04:09 +08:00
2022-01-11 20:22:16 +08:00
// 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 ;
2019-07-02 16:45:46 +08:00
}
2021-06-09 04:26:15 +08:00
public virtual bool Equals ( Background other )
{
if ( ReferenceEquals ( null , other ) ) return false ;
if ( ReferenceEquals ( this , other ) ) return true ;
return other . GetType ( ) = = GetType ( )
& & other . textureName = = textureName ;
}
2018-04-13 17:19:50 +08:00
}
}