Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should add center time-tag inside every input time-tag in the input list. #182

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions osu.Framework.Font.Tests/Graphics/Sprites/KaraokeSpriteTextTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) karaoke.dev <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using NUnit.Framework;
using osu.Framework.Font.Tests.Helper;
using osu.Framework.Graphics.Sprites;

namespace osu.Framework.Font.Tests.Graphics.Sprites
{
public class KaraokeSpriteTextTest
{
[TestCase(new[] { "[0,start]:500", "[0,end]:600" },
new[] { "[0,start]:500", "[0,end]:600" })] // there's no need to add the interpolation time-tags.
[TestCase(new[] { "[-1,start]:500", "[4,start]:600" },
new string[] { })] // will filter those out-of-range time-tags.
[TestCase(new[] { "[0,start]:500", "[1,start]:501" },
new[] { "[0,start]:500", "[1,start]:501" })] // there's no need to add the interpolation time-tags because timing is too small.
[TestCase(new[] { "[0,start]:500", "[2,start]:600" },
new[] { "[0,start]:500", "[1,end]:599", "[2,start]:600" })] // It's time to add the interpolation time-tags.
[TestCase(new[] { "[0,end]:500", "[1,end]:600" },
new[] { "[0,end]:500", "[1,start]:501", "[1,end]:600" })]
[TestCase(new[] { "[0,end]:500", "[2,start]:600" },
new[] { "[0,end]:500", "[1,start]:501", "[1,end]:599", "[2,start]:600" })]
[TestCase(new[] { "[0,end]:500", "[3,start]:600" },
new[] { "[0,end]:500", "[1,start]:501", "[2,end]:599", "[3,start]:600" })]
[TestCase(new[] { "[2,start]:500", "[0,start]:600" },
new[] { "[2,start]:500", "[1,end]:501", "[0,start]:600" })] // let's test some reverse state
[TestCase(new[] { "[1,end]:500", "[0,end]:600" },
new[] { "[1,end]:500", "[1,start]:599", "[0,end]:600" })]
[TestCase(new[] { "[2,start]:500", "[0,end]:600" },
new[] { "[2,start]:500", "[1,end]:501", "[1,start]:599", "[0,end]:600" })]
[TestCase(new[] { "[3,start]:500", "[0,end]:600" },
new[] { "[3,start]:500", "[2,end]:501", "[1,start]:599", "[0,end]:600" })]
public void TestGetInterpolatedTimeTags(string[] timeTags, string[] expectedTimeTags)
{
var karaokeSpriteText = new KaraokeSpriteText
{
Text = "カラオケ",
TimeTags = TestCaseTagHelper.ParseTimeTags(timeTags)
};

var expectedInterpolatedTimeTags = TestCaseTagHelper.ParseTimeTags(expectedTimeTags);
var actualInterpolatedTimeTags = karaokeSpriteText.GetInterpolatedTimeTags();

Assert.AreEqual(expectedInterpolatedTimeTags, actualInterpolatedTimeTags);
}
}
}
46 changes: 46 additions & 0 deletions osu.Framework.Font.Tests/Utils/TextIndexUtilsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,51 @@ public void TestClamp(int index, TextIndex.IndexState state, int minIndex, int m
Assert.Throws<ArgumentException>(() => TextIndexUtils.Clamp(textIndex, minIndex, maxIndex));
}
}

[TestCase(0, TextIndex.IndexState.Start, 0)]
[TestCase(0, TextIndex.IndexState.End, 1)]
[TestCase(-1, TextIndex.IndexState.Start, -1)] // In utils not checking is index out of range
[TestCase(-1, TextIndex.IndexState.End, 0)]
public void TestToStringIndex(int index, TextIndex.IndexState state, int expected)
{
var textIndex = new TextIndex(index, state);

int actual = TextIndexUtils.ToStringIndex(textIndex);
Assert.AreEqual(expected, actual);
}

[TestCase(TextIndex.IndexState.Start, TextIndex.IndexState.End)]
[TestCase(TextIndex.IndexState.End, TextIndex.IndexState.Start)]
public void TestReverseState(TextIndex.IndexState state, TextIndex.IndexState expected)
{
var actual = TextIndexUtils.ReverseState(state);
Assert.AreEqual(expected, actual);
}

[TestCase(1, TextIndex.IndexState.End, 1, TextIndex.IndexState.Start)]
[TestCase(1, TextIndex.IndexState.Start, 0, TextIndex.IndexState.End)]
[TestCase(0, TextIndex.IndexState.Start, -1, TextIndex.IndexState.End)] // didn't care about negative value.
[TestCase(-1, TextIndex.IndexState.End, -1, TextIndex.IndexState.Start)] // didn't care about negative value.
public void TestGetPreviousIndex(int index, TextIndex.IndexState state, int expectedIndex, TextIndex.IndexState expectedState)
{
var textIndex = new TextIndex(index, state);

var expected = new TextIndex(expectedIndex, expectedState);
var actual = TextIndexUtils.GetPreviousIndex(textIndex);
Assert.AreEqual(expected, actual);
}

[TestCase(0, TextIndex.IndexState.Start, 0, TextIndex.IndexState.End)]
[TestCase(0, TextIndex.IndexState.End, 1, TextIndex.IndexState.Start)]
[TestCase(-1, TextIndex.IndexState.Start, -1, TextIndex.IndexState.End)] // didn't care about negative value.
[TestCase(-1, TextIndex.IndexState.End, 0, TextIndex.IndexState.Start)] // didn't care about negative value.
public void TestGetNextIndex(int index, TextIndex.IndexState state, int expectedIndex, TextIndex.IndexState expectedState)
{
var textIndex = new TextIndex(index, state);

var expected = new TextIndex(expectedIndex, expectedState);
var actual = TextIndexUtils.GetNextIndex(textIndex);
Assert.AreEqual(expected, actual);
}
}
}
56 changes: 53 additions & 3 deletions osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Layout;
using osu.Framework.Utils;
using osuTK;
using osuTK.Graphics;

