Skip to content

Commit

Permalink
Merge pull request #27967 from peppy/storyboard-video-transforms-and-…
Browse files Browse the repository at this point in the history
…sizing

Fix storyboard videos not accepting transforms
  • Loading branch information
bdach authored Apr 23, 2024
2 parents 999c8fd + 602b16f commit c454dd2
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 39 deletions.
99 changes: 76 additions & 23 deletions osu.Game.Tests/Visual/Gameplay/TestSceneDrawableStoryboardSprite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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)));
Expand All @@ -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");

Expand All @@ -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()
Expand All @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
20 changes: 15 additions & 5 deletions osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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);

Expand All @@ -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()
Expand Down
17 changes: 7 additions & 10 deletions osu.Game/Storyboards/StoryboardVideo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit c454dd2

Please sign in to comment.