mirror of
https://github.com/ppy/osu
synced 2025-02-20 20:47:09 +00:00
Merge pull request #23809 from peppy/crop-texture-uploads-song-select
Greatly improve song select performance by cropping beatmap backgrounds before display
This commit is contained in:
commit
3c51b5a53a
@ -0,0 +1,89 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
// Implementation of this class is based off of `MaxDimensionLimitedTextureLoaderStore`.
|
||||
// If issues are found it's worth checking to make sure similar issues exist there.
|
||||
public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore<TextureUpload>
|
||||
{
|
||||
// These numbers are taken from the draw visualiser size requirements for song select panel textures at extreme aspect ratios.
|
||||
private const int max_height = 130;
|
||||
private const int max_width = 1280;
|
||||
|
||||
private readonly IResourceStore<TextureUpload>? textureStore;
|
||||
|
||||
public BeatmapPanelBackgroundTextureLoaderStore(IResourceStore<TextureUpload>? textureStore)
|
||||
{
|
||||
this.textureStore = textureStore;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
textureStore?.Dispose();
|
||||
}
|
||||
|
||||
public TextureUpload Get(string name)
|
||||
{
|
||||
var textureUpload = textureStore?.Get(name);
|
||||
|
||||
// NRT not enabled on framework side classes (IResourceStore / TextureLoaderStore), welp.
|
||||
if (textureUpload == null)
|
||||
return null!;
|
||||
|
||||
return limitTextureUploadSize(textureUpload);
|
||||
}
|
||||
|
||||
public async Task<TextureUpload> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
// NRT not enabled on framework side classes (IResourceStore / TextureLoaderStore), welp.
|
||||
if (textureStore == null)
|
||||
return null!;
|
||||
|
||||
var textureUpload = await textureStore.GetAsync(name, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (textureUpload == null)
|
||||
return null!;
|
||||
|
||||
return await Task.Run(() => limitTextureUploadSize(textureUpload), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private TextureUpload limitTextureUploadSize(TextureUpload textureUpload)
|
||||
{
|
||||
var image = Image.LoadPixelData(textureUpload.Data.ToArray(), textureUpload.Width, textureUpload.Height);
|
||||
|
||||
// The original texture upload will no longer be returned or used.
|
||||
textureUpload.Dispose();
|
||||
|
||||
Size size = image.Size();
|
||||
int usableWidth = Math.Min(max_width, size.Width);
|
||||
int usableHeight = Math.Min(max_height, size.Height);
|
||||
|
||||
// Crop the centre region of the background for now.
|
||||
Rectangle cropRectangle = new Rectangle(
|
||||
(size.Width - usableWidth) / 2,
|
||||
(size.Height - usableHeight) / 2,
|
||||
usableWidth,
|
||||
usableHeight
|
||||
);
|
||||
|
||||
image.Mutate(i => i.Crop(cropRectangle));
|
||||
|
||||
return new TextureUpload(image);
|
||||
}
|
||||
|
||||
public Stream? GetStream(string name) => textureStore?.GetStream(name);
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() => textureStore?.GetAvailableResources() ?? Array.Empty<string>();
|
||||
}
|
||||
}
|
@ -9,13 +9,18 @@ using osu.Game.IO;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface IBeatmapResourceProvider : IStorageResourceProvider
|
||||
internal interface IBeatmapResourceProvider : IStorageResourceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve a global large texture store, used for loading beatmap backgrounds.
|
||||
/// </summary>
|
||||
TextureStore LargeTextureStore { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a global large texture store, used specifically for retrieving cropped beatmap panel backgrounds.
|
||||
/// </summary>
|
||||
TextureStore BeatmapPanelTextureStore { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Access a global track store for retrieving beatmap tracks from.
|
||||
/// </summary>
|
||||
|
@ -32,12 +32,12 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// Whether the Beatmap has finished loading.
|
||||
///</summary>
|
||||
public bool BeatmapLoaded { get; }
|
||||
bool BeatmapLoaded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Track has finished loading.
|
||||
///</summary>
|
||||
public bool TrackLoaded { get; }
|
||||
bool TrackLoaded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="IBeatmap"/> which this <see cref="IWorkingBeatmap"/> represents.
|
||||
@ -49,6 +49,11 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
Texture GetBackground();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a cropped background for this <see cref="IWorkingBeatmap"/> used for display on panels.
|
||||
/// </summary>
|
||||
Texture GetPanelBackground();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="Waveform"/> for the <see cref="Track"/> of this <see cref="IWorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
@ -124,12 +129,12 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// Beings loading the contents of this <see cref="IWorkingBeatmap"/> asynchronously.
|
||||
/// </summary>
|
||||
public void BeginAsyncLoad();
|
||||
void BeginAsyncLoad();
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the asynchronous loading of the contents of this <see cref="IWorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
public void CancelAsyncLoad();
|
||||
void CancelAsyncLoad();
|
||||
|
||||
/// <summary>
|
||||
/// Reads the correct track restart point from beatmap metadata and sets looping to enabled.
|
||||
|
@ -66,6 +66,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
protected abstract IBeatmap GetBeatmap();
|
||||
public abstract Texture GetBackground();
|
||||
public virtual Texture GetPanelBackground() => GetBackground();
|
||||
protected abstract Track GetBeatmapTrack();
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Beatmaps
|
||||
private readonly AudioManager audioManager;
|
||||
private readonly IResourceStore<byte[]> resources;
|
||||
private readonly LargeTextureStore largeTextureStore;
|
||||
private readonly LargeTextureStore beatmapPanelTextureStore;
|
||||
private readonly ITrackStore trackStore;
|
||||
private readonly IResourceStore<byte[]> files;
|
||||
|
||||
@ -58,6 +59,7 @@ namespace osu.Game.Beatmaps
|
||||
this.host = host;
|
||||
this.files = files;
|
||||
largeTextureStore = new LargeTextureStore(host?.Renderer ?? new DummyRenderer(), host?.CreateTextureLoaderStore(files));
|
||||
beatmapPanelTextureStore = new LargeTextureStore(host?.Renderer ?? new DummyRenderer(), new BeatmapPanelBackgroundTextureLoaderStore(host?.CreateTextureLoaderStore(files)));
|
||||
this.trackStore = trackStore;
|
||||
}
|
||||
|
||||
@ -110,6 +112,7 @@ namespace osu.Game.Beatmaps
|
||||
#region IResourceStorageProvider
|
||||
|
||||
TextureStore IBeatmapResourceProvider.LargeTextureStore => largeTextureStore;
|
||||
TextureStore IBeatmapResourceProvider.BeatmapPanelTextureStore => beatmapPanelTextureStore;
|
||||
ITrackStore IBeatmapResourceProvider.Tracks => trackStore;
|
||||
IRenderer IStorageResourceProvider.Renderer => host?.Renderer ?? new DummyRenderer();
|
||||
AudioManager IStorageResourceProvider.AudioManager => audioManager;
|
||||
@ -160,7 +163,11 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public override Texture GetBackground()
|
||||
public override Texture GetPanelBackground() => getBackgroundFromStore(resources.BeatmapPanelTextureStore);
|
||||
|
||||
public override Texture GetBackground() => getBackgroundFromStore(resources.LargeTextureStore);
|
||||
|
||||
private Texture getBackgroundFromStore(TextureStore store)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Metadata?.BackgroundFile))
|
||||
return null;
|
||||
@ -168,7 +175,7 @@ namespace osu.Game.Beatmaps
|
||||
try
|
||||
{
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile);
|
||||
var texture = resources.LargeTextureStore.Get(fileStorePath);
|
||||
var texture = store.Get(fileStorePath);
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
|
@ -1,12 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -21,7 +23,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapBackgroundSprite(working)
|
||||
new PanelBeatmapBackground(working)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -68,5 +70,23 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public partial class PanelBeatmapBackground : Sprite
|
||||
{
|
||||
private readonly IWorkingBeatmap working;
|
||||
|
||||
public PanelBeatmapBackground(IWorkingBeatmap working)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(working);
|
||||
|
||||
this.working = working;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Texture = working.GetPanelBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user