Add nullable attributes, TimeSpan and overloads to Skottie (#2119)

* Add nullable attibutes and overloads to Skottie

* Changed Duration to TimeSpan

* Update xample to use new APIs
This commit is contained in:
Matthew Leibowitz 2022-06-20 08:06:56 +02:00 коммит произвёл GitHub
Родитель 917a4b58f8
Коммит 297d6bfc5b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 171 добавлений и 125 удалений

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

@ -1,7 +1,6 @@
using System;
using System.ComponentModel;
#nullable enable
using System;
using System.IO;
using System.Runtime.InteropServices;
using SkiaSharp.SceneGraph;
namespace SkiaSharp.Skottie
@ -22,27 +21,54 @@ namespace SkiaSharp.Skottie
protected override void DisposeNative ()
=> SkottieApi.skottie_animation_delete (Handle);
public static bool TryParse (string data, out Animation animation)
public static Animation? Parse (string json) =>
TryParse (json, out var animation)
? animation
: null;
public static bool TryParse (string json, [System.Diagnostics.CodeAnalysis.NotNullWhen (true)] out Animation? animation)
{
animation = GetObject (SkottieApi.skottie_animation_make_from_string (data, data.Length));
_ = json ?? throw new ArgumentNullException (nameof (json));
animation = GetObject (SkottieApi.skottie_animation_make_from_string (json, json.Length));
return animation != null;
}
public static bool TryCreate (Stream stream, out Animation animation)
public static Animation? Create (Stream stream) =>
TryCreate (stream, out var animation)
? animation
: null;
public static bool TryCreate (Stream stream, [System.Diagnostics.CodeAnalysis.NotNullWhen (true)] out Animation? animation)
{
using (var managed = new SKManagedStream (stream)) {
return TryCreate (managed, out animation);
}
_ = stream ?? throw new ArgumentNullException (nameof (stream));
using var managed = new SKManagedStream (stream);
return TryCreate (managed, out animation);
}
public static bool TryCreate (SKStream stream, out Animation animation)
public static Animation? Create (SKStream stream) =>
TryCreate (stream, out var animation)
? animation
: null;
public static bool TryCreate (SKStream stream, [System.Diagnostics.CodeAnalysis.NotNullWhen (true)] out Animation? animation)
{
_ = stream ?? throw new ArgumentNullException (nameof (stream));
animation = GetObject (SkottieApi.skottie_animation_make_from_stream (stream.Handle));
return animation != null;
}
public static bool TryCreate (string path, out Animation animation)
public static Animation? Create (string path) =>
TryCreate (path, out var animation)
? animation
: null;
public static bool TryCreate (string path, [System.Diagnostics.CodeAnalysis.NotNullWhen (true)] out Animation? animation)
{
_ = path ?? throw new ArgumentNullException (nameof (path));
animation = GetObject (SkottieApi.skottie_animation_make_from_file (path));
return animation != null;
}
@ -53,17 +79,20 @@ namespace SkiaSharp.Skottie
public void Render (SKCanvas canvas, SKRect dst, AnimationRenderFlags flags)
=> SkottieApi.skottie_animation_render_with_flags (Handle, canvas.Handle, &dst, flags);
public void Seek (double t, InvalidationController ic = null)
=> SkottieApi.skottie_animation_seek (Handle, (float)t, ic?.Handle ?? IntPtr.Zero);
public void Seek (double percent, InvalidationController? ic = null)
=> SkottieApi.skottie_animation_seek (Handle, (float)percent, ic?.Handle ?? IntPtr.Zero);
public void SeekFrame (double t, InvalidationController ic = null)
=> SkottieApi.skottie_animation_seek_frame (Handle, (float)t, ic?.Handle ?? IntPtr.Zero);
public void SeekFrame (double frame, InvalidationController? ic = null)
=> SkottieApi.skottie_animation_seek_frame (Handle, (float)frame, ic?.Handle ?? IntPtr.Zero);
public void SeekFrameTime (double t, InvalidationController ic = null)
=> SkottieApi.skottie_animation_seek_frame_time (Handle, (float)t, ic?.Handle ?? IntPtr.Zero);
public void SeekFrameTime (double seconds, InvalidationController? ic = null)
=> SkottieApi.skottie_animation_seek_frame_time (Handle, (float)seconds, ic?.Handle ?? IntPtr.Zero);
public double Duration
=> SkottieApi.skottie_animation_get_duration (Handle);
public void SeekFrameTime (TimeSpan time, InvalidationController? ic = null)
=> SeekFrameTime (time.TotalSeconds, ic);
public TimeSpan Duration
=> TimeSpan.FromSeconds(SkottieApi.skottie_animation_get_duration (Handle));
public double Fps
=> SkottieApi.skottie_animation_get_fps (Handle);
@ -77,9 +106,7 @@ namespace SkiaSharp.Skottie
public string Version {
get {
using var str = new SKString ();
SkottieApi.skottie_animation_get_version (Handle, str.Handle);
return str.ToString ();
}
}
@ -92,7 +119,7 @@ namespace SkiaSharp.Skottie
}
}
internal static Animation GetObject (IntPtr handle) =>
internal static Animation? GetObject (IntPtr handle) =>
handle == IntPtr.Zero ? null : new Animation (handle, true);
}
}

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

