Merge pull request #27967 from peppy/storyboard-video-transforms-and-sizing

Fix storyboard videos not accepting transforms
This commit is contained in:
Bartłomiej Dach 2024-04-23 18:09:09 +02:00 committed by GitHub
commit c454dd2496
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 39 deletions

View File

@ -10,6 +10,7 @@
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
@ -40,7 +41,7 @@ public void TestSkinSpriteDisallowedByDefault()
AddStep("disallow all lookups", () =>
{
storyboard.UseSkinSprites = false;
storyboard.AlwaysProvideTexture = false;
storyboard.ProvideResources = false;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -55,7 +56,7 @@ public void TestLookupFromStoryboard()
AddStep("allow storyboard lookup", () =>
{
storyboard.UseSkinSprites = false;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -67,13 +68,48 @@ public void TestLookupFromStoryboard()
assertStoryboardSourced();
}
[TestCase(false)]
[TestCase(true)]
public void TestVideo(bool scaleTransformProvided)
{
AddStep("allow storyboard lookup", () =>
{
storyboard.ProvideResources = true;
});
AddStep("create video", () => SetContents(_ =>
{
var layer = storyboard.GetLayer("Video");
var sprite = new StoryboardVideo("Videos/test-video.mp4", Time.Current);
if (scaleTransformProvided)
{
sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current + 1000, 1, 2);
sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current + 1000, Time.Current + 2000, 2, 1);
}
layer.Elements.Clear();
layer.Add(sprite);
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
storyboard.CreateDrawable()
}
};
}));
}
[Test]
public void TestSkinLookupPreferredOverStoryboard()
{
AddStep("allow all lookups", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -91,7 +127,7 @@ public void TestAllowLookupFromSkin()
AddStep("allow skin lookup", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = false;
storyboard.ProvideResources = false;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -109,7 +145,7 @@ public void TestFlippedSprite()
AddStep("allow all lookups", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -127,7 +163,7 @@ public void TestZeroScale()
AddStep("allow all lookups", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -142,7 +178,7 @@ public void TestNegativeScale()
AddStep("allow all lookups", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -156,7 +192,7 @@ public void TestNegativeScaleWithFlippedSprite()
AddStep("allow all lookups", () =>
{
storyboard.UseSkinSprites = true;
storyboard.AlwaysProvideTexture = true;
storyboard.ProvideResources = true;
});
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
@ -170,7 +206,7 @@ public void TestNegativeScaleWithFlippedSprite()
AddAssert("origin back", () => sprites.All(s => s.Origin == Anchor.TopLeft));
}
private DrawableStoryboard createSprite(string lookupName, Anchor origin, Vector2 initialPosition)
private Drawable createSprite(string lookupName, Anchor origin, Vector2 initialPosition)
{
var layer = storyboard.GetLayer("Background");
@ -180,7 +216,14 @@ private DrawableStoryboard createSprite(string lookupName, Anchor origin, Vector
layer.Elements.Clear();
layer.Add(sprite);
return storyboard.CreateDrawable().With(s => s.RelativeSizeAxes = Axes.Both);
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
storyboard.CreateDrawable()
}
};
}
private void assertStoryboardSourced()
@ -202,42 +245,52 @@ public override DrawableStoryboard CreateDrawable(IReadOnlyList<Mod>? mods = nul
return new TestDrawableStoryboard(this, mods);
}
public bool AlwaysProvideTexture { get; set; }
public bool ProvideResources { get; set; }
public override string GetStoragePathFromStoryboardPath(string path) => AlwaysProvideTexture ? path : string.Empty;
public override string GetStoragePathFromStoryboardPath(string path) => ProvideResources ? path : string.Empty;
private partial class TestDrawableStoryboard : DrawableStoryboard
{
private readonly bool alwaysProvideTexture;
private readonly bool provideResources;
public TestDrawableStoryboard(TestStoryboard storyboard, IReadOnlyList<Mod>? mods)
: base(storyboard, mods)
{
alwaysProvideTexture = storyboard.AlwaysProvideTexture;
provideResources = storyboard.ProvideResources;
}
protected override IResourceStore<byte[]> CreateResourceLookupStore() => alwaysProvideTexture
? new AlwaysReturnsTextureStore()
protected override IResourceStore<byte[]> CreateResourceLookupStore() => provideResources
? new ResourcesTextureStore()
: new ResourceStore<byte[]>();
internal class AlwaysReturnsTextureStore : IResourceStore<byte[]>
internal class ResourcesTextureStore : IResourceStore<byte[]>
{
private const string test_image = "Resources/Textures/test-image.png";
private readonly DllResourceStore store;
public AlwaysReturnsTextureStore()
public ResourcesTextureStore()
{
store = TestResources.GetStore();
}
public void Dispose() => store.Dispose();
public byte[] Get(string name) => store.Get(test_image);
public byte[] Get(string name) => store.Get(map(name));
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(test_image, cancellationToken);
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(map(name), cancellationToken);
public Stream GetStream(string name) => store.GetStream(test_image);
public Stream GetStream(string name) => store.GetStream(map(name));
private string map(string name)
{
switch (name)
{
case lookup_name:
return "Resources/Textures/test-image.png";
default:
return $"Resources/{name}";
}
}
public IEnumerable<string> GetAvailableResources() => store.GetAvailableResources();
}

View File

@ -114,7 +114,7 @@ private void handleEvents(string line)
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant()))
break;
storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset));
storyboard.GetLayer("Video").Add(storyboardSprite = new StoryboardVideo(path, offset));
break;
}

View File

@ -2,12 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
namespace osu.Game.Storyboards.Drawables
{
@ -23,11 +21,21 @@ public DrawableStoryboardVideo(StoryboardVideo video)
{
Video = video;
RelativeSizeAxes = Axes.Both;
// In osu-stable, a mapper can add a scale command for a storyboard video.
// This allows scaling based on the video's absolute size.
//
// If not specified we take up the full available space.
bool useRelative = !video.TimelineGroup.Scale.HasCommands;
RelativeSizeAxes = useRelative ? Axes.Both : Axes.None;
AutoSizeAxes = useRelative ? Axes.None : Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
[BackgroundDependencyLoader(true)]
private void load(IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
private void load(TextureStore textureStore)
{
var stream = textureStore.GetStream(Video.Path);
@ -36,12 +44,14 @@ private void load(IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
InternalChild = drawableVideo = new Video(stream, false)
{
RelativeSizeAxes = Axes.Both,
RelativeSizeAxes = RelativeSizeAxes,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
};
Video.ApplyTransforms(drawableVideo);
}
protected override void LoadComplete()

View File

@ -3,23 +3,20 @@
using osu.Framework.Graphics;
using osu.Game.Storyboards.Drawables;
using osuTK;
namespace osu.Game.Storyboards
{
public class StoryboardVideo : IStoryboardElement
public class StoryboardVideo : StoryboardSprite
{
public string Path { get; }
public bool IsDrawable => true;
public double StartTime { get; }
public StoryboardVideo(string path, double offset)
: base(path, Anchor.Centre, Vector2.Zero)
{
Path = path;
StartTime = offset;
// This is just required to get a valid StartTime based on the incoming offset.
// Actual fades are handled inside DrawableStoryboardVideo for now.
TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 0);
}
public Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
public override Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
}
}