Support loading animations with BOM (#2167)

* Support loading animations with BOM

Fixes #2166

* New .NET version that just came out
This commit is contained in:
Matthew Leibowitz 2022-07-14 05:26:52 +02:00 коммит произвёл GitHub
Родитель 0f250519aa
Коммит 8f81be2691
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 7529 добавлений и 54 удалений

Просмотреть файл

@ -103,7 +103,7 @@ namespace SkiaSharp
if (stream == null)
throw new ArgumentNullException (nameof (stream));
if (stream.CanSeek) {
return Create (stream, stream.Length);
return Create (stream, stream.Length - stream.Position);
} else {
using var memory = new SKDynamicMemoryWStream ();
using (var managed = new SKManagedStream (stream)) {

Просмотреть файл

@ -12,6 +12,25 @@ namespace SkiaSharp
{
internal const float NearlyZero = 1.0f / (1 << 12);
internal static int GetPreambleSize (SKData data)
{
_ = data ?? throw new ArgumentNullException (nameof (data));
var buffer = data.AsSpan ();
var len = buffer.Length;
if (len >= 2 && buffer[0] == 0xfe && buffer[1] == 0xff)
return 2;
else if (len >= 3 && buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
return 3;
else if (len >= 3 && buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
return 3;
else if (len >= 4 && buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
return 4;
else
return 0;
}
internal static Span<byte> AsSpan (this IntPtr ptr, int size) =>
new Span<byte> ((void*)ptr, size);
@ -105,8 +124,7 @@ namespace SkiaSharp
// GetUnicodeStringLength
private static int GetUnicodeStringLength (SKTextEncoding encoding) =>
encoding switch
{
encoding switch {
SKTextEncoding.Utf8 => 1,
SKTextEncoding.Utf16 => 1,
SKTextEncoding.Utf32 => 2,
@ -116,8 +134,7 @@ namespace SkiaSharp
// GetCharacterByteSize
internal static int GetCharacterByteSize (this SKTextEncoding encoding) =>
encoding switch
{
encoding switch {
SKTextEncoding.Utf8 => 1,
SKTextEncoding.Utf16 => 2,
SKTextEncoding.Utf32 => 4,
@ -156,8 +173,7 @@ namespace SkiaSharp
}
public static byte[] GetEncodedText (ReadOnlySpan<char> text, SKTextEncoding encoding) =>
encoding switch
{
encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetBytes (text),
SKTextEncoding.Utf16 => Encoding.Unicode.GetBytes (text),
SKTextEncoding.Utf32 => Encoding.UTF32.GetBytes (text),
@ -177,8 +193,7 @@ namespace SkiaSharp
if (data == null)
throw new ArgumentNullException (nameof (data));
return encoding switch
{
return encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (data, index, count),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (data, index, count),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (data, index, count),
@ -197,8 +212,7 @@ namespace SkiaSharp
return string.Empty;
fixed (byte* bp = data) {
return encoding switch
{
return encoding switch {
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (bp, data.Length),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (bp, data.Length),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (bp, data.Length),

Просмотреть файл

@ -21,6 +21,8 @@ namespace SkiaSharp.Skottie
protected override void DisposeNative ()
=> SkottieApi.skottie_animation_delete (Handle);
// Parse
public static Animation? Parse (string json) =>
TryParse (json, out var animation)
? animation
@ -34,6 +36,8 @@ namespace SkiaSharp.Skottie
return animation != null;
}
// Create
public static Animation? Create (Stream stream) =>
TryCreate (stream, out var animation)
? animation
@ -56,8 +60,8 @@ namespace SkiaSharp.Skottie
{
_ = stream ?? throw new ArgumentNullException (nameof (stream));
animation = GetObject (SkottieApi.skottie_animation_make_from_stream (stream.Handle));
return animation != null;
using var data = SKData.Create (stream);
return TryCreate (data, out animation);
}
public static Animation? Create (SKData data) =>
@ -69,8 +73,13 @@ namespace SkiaSharp.Skottie
{
_ = data ?? throw new ArgumentNullException (nameof (data));
animation = GetObject (SkottieApi.skottie_animation_make_from_data ((void*)data.Data, (IntPtr)data.Size));
return animation != null;
var preamble = Utils.GetPreambleSize (data);
var span = data.AsSpan ().Slice (preamble);
fixed (byte* ptr = span) {
animation = GetObject (SkottieApi.skottie_animation_make_from_data (ptr, (IntPtr)span.Length));
return animation != null;
}
}
public static Animation? Create (string path) =>
@ -82,16 +91,20 @@ namespace SkiaSharp.Skottie
{
_ = path ?? throw new ArgumentNullException (nameof (path));
animation = GetObject (SkottieApi.skottie_animation_make_from_file (path));
return animation != null;
using var data = SKData.Create (path);
return TryCreate (data, out animation);
}
// Render
public unsafe void Render (SKCanvas canvas, SKRect dst)
=> SkottieApi.skottie_animation_render (Handle, canvas.Handle, &dst);
public void Render (SKCanvas canvas, SKRect dst, AnimationRenderFlags flags)
=> SkottieApi.skottie_animation_render_with_flags (Handle, canvas.Handle, &dst, flags);
// Seek*
public void Seek (double percent, InvalidationController? ic = null)
=> SkottieApi.skottie_animation_seek (Handle, (float)percent, ic?.Handle ?? IntPtr.Zero);
@ -104,6 +117,8 @@ namespace SkiaSharp.Skottie
public void SeekFrameTime (TimeSpan time, InvalidationController? ic = null)
=> SeekFrameTime (time.TotalSeconds, ic);
// Properties
public TimeSpan Duration
=> TimeSpan.FromSeconds (SkottieApi.skottie_animation_get_duration (Handle));

Просмотреть файл

@ -15,7 +15,7 @@ variables:
MONO_VERSION_LINUX: ''
XCODE_VERSION: 13.2.1
VISUAL_STUDIO_VERSION: '17/pre'
DOTNET_VERSION_PREVIEW: '6.0.301'
DOTNET_VERSION_PREVIEW: '6.0.302'
DOTNET_WORKLOAD_SOURCE: 'https://aka.ms/dotnet/maui/6.0.300.json'
CONFIGURATION: 'Release'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -179,6 +179,32 @@ namespace SkiaSharp.Tests
Assert.NotNull(data);
}
[SkippableFact]
public void CanCreateFromPartiallyReadStream()
{
using var stream = File.OpenRead(Path.Combine(PathToImages, "baboon.png"));
stream.Position = 10;
using var data = SKData.Create(stream);
Assert.NotNull(data);
Assert.Equal(stream.Length - 10, data.Size);
}
[SkippableFact]
public void CanCreateFromPartiallyReadNonSeekable()
{
using var stream = File.OpenRead(Path.Combine(PathToImages, "baboon.png"));
stream.Position = 10;
using var nonSeekable = new NonSeekableReadOnlyStream(stream);
using var data = SKData.Create(nonSeekable);
Assert.NotNull(data);
Assert.Equal(stream.Length - 10, data.Size);
}
[SkippableFact(Skip = "Doesn't work as it relies on memory being overwritten by an external process.")]
public void DataDisposedReturnsInvalidStream()
{

Просмотреть файл

@ -8,10 +8,29 @@ namespace SkiaSharp.Tests
{
public class AnimationTest : SKTest
{
[SkippableFact]
public void When_Default_TryParse()
public static TheoryData<string> DefaultLottieFiles =>
new TheoryData<string>
{
"LottieLogo1.json",
"LottieLogo1_bom.json",
};
[SkippableTheory]
[InlineData("LottieLogo1.json", 0)]
[InlineData("LottieLogo1_bom.json", 3)]
public void EnsureLottieHasCorrectPreamble(string filename, int preamble)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
var data = SKData.Create(path);
Assert.Equal(preamble, Utils.GetPreambleSize(data));
}
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryParse(string filename)
{
var path = Path.Combine(PathToImages, filename);
var result = Animation.TryParse(File.ReadAllText(path), out var animation);
Assert.True(result);
@ -19,20 +38,22 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_Parse()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Parse(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
var animation = Animation.Parse(File.ReadAllText(path));
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_SKData()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryCreate_From_SKData(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var data = SKData.Create(path);
var result = Animation.TryCreate(data, out var animation);
@ -41,10 +62,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_Create_From_SKData()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Create_From_SKData(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var data = SKData.Create(path);
var animation = Animation.Create(data);
@ -52,10 +74,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_SKStream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryCreate_From_SKStream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
using var managedStream = new SKManagedStream(fileStream);
var result = Animation.TryCreate(managedStream, out var animation);
@ -65,10 +88,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_Create_From_SKStream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Create_From_SKStream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
using var managedStream = new SKManagedStream(fileStream);
var animation = Animation.Create(managedStream);
@ -77,10 +101,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_Stream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryCreate_From_Stream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
var result = Animation.TryCreate(fileStream, out var animation);
@ -88,10 +113,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
}
[SkippableFact]
public void When_Default_Create_From_Stream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Create_From_Stream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
var animation = Animation.Create(fileStream);
@ -99,10 +125,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_NonSeekableStream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryCreate_From_NonSeekableStream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
using var nonseekable = new NonSeekableReadOnlyStream(fileStream);
var result = Animation.TryCreate(nonseekable, out var animation);
@ -111,10 +138,11 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
}
[SkippableFact]
public void When_Default_Create_From_NonSeekableStream()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Create_From_NonSeekableStream(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
using var fileStream = File.OpenRead(path);
using var nonseekable = new NonSeekableReadOnlyStream(fileStream);
var animation = Animation.Create(nonseekable);
@ -123,20 +151,22 @@ namespace SkiaSharp.Tests
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_File()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_TryCreate_From_File(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
var result = Animation.TryCreate(path, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
}
[SkippableFact]
public void When_Default_Create_From_File()
[SkippableTheory]
[MemberData(nameof(DefaultLottieFiles))]
public void When_Default_Create_From_File(string filename)
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var path = Path.Combine(PathToImages, filename);
var animation = Animation.Create(path);
Assert.NotNull(animation);