@ -1,7 +1,4 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using SkiaSharp;
@ -12,24 +9,12 @@ namespace SkiaSharpSample.Samples
[Preserve(AllMembers = true)]
public class SkottieSample : AnimatedSampleBase
{
private readonly Animation _animation;
private Animation _animation;
private Stopwatch _watch = new Stopwatch();
[Preserve]
public SkottieSample()
{
using var fileStream = new SKManagedStream(SampleMedia.Images.LottieLogo);
if (SkiaSharp.Skottie.Animation.TryCreate(fileStream, out _animation))
{
_animation.Seek(0, null);
Console.WriteLine($"SkottieSample(): Version:{_animation.Version} Duration:{_animation.Duration} Fps:{_animation.Fps} InPoint:{_animation.InPoint} OutPoint:{_animation.OutPoint}");
}
else
{
Console.WriteLine($"SkottieSample(): failed to load animation");
}
}
public override string Title => "Skottie";
@ -38,47 +23,35 @@ namespace SkiaSharpSample.Samples
protected override async Task OnInit()
{
try
{
await base.OnInit();
_watch.Start();
}
catch (Exception e)
{
Console.WriteLine(e);
}
_animation = Animation.Create(SampleMedia.Images.LottieLogo);
_animation.Seek(0, null);
_watch.Start();
await base.OnInit();
}
protected override async Task OnUpdate(CancellationToken token)
{
try
{
await Task.Delay(25, token);
if (_animation == null)
return;
_animation.SeekFrameTime((float)_watch.Elapsed.TotalSeconds, null);
await Task.Delay(25, token);
if(_watch.Elapsed.TotalSeconds > _animation.Duration)
{
_watch.Restart();
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
_animation.SeekFrameTime(_watch.Elapsed);
if (_watch.Elapsed > _animation.Duration)
_watch.Restart();
}
protected override void OnDrawSample(SKCanvas canvas, int width, int height)
{
try
{
_animation.Render(canvas, new SKRect(0, 0, width, height));
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (_animation == null)
return;
canvas.Clear(SKColors.White);
_animation.Render(canvas, new SKRect(0, 0, width, height));
}
}
}

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

@ -1,93 +1,130 @@
using System;
using System.Collections.Generic;
using Xunit;
using System.IO;
using SkiaSharp.SceneGraph;
using SkiaSharp.Skottie;
using Xunit;
namespace SkiaSharp.Tests
{
public class AnimationTest : SKTest
{
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Default_Make()
public void When_Default_TryParse()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var result = Animation.TryParse(File.ReadAllText(path), out var animation);
var result = SkiaSharp.Skottie.Animation.TryParse(File.ReadAllText(path), out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Default_Make_From_SKStream()
public void When_Default_Parse()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var animation = Animation.Parse(File.ReadAllText(path));
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_SKStream()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
using var fileStream = File.OpenRead(path);
using var managedStream = new SKManagedStream(fileStream);
var result = SkiaSharp.Skottie.Animation.TryCreate(managedStream, out var animation);
var result = Animation.TryCreate(managedStream, out var animation);
Assert.True(result);
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_Create_From_SKStream()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
using var fileStream = File.OpenRead(path);
using var managedStream = new SKManagedStream(fileStream);
var animation = Animation.Create(managedStream);
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_Stream()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
using var fileStream = File.OpenRead(path);
var result = Animation.TryCreate(fileStream, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Default_Make_From_Stream()
public void When_Default_Create_From_Stream()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
using var fileStream = File.OpenRead(path);
var result = SkiaSharp.Skottie.Animation.TryCreate(fileStream, out var animation);
var animation = Animation.Create(fileStream);
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
[SkippableFact]
public void When_Default_TryCreate_From_File()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var result = Animation.TryCreate(path, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Seek_Without_Controller()
public void When_Default_Create_From_File()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var animation = Animation.Create(path);
using var fileStream = File.OpenRead(path);
var result = SkiaSharp.Skottie.Animation.TryCreate(fileStream, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
animation.Seek(.1);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Seek_With_Controller()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
using var fileStream = File.OpenRead(path);
var result = SkiaSharp.Skottie.Animation.TryCreate(fileStream, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
var controller = new InvalidationController();
animation.Seek(.1, controller);
Assert.NotNull(animation);
Assert.NotEqual(IntPtr.Zero, animation.Handle);
}
private Animation BuildDefaultAnimation()
{
var path = Path.Combine(PathToImages, "LottieLogo1.json");
var result = Animation.TryCreate(path, out var animation);
using var fileStream = File.OpenRead(path);
var result = SkiaSharp.Skottie.Animation.TryCreate(fileStream, out var animation);
Assert.True(result);
Assert.NotEqual(IntPtr.Zero, animation?.Handle);
return animation;
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Seek_Without_Controller()
{
var animation = BuildDefaultAnimation();
animation.Seek(.1);
}
[SkippableFact]
public void When_Seek_With_Controller()
{
var animation = BuildDefaultAnimation();
var controller = new InvalidationController();
animation.Seek(.1, controller);
}
[SkippableFact]
public void When_SeekFrame_Without_Controller()
{
@ -96,7 +133,6 @@ namespace SkiaSharp.Tests
animation.SeekFrame(.1);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_SeekFrame_With_Controller()
{
@ -107,7 +143,6 @@ namespace SkiaSharp.Tests
animation.SeekFrame(.1, controller);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_SeekFrameTime_Without_Controller()
{
@ -116,7 +151,6 @@ namespace SkiaSharp.Tests
animation.SeekFrameTime(.1);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_SeekFrameTime_With_Controller()
{
@ -127,58 +161,70 @@ namespace SkiaSharp.Tests
animation.SeekFrameTime(.1, controller);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_SeekFrameTimeSpan_Without_Controller()
{
var animation = BuildDefaultAnimation();
animation.SeekFrameTime(TimeSpan.FromSeconds(.1));
}
[SkippableFact]
public void When_SeekFrameTimeSpan_With_Controller()
{
var animation = BuildDefaultAnimation();
var controller = new InvalidationController();
animation.SeekFrameTime(TimeSpan.FromSeconds(.1), controller);
}
[SkippableFact]
public void When_Duration()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.Duration > 0);
Assert.Equal(TimeSpan.FromSeconds(5.9666666), animation.Duration);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Fps()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.Fps == 30);
Assert.Equal(30, animation.Fps);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_InPoint()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.InPoint == 0);
Assert.Equal(0, animation.InPoint);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_OutPoint()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.OutPoint > 0);
Assert.Equal(179, animation.OutPoint);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Version()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.Version.Length > 0);
Assert.Equal("4.4.26", animation.Version);
}
[Trait(CategoryKey, ApiCategory)]
[SkippableFact]
public void When_Size()
{
var animation = BuildDefaultAnimation();
Assert.True(animation.Size.Height > 0);
Assert.Equal(new SKSize(375, 667), animation.Size);
}
}
}