Expand All @@ -20,6 +21,8 @@ public class KaraokeSpriteText : KaraokeSpriteText<LyricSpriteText>

public partial class KaraokeSpriteText<T> : CompositeDrawable, ISingleShaderBufferedDrawable, IHasRuby, IHasRomaji where T : LyricSpriteText, new()
{
internal const double INTERPOLATION_TIMING = 1;

private readonly MaskingContainer<T> leftLyricTextContainer;
private readonly T leftLyricText;

Expand Down Expand Up @@ -447,9 +450,7 @@ public virtual void RefreshStateTransforms()
rightLyricTextContainer.ClearTransforms();

// filter valid time-tag with order.
var validTimeTag = TimeTags
.Where(x => x.Value.Index >= 0 && x.Value.Index < Text.Length)
.OrderBy(x => x.Key).ToArray();
var validTimeTag = GetInterpolatedTimeTags();

// not initialize if no time-tag or text.
var hasTimeTag = validTimeTag.Any();
Expand Down Expand Up @@ -489,6 +490,55 @@ public virtual void RefreshStateTransforms()
}
}

internal IReadOnlyDictionary<double, TextIndex> GetInterpolatedTimeTags()
{
var orderedTimeTags = TimeTags
.Where(x => x.Value.Index >= 0 && x.Value.Index < Text.Length)
.OrderBy(x => x.Key).ToArray();

return orderedTimeTags.Aggregate(new Dictionary<double, TextIndex>(), (collections, lastTimeTag) =>
{
if (collections.Count == 0)
{
collections.Add(lastTimeTag.Key, lastTimeTag.Value);
return collections;
}

foreach (var (time, textIndex) in getInterpolatedTimeTagBetweenTwoTimeTag(collections.LastOrDefault(), lastTimeTag))
{
collections.Add(time, textIndex);
}

collections.Add(lastTimeTag.Key, lastTimeTag.Value);
return collections;
});

IEnumerable<KeyValuePair<double, TextIndex>> getInterpolatedTimeTagBetweenTwoTimeTag(KeyValuePair<double, TextIndex> firstTimeTag, KeyValuePair<double, TextIndex> secondTimeTag)
{
// we should not add the interpolation if timing is too small between two time-tags.
var firstTimeTagTime = firstTimeTag.Key;
var secondTimeTagTime = secondTimeTag.Key;
if (Math.Abs(firstTimeTagTime - secondTimeTagTime) <= INTERPOLATION_TIMING * 2)
yield break;

// there's no need to add the interpolation if index are the same.
if (firstTimeTag.Value.Index == secondTimeTag.Value.Index)
yield break;

var firstTimeTagIndex = firstTimeTag.Value;
var secondTimeTagIndex = secondTimeTag.Value;
var isLarger = firstTimeTag.Value < secondTimeTag.Value;

var firstInterpolatedTimeTagIndex = isLarger ? TextIndexUtils.GetNextIndex(firstTimeTagIndex) : TextIndexUtils.GetPreviousIndex(firstTimeTagIndex);
if (firstInterpolatedTimeTagIndex.Index != firstTimeTagIndex.Index)
yield return new KeyValuePair<double, TextIndex>(firstTimeTagTime + INTERPOLATION_TIMING, firstInterpolatedTimeTagIndex);

var secondInterpolatedTimeTag = isLarger ? TextIndexUtils.GetPreviousIndex(secondTimeTagIndex) : TextIndexUtils.GetNextIndex(secondTimeTagIndex);
if (secondInterpolatedTimeTag.Index != secondTimeTagIndex.Index)
yield return new KeyValuePair<double, TextIndex>(secondTimeTagTime - INTERPOLATION_TIMING, secondInterpolatedTimeTag);
}
}

private float getTextIndexPosition(TextIndex index)
{
var leftTextIndexPosition = leftLyricText.GetTextIndexPosition(index);
Expand Down
27 changes: 27 additions & 0 deletions osu.Framework.Font/Utils/TextIndexUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,32 @@ public static TextIndex Clamp(TextIndex value, int minValue, int maxValue)
{
return new TextIndex(Math.Clamp(value.Index, minValue, maxValue), value.State);
}

public static int ToStringIndex(TextIndex index)
{
if (index.State == TextIndex.IndexState.Start)
return index.Index;

return index.Index + 1;
}

public static TextIndex.IndexState ReverseState(TextIndex.IndexState state)
{
return state == TextIndex.IndexState.Start ? TextIndex.IndexState.End : TextIndex.IndexState.Start;
}

public static TextIndex GetPreviousIndex(TextIndex originIndex)
{
int previousIndex = ToStringIndex(originIndex) - 1;
var previousState = ReverseState(originIndex.State);
return new TextIndex(previousIndex, previousState);
}

public static TextIndex GetNextIndex(TextIndex originIndex)
{
int nextIndex = ToStringIndex(originIndex);
var nextState = ReverseState(originIndex.State);
return new TextIndex(nextIndex, nextState);
}
}
}
5 changes: 5 additions & 0 deletions osu.Framework.Font/osu.Framework.Font.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@
<ItemGroup>
<EmbeddedResource Include="Resources\**\*" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>osu.Framework.Font.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>