Replacing Newtonsoft with System.Text.Json. (#247)

This removes our dependency on Newtonsoft for JSON parsing. We'll now use the newer System.Text.Json reader.
This commit is contained in:
Simeon 2020-02-21 18:12:21 -08:00 коммит произвёл GitHub
Родитель 47790ee0d3
Коммит 76565f1182
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
23 изменённых файлов: 565 добавлений и 696 удалений

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

@ -8,8 +8,6 @@
// a specific target and scoped to a namespace, type, member, etc.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "IAsyncAction is awaitable. Rule doesn't apply", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieVisualSource.SetSourceAsync(System.Uri)~Windows.Foundation.IAsyncAction")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "IAsyncAction is awaitable. Rule doesn't apply", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieVisualSource.SetSourceAsync(Windows.Storage.StorageFile)~Windows.Foundation.IAsyncAction")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment must be preceded by blank line", Justification = "Adding empty lines in the middle of expressions doesn't seem right", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization.LottieCompositionReader.AnimatableGradientStopsParser.ReadValue(Newtonsoft.Json.Linq.JToken)~Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.GradientStop}")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment must be preceded by blank line", Justification = "Not valid in a parameter list", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization.LottieCompositionReader.AnimatableGradientStopsParser.ReadValue(Newtonsoft.Json.Linq.JToken)~Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.GradientStop}")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment must be preceded by blank line", Justification = "Not valid in a parameter list", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp.LottieToWinCompTranslator.ApplyPathKeyFrameAnimation(Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp.LottieToWinCompTranslator.TranslationContext,Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Animatable{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.BezierSegment}},Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.ShapeFill.PathFillType,Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.CompositionObject,System.String,System.String,System.String)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment must be preceded by blank line", Justification = "Space in the middle of a parameter list seems wrong", Scope = "member", Target = "~M:Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp.LottieToWinCompTranslator.ApplyPathKeyFrameAnimation(Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp.LottieToWinCompTranslator.TranslationContext,Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Animatable{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence{Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.BezierSegment}},Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.ShapeFill.PathFillType,Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.CompositionObject,System.String,System.String,System.String)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment must be preceded by blank line", Justification = "This is a space in the middle of an expression", Scope = "member", Target = "~M:Program.TryGenerateCode(System.String,System.String,System.String,System.Boolean,Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Tools.Stats@,Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools.Stats@,Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools.Stats@)~System.Boolean")]

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

@ -12,11 +12,6 @@
<!-- Enable the latest C# language features -->
<LangVersion>latest</LangVersion>
<!--
Turn off warning about experimental types in Microsoft.UI.Xaml. This can be removed
once AnimatedVisualPlayer is promoted out of prerelease. -->
<NoWarn>8305</NoWarn>
<!--
Turn off debugging information for now. It is causing errors with winmd generation because
the build system is creating Microsoft.Toolkit.Uwp.UI.Lottie.compile.pdb but the winmdexp
@ -28,16 +23,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.3.200213001</Version>
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Numerics.Vectors">
<Version>4.5.0</Version>
@ -64,4 +55,4 @@
<Import Project="..\source\WinUIXamlMediaData\WinUIXamlMediaData.projitems" Label="Shared" />
<Import Project="..\source\YamlData\YamlData.projitems" Label="Shared" />
</Project>
</Project>

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

@ -23,7 +23,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />

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

@ -226,9 +226,6 @@
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.3.200213001</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
<PackageReference Include="System.Memory">
<Version>4.5.3</Version>
</PackageReference>

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

@ -10,7 +10,6 @@
<Import Project="..\..\source\LottieReader\LottieReader.projitems" Label="Shared" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="System.Text.Json" Version="4.7.1" />
</ItemGroup>

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

@ -263,7 +263,7 @@
<Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.2.190917002</Version>
<Version>2.3.200213001</Version>
</PackageReference>
</ItemGroup>
<Target Name="Pack">

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

@ -13,11 +13,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Enums.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Exceptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Fonts.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JsonToGenericData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Layers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieCompositionReader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieCompositionReaderException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieJsonArrayElement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieJsonDocument.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieJsonElement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieJsonObjectElement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Markers.cs" />

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

@ -26,6 +26,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
static readonly Animatable<Sequence<GradientStop>> s_animatableGradientStopsSingle = CreateNonAnimatedAnimatable(s_defaultGradientStops);
static readonly Animatable<Trim> s_animatableTrimNone = CreateNonAnimatedAnimatable(Trim.None);
static readonly AnimatableVector3 s_animatableVector3Zero = new AnimatableVector3(Vector3.Zero, null);
static readonly AnimatableVector3 s_animatableVector3OneHundred = new AnimatableVector3(new Vector3(100, 100, 100), null);
static readonly AnimatableParser<double> s_animatableFloatParser =
CreateAnimatableParser((in LottieJsonElement element) => element.AsDouble() ?? 0);
@ -54,7 +55,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
where T : IEquatable<T>
{
parser.ParseJson(this, obj, out var keyFrames, out var initialValue);
return CreateAnimatable(initialValue, keyFrames, obj.Int32OrNullProperty("ix"));
return CreateAnimatable(initialValue, keyFrames, obj.Int32PropertyOrNull("ix"));
}
Animatable<Color> ReadAnimatableColor(in LottieJsonObjectElement? obj)
@ -80,7 +81,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
Animatable<Sequence<GradientStop>> ReadAnimatableGradientStops(in LottieJsonObjectElement obj)
{
// If the "k" doesn't exist, we can't parse.
var kObj = obj.ObjectOrNullProperty("k");
var kObj = obj.ObjectPropertyOrNull("k");
if (kObj is null)
{
@ -91,8 +92,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Get the number of color stops. This is optional unless there are opacity stops.
// If this value doesn't exist, all the stops are color stops. If the value exists
// then this is the number of color stops, and the remaining stops are opacity stops.
var numberOfColorStops = obj.Int32OrNullProperty("p");
var propertyIndex = obj.Int32OrNullProperty("ix");
var numberOfColorStops = obj.Int32PropertyOrNull("p");
var propertyIndex = obj.Int32PropertyOrNull("ix");
return ReadAnimatableGradientStops(kObj.Value, numberOfColorStops, propertyIndex);
}
}
@ -202,11 +203,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
? s_animatableTrimNone
: ReadAnimatable(s_animatableTrimParser, obj.Value);
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement? obj)
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement? obj, IAnimatableVector3 defaultValue)
=> obj is null
? s_animatableVector3Zero
? defaultValue
: ReadAnimatableVector3(obj.Value);
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement? obj)
=> ReadAnimatableVector3(obj, s_animatableVector3Zero);
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement obj)
{
obj.IgnorePropertyThatIsNotYetSupported("s");
@ -214,7 +218,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Expressions not supported.
obj.IgnorePropertyThatIsNotYetSupported("x");
var propertyIndex = obj.Int32OrNullProperty("ix");
var propertyIndex = obj.Int32PropertyOrNull("ix");
if (obj.ContainsProperty("k"))
{
s_animatableVector3Parser.ParseJson(this, obj, out var keyFrames, out var initialValue);
@ -227,8 +231,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
else
{
// Split X and Y dimensions
var x = ReadAnimatableFloat(obj.ObjectOrNullProperty("x"));
var y = ReadAnimatableFloat(obj.ObjectOrNullProperty("y"));
var x = ReadAnimatableFloat(obj.ObjectPropertyOrNull("x"));
var y = ReadAnimatableFloat(obj.ObjectPropertyOrNull("y"));
obj.AssertAllPropertiesRead();
return new AnimatableXYZ(x, y, s_animatableDoubleZero);
@ -280,7 +284,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
goto AllColorChannelsRead;
}
throw UnexpectedTokenException(jsonValue.Kind);
throw Exception($"Unexpected {jsonValue.Kind}");
}
var number = jsonValue.AsDouble() ?? 0;
@ -572,7 +576,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
var lottieKeyFrame = jsonArray[i].AsObject();
if (lottieKeyFrame is null)
{
throw UnexpectedTokenException(jsonArray[i].Kind);
throw Exception($"Unexpected {jsonArray[i].Kind}");
}
var lottieKeyFrameObj = lottieKeyFrame.Value;
@ -581,7 +585,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
lottieKeyFrameObj.IgnorePropertyIntentionally("n");
// Read the start frame.
var startFrame = lottieKeyFrameObj.DoubleOrNullProperty("t") ?? 0;
var startFrame = lottieKeyFrameObj.DoublePropertyOrNull("t") ?? 0;
if (i == count - 1)
{
@ -614,12 +618,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Spatial control points.
if (lottieKeyFrameObj.ContainsProperty("ti"))
{
ti = lottieKeyFrameObj.AsArrayProperty("ti")?.AsVector3() ?? Vector3.Zero;
to = lottieKeyFrameObj.AsArrayProperty("to")?.AsVector3() ?? Vector3.Zero;
ti = lottieKeyFrameObj.ArrayPropertyOrNull("ti")?.AsVector3() ?? Vector3.Zero;
to = lottieKeyFrameObj.ArrayPropertyOrNull("to")?.AsVector3() ?? Vector3.Zero;
}
// Get the easing to the end value, and get the end value.
if (lottieKeyFrameObj.BoolOrNullProperty("h") == true)
if (lottieKeyFrameObj.BoolPropertyOrNull("h") == true)
{
// Hold the current value. The next value comes from the start
// of the next entry.
@ -631,8 +635,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
else
{
// Read the easing function parameters. If there are any parameters, it's a CubicBezierEasing.
var cp1Json = lottieKeyFrameObj.ObjectOrNullProperty("o");
var cp2Json = lottieKeyFrameObj.ObjectOrNullProperty("i");
var cp1Json = lottieKeyFrameObj.ObjectPropertyOrNull("o");
var cp2Json = lottieKeyFrameObj.ObjectPropertyOrNull("i");
if (cp1Json != null && cp2Json != null)
{
var cp1 = cp1Json.Value.AsVector3();

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

@ -14,7 +14,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
Asset ParseAsset(ref Reader reader)
{
ExpectToken(ref reader, JsonTokenType.StartObject);
reader.ExpectToken(JsonTokenType.StartObject);
int e = 0;
string id = null;
@ -31,7 +31,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case JsonTokenType.PropertyName:
{
var currentProperty = reader.GetString();
ConsumeToken(ref reader);
reader.ConsumeToken();
switch (currentProperty)
{
@ -53,7 +53,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
id = reader.ParseInt().ToString();
break;
default:
throw UnexpectedTokenException(ref reader);
throw reader.ThrowUnexpectedToken();
}
break;
@ -70,7 +70,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
width = reader.ParseDouble();
break;
// Report but ignore unexpected fields.
// Report but ignore unexpected properties.
case "xt":
case "nm":
default:
@ -85,7 +85,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
if (id is null)
{
throw Exception("Asset with no id", ref reader);
throw reader.Throw("Asset with no id");
}
if (layers is object)
@ -103,7 +103,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
}
default: throw UnexpectedTokenException(ref reader);
default: throw reader.ThrowUnexpectedToken();
}
}

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

@ -13,14 +13,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
Char ParseChar(ref Reader reader)
{
ExpectToken(ref reader, JsonTokenType.StartObject);
reader.ExpectToken(JsonTokenType.StartObject);
string ch = null;
string fFamily = null;
double? size = null;
string style = null;
double? width = null;
List<ShapeLayerContent> shapes = null;
ShapeLayerContent[] shapes = null;
while (reader.Read())
{
@ -29,7 +29,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case JsonTokenType.PropertyName:
{
var currentProperty = reader.GetString();
ConsumeToken(ref reader);
reader.ConsumeToken();
switch (currentProperty)
{
@ -37,7 +37,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
ch = reader.GetString();
break;
case "data":
shapes = ReadShapes(LottieJsonObjectElement.Load(this, ref reader, s_jsonLoadSettings));
shapes = ReadShapes(ref reader);
break;
case "fFamily":
fFamily = reader.GetString();
@ -63,7 +63,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
return new Char(ch, fFamily, style, size ?? 0, width ?? 0, shapes);
}
default: throw UnexpectedTokenException(ref reader);
default: throw reader.ThrowUnexpectedToken();
}
}

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

@ -1,90 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// If defined, an issue will be reported for each field that is discovered
// but not parsed. This is used to help test that parsing is complete.
#define CheckForUnparsedFields
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
#if CheckForUnparsedFields
using JArray = Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization.CheckedJsonArray;
using JObject = Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization.CheckedJsonObject;
#endif
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
#if CheckForUnparsedFields
sealed class CheckedJsonArray : IList<JToken>
{
internal Newtonsoft.Json.Linq.JArray Wrapped { get; }
internal CheckedJsonArray(Newtonsoft.Json.Linq.JArray wrapped)
{
Wrapped = wrapped;
}
internal static CheckedJsonArray Load(JsonReader reader, JsonLoadSettings settings)
{
return new CheckedJsonArray(Newtonsoft.Json.Linq.JArray.Load(reader, settings));
}
public JToken this[int index] { get => Wrapped[index]; set => throw new NotImplementedException(); }
public int Count => Wrapped.Count;
public bool IsReadOnly => throw new NotImplementedException();
public void Add(JToken item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(JToken item)
{
throw new NotImplementedException();
}
public void CopyTo(JToken[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public IEnumerator<JToken> GetEnumerator()
{
foreach (var value in Wrapped)
{
yield return value;
}
}
public int IndexOf(JToken item) => throw new NotImplementedException();
public void Insert(int index, JToken item) => throw new NotImplementedException();
public bool Remove(JToken item) => throw new NotImplementedException();
public void RemoveAt(int index) => throw new NotImplementedException();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public static implicit operator CheckedJsonArray(Newtonsoft.Json.Linq.JArray value)
{
return value is null ? null : new CheckedJsonArray(value);
}
}
#endif
}

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

@ -1,63 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// If defined, an issue will be reported for each field that is discovered
// but not parsed. This is used to help test that parsing is complete.
#define CheckForUnparsedFields
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
#if CheckForUnparsedFields
sealed class JCObject : IEnumerable<KeyValuePair<string, JToken>>
{
internal Newtonsoft.Json.Linq.JObject Wrapped { get; }
internal HashSet<string> ReadFields { get; } = new HashSet<string>();
internal JCObject(Newtonsoft.Json.Linq.JObject wrapped)
{
Wrapped = wrapped;
}
internal static JCObject Parse(string input, JsonLoadSettings loadSettings) => new JCObject(Newtonsoft.Json.Linq.JObject.Parse(input, loadSettings));
internal bool ContainsKey(string key)
{
ReadFields.Add(key);
return Wrapped.ContainsKey(key);
}
internal bool TryGetValue(string propertyName, out JToken value)
{
ReadFields.Add(propertyName);
return Wrapped.TryGetValue(propertyName, out value);
}
internal static JCObject Load(JsonReader reader, JsonLoadSettings settings)
{
return new JCObject(Newtonsoft.Json.Linq.JObject.Load(reader, settings));
}
public IEnumerator<KeyValuePair<string, JToken>> GetEnumerator()
{
return Wrapped.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Wrapped.GetEnumerator();
}
public static implicit operator JCObject(Newtonsoft.Json.Linq.JObject value)
{
return value is null ? null : new JCObject(value);
}
}
#endif
}

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

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
@ -12,12 +13,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
sealed partial class LottieCompositionReader
{
Font[] ParseFonts(ref Reader reader)
{
using var subDocument = reader.ParseElement();
var fontsObject = subDocument.RootElement.AsObject();
return fontsObject.HasValue
? ParseFonts(fontsObject.Value)
: Array.Empty<Font>();
}
Font[] ParseFonts(in LottieJsonObjectElement fontsObject)
{
IList<Font> list = EmptyList<Font>.Singleton;
var fontsObject = LottieJsonObjectElement.Load(this, ref reader, s_jsonLoadSettings);
foreach (var item in fontsObject.AsArrayProperty("list"))
foreach (var item in fontsObject.ArrayPropertyOrNull("list"))
{
var element = item.AsObject();
if (!element.HasValue)
@ -26,11 +36,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
var obj = element.Value;
var fName = obj.StringOrNullProperty("fName") ?? string.Empty;
var fFamily = obj.StringOrNullProperty("fFamily") ?? string.Empty;
var fStyle = obj.StringOrNullProperty("fStyle") ?? string.Empty;
var ascent = obj.DoubleOrNullProperty("ascent") ?? 0;
var fName = obj.StringPropertyOrNull("fName") ?? string.Empty;
var fFamily = obj.StringPropertyOrNull("fFamily") ?? string.Empty;
var fStyle = obj.StringPropertyOrNull("fStyle") ?? string.Empty;
var ascent = obj.DoublePropertyOrNull("ascent") ?? 0;
obj.AssertAllPropertiesRead();
if (list == EmptyList<Font>.Singleton)
{
list = new List<Font>();

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

@ -1,58 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Toolkit.Uwp.UI.Lottie.GenericData;
using Newtonsoft.Json.Linq;
using static Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization.Exceptions;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
/// <summary>
/// Reads the contents of the given <see cref="JToken"/> into a <see cref="GenericDataObject"/>.
/// </summary>
static class JsonToGenericData
{
internal static GenericDataObject JTokenToGenericData(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
var jobject = (IEnumerable<KeyValuePair<string, JToken>>)token;
return jobject.ToDictionary(field => field.Key, field => JTokenToGenericData(field.Value));
case JTokenType.Array:
return GenericDataList.Create(((JArray)token).Select(item => JTokenToGenericData(item)));
case JTokenType.Integer:
return (long)token;
case JTokenType.Float:
return (double)token;
case JTokenType.String:
return (string)token;
case JTokenType.Boolean:
return (bool)token;
case JTokenType.Null:
return null;
// Currently unsupported types.
case JTokenType.None:
case JTokenType.Constructor:
case JTokenType.Property:
case JTokenType.Comment:
case JTokenType.Undefined:
case JTokenType.Date:
case JTokenType.Raw:
case JTokenType.Bytes:
case JTokenType.Guid:
case JTokenType.Uri:
case JTokenType.TimeSpan:
throw new InvalidOperationException($"Unsupported JSON token type: {token.Type}");
default:
throw Unreachable;
}
}
}
}

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

@ -12,7 +12,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
#pragma warning disable SA1601 // Partial elements should be documented
sealed partial class LottieCompositionReader
{
Layer ParseLayer(ref Reader reader) => ReadLayer(LottieJsonObjectElement.Load(this, ref reader, s_jsonLoadSettings));
// May return null if there was a problem reading the layer.
Layer ParseLayer(ref Reader reader)
{
using var subDocument = reader.ParseElement();
var obj = subDocument.RootElement.AsObject();
return obj.HasValue
? ReadLayer(obj.Value)
: null;
}
// May return null if there was a problem reading the layer.
Layer ReadLayer(in LottieJsonObjectElement obj)
@ -26,7 +36,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
var layerArgs = default(Layer.LayerArgs);
layerArgs.Name = ReadName(obj);
var index = obj.Int32OrNullProperty("ind");
var index = obj.Int32PropertyOrNull("ind");
if (!index.HasValue)
{
@ -34,12 +44,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
layerArgs.Index = index.Value;
layerArgs.Parent = obj.Int32OrNullProperty("parent");
layerArgs.Is3d = obj.BoolOrNullProperty("ddd") == true;
layerArgs.AutoOrient = obj.BoolOrNullProperty("ao") == true;
layerArgs.BlendMode = BmToBlendMode(obj.DoubleOrNullProperty("bm"));
layerArgs.IsHidden = obj.BoolOrNullProperty("hd") == true;
var render = obj.BoolOrNullProperty("render") != false;
layerArgs.Parent = obj.Int32PropertyOrNull("parent");
layerArgs.Is3d = obj.BoolPropertyOrNull("ddd") == true;
layerArgs.AutoOrient = obj.BoolPropertyOrNull("ao") == true;
layerArgs.BlendMode = BmToBlendMode(obj.DoublePropertyOrNull("bm"));
layerArgs.IsHidden = obj.BoolPropertyOrNull("hd") == true;
var render = obj.BoolPropertyOrNull("render") != false;
if (!render)
{
@ -48,7 +58,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
// Warnings
if (layerArgs.Name.EndsWith(".ai") || obj.StringOrNullProperty("cl") == "ai")
if (layerArgs.Name.EndsWith(".ai") || obj.StringPropertyOrNull("cl") == "ai")
{
_issues.IllustratorLayers();
}
@ -63,28 +73,28 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// ----------------------
var shapeLayerContentArgs = default(ShapeLayerContent.ShapeLayerContentArgs);
ReadShapeLayerContentArgs(obj, ref shapeLayerContentArgs);
layerArgs.Transform = ReadTransform(obj.ObjectOrNullProperty("ks"), in shapeLayerContentArgs);
layerArgs.Transform = ReadTransform(obj.ObjectPropertyOrNull("ks"), in shapeLayerContentArgs);
// ------------------------------
// Layer Animation
// ------------------------------
layerArgs.TimeStretch = obj.DoubleOrNullProperty("sr") ?? 1;
layerArgs.TimeStretch = obj.DoublePropertyOrNull("sr") ?? 1;
// Time when the layer starts
layerArgs.StartFrame = obj.DoubleOrNullProperty( "st") ?? double.NaN;
layerArgs.StartFrame = obj.DoublePropertyOrNull("st") ?? double.NaN;
// Time when the layer becomes visible.
layerArgs.InFrame = obj.DoubleOrNullProperty("ip") ?? double.NaN;
layerArgs.OutFrame = obj.DoubleOrNullProperty( "op") ?? double.NaN;
layerArgs.InFrame = obj.DoublePropertyOrNull("ip") ?? double.NaN;
layerArgs.OutFrame = obj.DoublePropertyOrNull("op") ?? double.NaN;
// NOTE: The spec specifies this as 'maskProperties' but the BodyMovin tool exports
// 'masksProperties' with the plural 'masks'.
var maskProperties = obj.AsArrayProperty("masksProperties");
var maskProperties = obj.ArrayPropertyOrNull("masksProperties");
layerArgs.Masks = maskProperties != null ? ReadMaskProperties(maskProperties.Value) : null;
layerArgs.LayerMatteType = TTToMatteType(obj.DoubleOrNullProperty( "tt"));
layerArgs.LayerMatteType = TTToMatteType(obj.DoublePropertyOrNull("tt"));
Layer.LayerType? layerType = TyToLayerType(obj.DoubleOrNullProperty( "ty"));
Layer.LayerType? layerType = TyToLayerType(obj.DoublePropertyOrNull("ty"));
if (!layerType.HasValue)
{
@ -97,10 +107,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
case Layer.LayerType.PreComp:
{
var refId = obj.StringOrNullProperty("refId") ?? string.Empty;
var width = obj.DoubleOrNullProperty("w") ?? double.NaN;
var height = obj.DoubleOrNullProperty("h") ?? double.NaN;
var tm = obj.ObjectOrNullProperty("tm");
var refId = obj.StringPropertyOrNull("refId") ?? string.Empty;
var width = obj.DoublePropertyOrNull("w") ?? double.NaN;
var height = obj.DoublePropertyOrNull("h") ?? double.NaN;
var tm = obj.ObjectPropertyOrNull("tm");
if (tm != null)
{
_issues.TimeRemappingOfPreComps();
@ -111,15 +121,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case Layer.LayerType.Solid:
{
var solidWidth = obj.Int32OrNullProperty("sw") ?? 0;
var solidHeight = obj.Int32OrNullProperty("sh") ?? 0;
var solidColor = ReadColorFromString(obj.StringOrNullProperty("sc") ?? string.Empty);
var solidWidth = obj.Int32PropertyOrNull("sw") ?? 0;
var solidHeight = obj.Int32PropertyOrNull("sh") ?? 0;
var solidColor = ReadColorFromString(obj.StringPropertyOrNull("sc") ?? string.Empty);
return new SolidLayer(in layerArgs, solidWidth, solidHeight, solidColor);
}
case Layer.LayerType.Image:
{
var refId = obj.StringOrNullProperty("refId") ?? string.Empty;
var refId = obj.StringPropertyOrNull("refId") ?? string.Empty;
return new ImageLayer(in layerArgs, refId);
}
@ -135,10 +145,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case Layer.LayerType.Text:
{
// Text layer references an asset.
var refId = obj.StringOrNullProperty("refId") ?? string.Empty;
var refId = obj.StringPropertyOrNull("refId") ?? string.Empty;
// Text data.
ReadTextData(obj.ObjectOrNullProperty("t").Value);
ReadTextData(obj.ObjectPropertyOrNull("t").Value);
return new TextLayer(in layerArgs, refId);
}
@ -151,12 +161,22 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
}
List<ShapeLayerContent> ReadShapes(in LottieJsonObjectElement obj)
ShapeLayerContent[] ReadShapes(ref Reader reader)
{
return ReadShapesList(obj.AsArrayProperty("shapes"));
using var subDocument = reader.ParseElement();
var obj = subDocument.RootElement.AsObject();
return obj.HasValue
? ReadShapes(obj.Value)
: Array.Empty<ShapeLayerContent>();
}
List<ShapeLayerContent> ReadShapesList(in LottieJsonArrayElement? shapesJson)
ShapeLayerContent[] ReadShapes(in LottieJsonObjectElement obj)
{
return ReadShapesList(obj.ArrayPropertyOrNull("shapes"));
}
ShapeLayerContent[] ReadShapesList(in LottieJsonArrayElement? shapesJson)
{
var shapes = new List<ShapeLayerContent>();
if (shapesJson != null)
@ -173,7 +193,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
}
return shapes;
return shapes.ToArray();
}
void ReadTextData(in LottieJsonObjectElement obj)
@ -210,11 +230,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// expand or shrink a mask getting a reduced or expanded version of the same shape.
obj.IgnorePropertyThatIsNotYetSupported("x");
var inverted = obj.BoolOrNullProperty("inv") ?? false;
var inverted = obj.BoolPropertyOrNull("inv") ?? false;
var name = ReadName(obj);
var animatedGeometry = ReadAnimatableGeometry(obj.ObjectOrNullProperty("pt"));
var opacity = ReadOpacityFromO(obj);
var maskMode = obj.StringOrNullProperty("mode") ?? string.Empty;
var animatedGeometry = ReadAnimatableGeometry(obj.ObjectPropertyOrNull("pt"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var maskMode = obj.StringPropertyOrNull("mode") ?? string.Empty;
Mask.MaskMode mode;
switch (maskMode)

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

@ -6,10 +6,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using Microsoft.Toolkit.Uwp.UI.Lottie.GenericData;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PathGeometry = Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence<Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.BezierSegment>;
@ -24,15 +23,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
#pragma warning disable SA1601 // Partial elements should be documented
sealed partial class LottieCompositionReader
{
static readonly JsonLoadSettings s_jsonLoadSettings = new JsonLoadSettings
{
// Ignore commands and line info. Not needed and makes the parser a bit faster.
CommentHandling = CommentHandling.Ignore,
LineInfoHandling = LineInfoHandling.Ignore,
};
readonly ParsingIssues _issues = new ParsingIssues(throwOnIssue: false);
Options _options;
readonly ParsingIssues _issues;
readonly Options _options;
/// <summary>
/// Specifies optional behavior for the reader.
@ -74,45 +66,30 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
Options options,
out IReadOnlyList<(string Code, string Description)> issues)
{
JsonReader jsonReader;
try
{
var streamReader = new StreamReader(stream);
jsonReader = new JsonTextReader(streamReader);
}
catch (Exception e)
{
var issueCollector = new ParsingIssues(throwOnIssue: false);
issueCollector.FailedToParseJson(e.Message);
issues = issueCollector.GetIssues();
return null;
}
var reader = new Reader(jsonReader);
return ReadLottieCompositionFromJson(ref reader, options, out issues);
ReadStreamToUTF8(stream, out var utf8Text);
return ReadLottieCompositionFromJson(utf8Text, options, out issues);
}
LottieCompositionReader(Options options)
{
_issues = new ParsingIssues(throwOnIssue: false);
_options = options;
_animatableColorParser = new AnimatableColorParser(!options.HasFlag(Options.DoNotIgnoreAlpha));
}
static LottieComposition ReadLottieCompositionFromJson(
ref Reader jsonReader,
in ReadOnlySpan<byte> utf8JsonText,
Options options,
out IReadOnlyList<(string Code, string Description)> issues)
{
var reader = new LottieCompositionReader(options);
var jsonReader = new Reader(reader, utf8JsonText);
LottieComposition result = null;
try
{
result = reader.ParseLottieComposition(ref jsonReader);
}
catch (JsonReaderException e)
{
reader._issues.FatalError(e.Message);
}
catch (LottieCompositionReaderException e)
{
reader._issues.FatalError(e.Message);
@ -139,7 +116,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
var markers = Array.Empty<Marker>();
Dictionary<string, GenericDataObject> extraData = null;
ConsumeToken(ref reader);
reader.ConsumeToken();
while (reader.Read())
{
@ -147,13 +124,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
case JsonTokenType.Comment:
// Ignore comments.
ConsumeToken(ref reader);
reader.ConsumeToken();
break;
case JsonTokenType.PropertyName:
var currentProperty = reader.GetString();
ConsumeToken(ref reader);
reader.ConsumeToken();
switch (currentProperty)
{
@ -199,13 +176,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Treat any other property as an extension of the BodyMovin format.
default:
_issues.UnexpectedField(currentProperty);
if (extraData is null)
{
extraData = new Dictionary<string, GenericDataObject>();
_issues.UnexpectedField(currentProperty);
if (extraData is null)
{
extraData = new Dictionary<string, GenericDataObject>();
}
using var subDocument = reader.ParseElement();
extraData.Add(currentProperty, subDocument.RootElement.ToGenericDataObject());
}
extraData.Add(currentProperty, JsonToGenericData.JTokenToGenericData(JToken.Load(reader.NewtonsoftReader, s_jsonLoadSettings)));
break;
}
@ -216,32 +197,32 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Check that the required properties were found. If any are missing, throw.
if (version is null)
{
throw Exception("Version parameter not found.", ref reader);
throw reader.Throw("Version parameter not found.");
}
if (!width.HasValue)
{
throw Exception("Width parameter not found.", ref reader);
throw reader.Throw("Width parameter not found.");
}
if (!height.HasValue)
{
throw Exception("Height parameter not found.", ref reader);
throw reader.Throw("Height parameter not found.");
}
if (!inPoint.HasValue)
{
throw Exception("Start frame parameter not found.", ref reader);
throw reader.Throw("Start frame parameter not found.");
}
if (!outPoint.HasValue)
{
Exception("End frame parameter not found.", ref reader);
throw reader.Throw("End frame parameter not found.");
}
if (layers is null)
{
throw Exception("No layers found.", ref reader);
throw reader.Throw("No layers found.");
}
int[] versions;
@ -281,7 +262,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Here means the JSON was invalid or our parser got confused. There is no way to
// recover from this, so throw.
default:
throw UnexpectedTokenException(ref reader);
throw reader.ThrowUnexpectedToken();
}
}
@ -297,7 +278,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
else
{
return obj.StringOrNullProperty("nm") ?? string.Empty;
return obj.StringPropertyOrNull("nm") ?? string.Empty;
}
}
@ -310,21 +291,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
else
{
return obj.StringOrNullProperty("mn") ?? string.Empty;
return obj.StringPropertyOrNull("mn") ?? string.Empty;
}
}
Animatable<Color> ReadColorFromC(in LottieJsonObjectElement obj)
=> ReadAnimatableColor(obj.ObjectOrNullProperty("c"));
Animatable<Opacity> ReadOpacityFromO(in LottieJsonObjectElement obj)
=> ReadOpacityFromObject(obj.ObjectOrNullProperty("o"));
Animatable<Opacity> ReadOpacityFromObject(in LottieJsonObjectElement? obj)
{
return ReadAnimatableOpacity(obj);
}
static PathGeometry ParseGeometry(in LottieJsonElement element)
{
LottieJsonObjectElement? pointsData = null;
@ -349,10 +319,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
var points = pointsData.Value;
var vertices = points.AsArrayProperty("v");
var inTangents = points.AsArrayProperty("i");
var outTangents = points.AsArrayProperty("o");
var isClosed = points.BoolOrNullProperty("c") ?? false;
var vertices = points.ArrayPropertyOrNull("v");
var inTangents = points.ArrayPropertyOrNull("i");
var outTangents = points.ArrayPropertyOrNull("o");
var isClosed = points.BoolPropertyOrNull("c") ?? false;
if (vertices is null || inTangents is null || outTangents is null)
{
@ -433,19 +403,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
}
static void ExpectToken(ref Reader reader, JsonTokenType token)
{
if (reader.TokenType != token)
{
throw UnexpectedTokenException(ref reader);
}
}
delegate T Parser<T>(ref Reader reader);
T[] ParseArray<T>(ref Reader reader, Parser<T> parser)
{
ExpectToken(ref reader, JsonTokenType.StartArray);
reader.ExpectToken(JsonTokenType.StartArray);
IList<T> list = EmptyList<T>.Singleton;
@ -471,32 +433,96 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
return list.ToArray();
default:
throw UnexpectedTokenException(ref reader);
throw reader.ThrowUnexpectedToken();
}
}
throw EofException;
}
// Consumes a token from the stream.
static void ConsumeToken(ref Reader reader)
static void ReadStreamToUTF8(Stream stream, out ReadOnlySpan<byte> utf8Text)
{
if (!reader.Read())
// This buffer size is chosen to be about 50% larger than
// the average file size in our corpus, so most of the time
// we don't need to reallocate and copy.
var buffer = new byte[150000];
var bytesRead = stream.Read(buffer, 0, buffer.Length);
var spaceLeftInBuffer = buffer.Length - bytesRead;
while (spaceLeftInBuffer == 0)
{
throw EofException;
// Might be more to read. Expand the buffer.
var newBuffer = new byte[buffer.Length * 2];
spaceLeftInBuffer = buffer.Length;
var totalBytesRead = buffer.Length;
Array.Copy(buffer, 0, newBuffer, 0, totalBytesRead);
buffer = newBuffer;
bytesRead = stream.Read(buffer, totalBytesRead, buffer.Length - totalBytesRead);
spaceLeftInBuffer -= bytesRead;
}
utf8Text = new ReadOnlySpan<byte>(buffer);
NormalizeTextToUTF8(ref utf8Text);
}
// Updates the given span so that its contents are UTF8.
static void NormalizeTextToUTF8(ref ReadOnlySpan<byte> text)
{
if (text.Length >= 1)
{
switch (text[0])
{
case 0xEF:
// Possibly start of UTF8 BOM.
if (text.Length >= 3 && text[1] == 0xBB && text[2] == 0xBF)
{
// UTF8 BOM.
// Step over the UTF8 BOM.
text = text.Slice(3, text.Length - 3);
}
break;
case 0xFE:
// Possibly start of UTF16LE BOM.
if (text.Length >= 2 && text[1] == 0xFF)
{
// Step over the UTF16 BOM and convert to UTF8.
text = Encoding.UTF8.GetBytes(
Encoding.Unicode.GetString(
text.Slice(2, text.Length - 2)
#if WINDOWS_UWP
// NOTE: the ToArray here is necessary for UWP apps as they don't
// yet support GetString(ReadOnlySpan<byte>).
.ToArray()
#endif // WINDOWS_UWP
));
}
break;
case 0xFF:
// Possibly start of UTF16BE BOM.
if (text.Length >= 2 && text[1] == 0xFE)
{
// Step over the UTF16 BOM and convert to UTF8.
text = Encoding.UTF8.GetBytes(
Encoding.BigEndianUnicode.GetString(
text.Slice(2, text.Length - 2)
#if WINDOWS_UWP
// NOTE: the ToArray here is necessary for UWP apps as they don't
// yet support GetString(ReadOnlySpan<byte>).
.ToArray()
#endif // WINDOWS_UWP
));
}
break;
}
}
}
// We got to the end of the file while still reading. Fatal.
static LottieCompositionReaderException EofException => Exception("EOF");
// The JSON is malformed - we found an unexpected token. Fatal.
static LottieCompositionReaderException UnexpectedTokenException(ref Reader reader) => Exception($"Unexpected token: {reader.TokenType}", ref reader);
static LottieCompositionReaderException UnexpectedTokenException(JsonValueKind kind) => Exception($"Unexpected token: {kind}");
static LottieCompositionReaderException Exception(string message, ref Reader reader) => Exception($"{message} @ {reader.NewtonsoftReader.Path}");
static LottieCompositionReaderException Exception(string message) => new LottieCompositionReaderException(message);
}
}

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

@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using Newtonsoft.Json.Linq;
using System.Text.Json;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
@ -14,16 +15,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal readonly struct LottieJsonArrayElement
{
readonly LottieCompositionReader _owner;
readonly JArray _wrapped;
readonly JsonElement _wrapped;
internal LottieJsonArrayElement(LottieCompositionReader owner, JToken wrapped)
internal LottieJsonArrayElement(LottieCompositionReader owner, JsonElement wrapped)
{
Debug.Assert(wrapped.Type == JTokenType.Array, "Precondition");
Debug.Assert(wrapped.ValueKind == JsonValueKind.Array, "Precondition");
_owner = owner;
_wrapped = (JArray)wrapped;
_wrapped = wrapped;
}
internal int Count => _wrapped.Count;
internal int Count => _wrapped.GetArrayLength();
public LottieJsonElement this[int index] => new LottieJsonElement(_owner, _wrapped[index]);
@ -78,6 +79,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
public Enumerator GetEnumerator() => new Enumerator(this);
internal T[] Select<T>(LottieJsonElementReader<T> reader)
{
var count = Count;
var result = new T[count];
for (var i = 0; i < count; i++)
{
result[i] = reader(this[i]);
}
return result;
}
internal T[] Select<T>(Func<LottieJsonElement, T> reader)
{
var count = Count;
var result = new T[count];
@ -102,7 +115,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
public LottieJsonElement Current => new LottieJsonElement(_owner._owner, _owner._wrapped[_currentIndex]);
public bool MoveNext() => _owner._wrapped.Count > ++_currentIndex;
public bool MoveNext() => _owner._wrapped.GetArrayLength() > ++_currentIndex;
public void Dispose()
{

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

@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
#pragma warning disable SA1205 // Partial elements should declare access
#pragma warning disable SA1601 // Partial elements should be documented
sealed partial class LottieCompositionReader
{
ref struct LottieJsonDocument
{
LottieCompositionReader _owner;
JsonDocument _wrapped;
internal LottieJsonDocument(LottieCompositionReader owner, JsonDocument wrapped)
{
_owner = owner;
_wrapped = wrapped;
}
internal LottieJsonElement RootElement => new LottieJsonElement(_owner, _wrapped.RootElement);
public void Dispose()
=> _wrapped.Dispose();
}
}
}

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

@ -3,8 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Toolkit.Uwp.UI.Lottie.GenericData;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
@ -17,72 +18,35 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal readonly struct LottieJsonElement
{
readonly LottieCompositionReader _owner;
readonly JToken _wrapped;
readonly JsonElement _wrapped;
internal LottieJsonElement(LottieCompositionReader owner, JToken wrapped)
internal LottieJsonElement(LottieCompositionReader owner, JsonElement wrapped)
{
if (wrapped is null)
{
throw new ArgumentException();
}
_owner = owner;
_wrapped = wrapped;
}
internal JsonValueKind Kind
{
get
{
switch (_wrapped.Type)
{
case JTokenType.Array:
return JsonValueKind.Array;
case JTokenType.Boolean:
return JsonValueKind.False;
case JTokenType.Integer:
case JTokenType.Float:
return JsonValueKind.Number;
case JTokenType.Null:
return JsonValueKind.Null;
case JTokenType.Object:
return JsonValueKind.Object;
case JTokenType.String:
return JsonValueKind.String;
case JTokenType.None:
case JTokenType.Constructor:
case JTokenType.Property:
case JTokenType.Comment:
case JTokenType.Undefined:
case JTokenType.Date:
case JTokenType.Raw:
case JTokenType.Bytes:
case JTokenType.Guid:
case JTokenType.Uri:
case JTokenType.TimeSpan:
default:
return JsonValueKind.Undefined;
}
}
}
=> _wrapped.ValueKind;
internal LottieJsonArrayElement? AsArray()
=> _wrapped.Type == JTokenType.Array
=> _wrapped.ValueKind == JsonValueKind.Array
? new LottieJsonArrayElement(_owner, _wrapped)
: (LottieJsonArrayElement?)null;
internal LottieJsonObjectElement? AsObject()
=> _wrapped.Type == JTokenType.Object
=> _wrapped.ValueKind == JsonValueKind.Object
? new LottieJsonObjectElement(_owner, _wrapped)
: (LottieJsonObjectElement?)null;
internal bool? AsBoolean()
{
switch (_wrapped.Type)
switch (_wrapped.ValueKind)
{
case JTokenType.Boolean:
return (bool)_wrapped;
case JsonValueKind.True:
return true;
case JsonValueKind.False:
return false;
default:
var number = AsDouble();
return number.HasValue
@ -93,17 +57,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal double? AsDouble()
{
switch (_wrapped.Type)
switch (_wrapped.ValueKind)
{
case JTokenType.Float:
case JTokenType.Integer:
return (double)_wrapped;
case JTokenType.String:
return double.TryParse((string)_wrapped, out var result)
case JsonValueKind.Number:
return _wrapped.GetDouble();
case JsonValueKind.String:
return double.TryParse(_wrapped.GetString(), out var result)
? result
: (double?)null;
case JTokenType.Array:
case JsonValueKind.Array:
{
var array = AsArray().Value;
switch (array.Count)
@ -141,14 +104,45 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal string AsString()
{
switch (_wrapped.Type)
switch (_wrapped.ValueKind)
{
case JTokenType.String:
return (string)_wrapped;
case JsonValueKind.String:
return _wrapped.GetString();
default:
return null;
}
}
internal GenericDataObject ToGenericDataObject()
{
switch (Kind)
{
case JsonValueKind.Object:
var obj = AsObject().Value;
var dict = new Dictionary<string, GenericDataObject>();
foreach (var property in obj)
{
dict.Add(property.Key, property.Value.ToGenericDataObject());
}
return GenericDataMap.Create(dict);
case JsonValueKind.Array:
return GenericDataList.Create(AsArray().Value.Select<GenericDataObject>(elem => elem.ToGenericDataObject()));
case JsonValueKind.String:
return GenericDataString.Create(AsString());
case JsonValueKind.Number:
return GenericDataNumber.Create(AsDouble().Value);
case JsonValueKind.True:
return GenericDataBool.True;
case JsonValueKind.False:
return GenericDataBool.False;
case JsonValueKind.Null:
return null;
default:
throw Exceptions.Unreachable;
}
}
}
}
}

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

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Newtonsoft.Json.Linq;
using System.Text.Json;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
@ -18,48 +18,46 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal readonly struct LottieJsonObjectElement
{
readonly LottieCompositionReader _owner;
readonly JObject _wrapped;
readonly (bool unread, string propertyName)[] _propertyNames;
readonly (bool unread, JsonProperty property)[] _properties;
internal LottieJsonObjectElement(LottieCompositionReader owner, JToken wrapped)
internal LottieJsonObjectElement(LottieCompositionReader owner, JsonElement wrapped)
{
Debug.Assert(wrapped.Type == JTokenType.Object, "Precondition");
Debug.Assert(wrapped.ValueKind == JsonValueKind.Object, "Precondition");
_owner = owner;
_wrapped = (JObject)wrapped;
// Get the list of property names.
_propertyNames = ((IEnumerable<KeyValuePair<string, JToken>>)wrapped).Select(s => (true, s.Key)).ToArray();
// Get the properties.
_properties = wrapped.EnumerateObject().Select(jp => (true, jp)).ToArray();
// Sort the names so they can be binary searched.
Array.Sort(_propertyNames, Comparer.Instance);
Array.Sort(_properties, Comparer.Instance);
}
public Vector3? AsVector3()
{
var x = DoubleOrNullProperty("x");
var y = DoubleOrNullProperty("y");
var z = DoubleOrNullProperty("z");
var x = DoublePropertyOrNull("x");
var y = DoublePropertyOrNull("y");
var z = DoublePropertyOrNull("z");
return x is null
? (Vector3?)null
: new Vector3(x.Value, y ?? 0, z ?? 0);
}
internal LottieJsonArrayElement? AsArrayProperty(string propertyName)
internal LottieJsonArrayElement? ArrayPropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsArray() : null;
internal bool? BoolOrNullProperty(string propertyName)
internal bool? BoolPropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsBoolean() : null;
internal double? DoubleOrNullProperty(string propertyName)
internal double? DoublePropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsDouble() : null;
internal int? Int32OrNullProperty(string propertyName)
internal int? Int32PropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsInt32() : null;
internal LottieJsonObjectElement? ObjectOrNullProperty(string propertyName)
internal LottieJsonObjectElement? ObjectPropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsObject() : null;
internal string StringOrNullProperty(string propertyName)
internal string StringPropertyOrNull(string propertyName)
=> TryGetProperty(propertyName, out var value) ? value.AsString() : null;
// Indicates that the given property will not be read because we don't yet support it.
@ -119,49 +117,56 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal bool TryGetProperty(string propertyName, out LottieJsonElement value)
{
if (_wrapped.TryGetValue(propertyName, out var jtokenValue))
var propertyIndex = FindProperty(propertyName);
if (propertyIndex is null)
{
MarkPropertyAsRead(propertyName);
value = new LottieJsonElement(_owner, jtokenValue);
value = default(LottieJsonElement);
return false;
}
else
{
ref var property = ref _properties[propertyIndex.Value];
property.unread = false;
value = new LottieJsonElement(_owner, property.property.Value);
return true;
}
value = default(LottieJsonElement);
return false;
}
internal static LottieJsonObjectElement Load(LottieCompositionReader owner, ref Reader reader, JsonLoadSettings settings)
{
return new LottieJsonObjectElement(owner, JObject.Load(reader.NewtonsoftReader, settings));
}
internal void AssertAllPropertiesRead([CallerMemberName]string memberName = "")
{
foreach (var pair in _propertyNames)
foreach (var pair in _properties)
{
if (pair.unread)
{
_owner._issues.IgnoredField($"{memberName}.{pair.propertyName}");
_owner._issues.IgnoredField($"{memberName}.{pair.property.Name}");
}
}
}
public Enumerator GetEnumerator() => new Enumerator(this, _wrapped);
public Enumerator GetEnumerator() => new Enumerator(this);
// Marks the given property name as being read.
void MarkPropertyAsRead(string propertyName)
{
var propertyIndex = FindProperty(propertyName);
if (propertyIndex.HasValue)
{
_properties[propertyIndex.Value].unread = false;
}
}
// Returns the index of the given property, or null if not found.
int? FindProperty(string propertyName)
{
int min = 0;
int max = _propertyNames.Length - 1;
int max = _properties.Length - 1;
while (min <= max)
{
int mid = (min + max) / 2;
var current = _propertyNames[mid].propertyName;
var current = _properties[mid].property.Name;
if (propertyName == current)
{
_propertyNames[mid].unread = false;
return;
return mid;
}
else if (string.CompareOrdinal(propertyName, current) < 0)
{
@ -176,35 +181,40 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
}
// Not found.
return null;
}
sealed class Comparer : IComparer<(bool, string)>
sealed class Comparer : IComparer<(bool, JsonProperty)>
{
internal static readonly Comparer Instance = new Comparer();
int IComparer<(bool, string)>.Compare((bool, string) x, (bool, string) y)
=> string.CompareOrdinal(x.Item2, y.Item2);
int IComparer<(bool, JsonProperty)>.Compare((bool, JsonProperty) x, (bool, JsonProperty) y)
=> string.CompareOrdinal(x.Item2.Name, y.Item2.Name);
}
public struct Enumerator
{
readonly LottieJsonObjectElement _owner;
readonly IEnumerator<KeyValuePair<string, JToken>> _wrapped;
int _currentIndex;
internal Enumerator(LottieJsonObjectElement owner, JObject wrapped)
internal Enumerator(LottieJsonObjectElement owner)
{
_owner = owner;
_wrapped = wrapped.GetEnumerator();
_currentIndex = -1;
}
public KeyValuePair<string, LottieJsonElement> Current
=> new KeyValuePair<string, LottieJsonElement>(
_wrapped.Current.Key,
new LottieJsonElement(_owner._owner, _wrapped.Current.Value));
{
get
{
ref var property = ref _owner._properties[_currentIndex].property;
return new KeyValuePair<string, LottieJsonElement>(
property.Name,
new LottieJsonElement(_owner._owner, property.Value));
}
}
public void Dispose() => _wrapped.Dispose();
public bool MoveNext() => _wrapped.MoveNext();
public bool MoveNext() => _owner._properties.Length > ++_currentIndex;
}
}
}

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

@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
Marker ParseMarker(ref Reader reader)
{
ExpectToken(ref reader, JsonTokenType.StartObject);
reader.ExpectToken(JsonTokenType.StartObject);
string name = null;
double durationMilliseconds = 0;
@ -25,7 +25,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case JsonTokenType.PropertyName:
var currentProperty = reader.GetString();
ConsumeToken(ref reader);
reader.ConsumeToken();
switch (currentProperty)
{
@ -48,7 +48,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
case JsonTokenType.EndObject:
return new Marker(name: name, frame: frame, durationMilliseconds: durationMilliseconds);
default:
throw UnexpectedTokenException(ref reader);
throw reader.ThrowUnexpectedToken();
}
}

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

@ -4,132 +4,126 @@
using System;
using System.Text.Json;
using Newtonsoft.Json;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
// A type that has a similar interface to System.Text.Json.Utf8JsonReader.
// This type helps to hide the choice of Newtonsoft.Json vs System.Text.Json.
ref struct Reader
#pragma warning disable SA1205 // Partial elements should declare access
#pragma warning disable SA1601 // Partial elements should be documented
sealed partial class LottieCompositionReader
{
readonly JsonReader _reader;
internal Reader(JsonReader reader)
ref struct Reader
{
_reader = reader;
}
LottieCompositionReader _owner;
Utf8JsonReader _jsonReader;
internal JsonTokenType TokenType
{
get
internal Reader(LottieCompositionReader owner, ReadOnlySpan<byte> jsonText)
{
switch (_reader.TokenType)
_owner = owner;
_jsonReader = new Utf8JsonReader(
jsonText,
new JsonReaderOptions
{
// Be resilient about trailing commas - ignore them.
AllowTrailingCommas = true,
// Be resilient about comments - ignore them.
CommentHandling = JsonCommentHandling.Skip,
// Fail if the JSON exceeds this depth.
MaxDepth = 64,
});
}
internal void ExpectToken(JsonTokenType tokenType)
{
if (_jsonReader.TokenType != tokenType)
{
case JsonToken.None:
return JsonTokenType.None;
case JsonToken.StartObject:
return JsonTokenType.StartObject;
case JsonToken.StartArray:
return JsonTokenType.StartArray;
case JsonToken.PropertyName:
return JsonTokenType.PropertyName;
case JsonToken.Comment:
return JsonTokenType.Comment;
case JsonToken.Integer:
case JsonToken.Float:
return JsonTokenType.Number;
case JsonToken.String:
return JsonTokenType.String;
case JsonToken.Boolean:
return JsonTokenType.False;
case JsonToken.Null:
return JsonTokenType.Null;
case JsonToken.EndObject:
return JsonTokenType.EndObject;
case JsonToken.EndArray:
return JsonTokenType.EndArray;
case JsonToken.Bytes:
case JsonToken.Date:
case JsonToken.EndConstructor:
case JsonToken.Raw:
case JsonToken.StartConstructor:
case JsonToken.Undefined:
default:
throw Exceptions.Unreachable;
throw ThrowUnexpectedToken(tokenType);
}
}
}
internal bool Read() => _reader.Read();
internal JsonTokenType TokenType => _jsonReader.TokenType;
internal void Skip() => _reader.Skip();
internal bool Read() => _jsonReader.Read();
internal bool GetBoolean() => (bool)_reader.Value;
internal double GetDouble() => (double)_reader.Value;
internal long GetInt64() => (long)_reader.Value;
internal string GetString() => (string)_reader.Value;
internal bool ParseBool()
{
switch (_reader.TokenType)
// Consumes a token from the stream.
internal void ConsumeToken()
{
case JsonToken.Integer:
return GetInt64() != 0;
case JsonToken.Float:
return GetDouble() != 0;
case JsonToken.Boolean:
return GetBoolean();
default:
throw new LottieCompositionReaderException($"Expected a bool, but got {_reader.TokenType}");
}
}
internal double ParseDouble()
{
switch (_reader.TokenType)
{
case JsonToken.Integer:
return GetInt64();
case JsonToken.Float:
return GetDouble();
case JsonToken.String:
if (double.TryParse(GetString(), out var result))
{
return result;
}
break;
if (!_jsonReader.Read())
{
throw EofException;
}
}
throw new LottieCompositionReaderException($"Expected a number, but got {_reader.TokenType}");
}
internal void Skip() => _jsonReader.Skip();
internal int ParseInt()
{
switch (_reader.TokenType)
internal string GetString() => _jsonReader.GetString();
internal LottieJsonDocument ParseElement()
=> JsonDocument.TryParseValue(ref _jsonReader, out var document)
? new LottieJsonDocument(_owner, document)
: throw Throw("Failed to parse value.");
internal bool ParseBool()
{
case JsonToken.Integer:
return checked((int)GetInt64());
case JsonToken.Float:
return checked((int)(long)Math.Round(GetDouble()));
case JsonToken.String:
if (double.TryParse(GetString(), out var result))
{
return checked((int)(long)Math.Round(result));
}
break;
switch (_jsonReader.TokenType)
{
case JsonTokenType.Number:
return _jsonReader.GetDouble() != 0;
case JsonTokenType.True:
return true;
case JsonTokenType.False:
return false;
default:
throw ThrowUnexpectedToken("bool");
}
}
throw new LottieCompositionReaderException($"Expected a number, but got {_reader.TokenType}");
}
internal double ParseDouble()
{
switch (_jsonReader.TokenType)
{
case JsonTokenType.Number:
return _jsonReader.GetDouble();
case JsonTokenType.String:
if (double.TryParse(_jsonReader.GetString(), out var result))
{
return result;
}
// Not part of System.Text.Json. This is a backdoor to get access to the
// underlying Newtonsoft reader while we transition the code more to
// System.Text.Json patterns.
internal JsonReader NewtonsoftReader => _reader;
break;
}
throw ThrowUnexpectedToken(JsonTokenType.Number);
}
internal int ParseInt()
{
switch (_jsonReader.TokenType)
{
case JsonTokenType.Number:
return checked((int)(long)Math.Round(_jsonReader.GetDouble()));
case JsonTokenType.String:
if (double.TryParse(_jsonReader.GetString(), out var result))
{
return checked((int)(long)Math.Round(result));
}
break;
}
throw ThrowUnexpectedToken(JsonTokenType.Number);
}
internal Exception ThrowUnexpectedToken(JsonTokenType expectedType) => ThrowUnexpectedToken(expectedType.ToString());
internal Exception ThrowUnexpectedToken(string expectedType)
=> Throw($"Unexpected token. Expected {expectedType} but got {_jsonReader.TokenType}");
internal Exception ThrowUnexpectedToken() => Throw($"Unexpected token: {_jsonReader.TokenType}");
internal Exception Throw(string message)
=> throw Exception($"{message} @ {_jsonReader.Position} depth={_jsonReader.CurrentDepth}");
}
}
}

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

@ -16,7 +16,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
var args = default(ShapeLayerContent.ShapeLayerContentArgs);
ReadShapeLayerContentArgs(obj, ref args);
var type = obj.StringOrNullProperty("ty") ?? string.Empty;
var type = obj.StringPropertyOrNull("ty") ?? string.Empty;
switch (type)
{
@ -61,8 +61,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("cix", "cl", "ix", "hd");
var numberOfProperties = obj.Int32OrNullProperty("np");
var items = ReadShapesList(obj.AsArrayProperty("it"));
var numberOfProperties = obj.Int32PropertyOrNull("np");
var items = ReadShapesList(obj.ArrayPropertyOrNull("it"));
obj.AssertAllPropertiesRead();
return new ShapeGroup(in shapeLayerContentArgs, items);
}
@ -73,7 +73,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
args.Name = ReadName(obj);
args.MatchName = ReadMatchName(obj);
args.BlendMode = BmToBlendMode(obj.DoubleOrNullProperty("bm"));
args.BlendMode = BmToBlendMode(obj.DoublePropertyOrNull("bm"));
}
// "st"
@ -84,17 +84,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("fillEnabled", "hd");
var color = ReadColorFromC(obj);
var opacity = ReadOpacityFromO(obj);
var strokeWidth = ReadAnimatableFloat(obj.ObjectOrNullProperty("w"));
var capType = LcToLineCapType(obj.DoubleOrNullProperty("lc"));
var joinType = LjToLineJoinType(obj.DoubleOrNullProperty("lj"));
var miterLimit = obj.DoubleOrNullProperty("ml") ?? 4; // Default miter limit in After Effects is 4
var color = ReadAnimatableColor(obj.ObjectPropertyOrNull("c"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var strokeWidth = ReadAnimatableFloat(obj.ObjectPropertyOrNull("w"));
var capType = LcToLineCapType(obj.DoublePropertyOrNull("lc"));
var joinType = LjToLineJoinType(obj.DoublePropertyOrNull("lj"));
var miterLimit = obj.DoublePropertyOrNull("ml") ?? 4; // Default miter limit in After Effects is 4
// Get dash pattern to be set as StrokeDashArray
Animatable<double> offset = null;
var dashPattern = new List<double>();
var dashes = obj.AsArrayProperty("d");
var dashes = obj.ArrayPropertyOrNull("d");
if (dashes != null)
{
var dashesArray = dashes.Value;
@ -103,14 +103,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
var dashObj = dashesArray[i].AsObject();
switch (dashObj?.StringOrNullProperty("n"))
switch (dashObj?.StringPropertyOrNull("n"))
{
case "o":
offset = ReadAnimatableFloat(dashObj?.ObjectOrNullProperty("v"));
offset = ReadAnimatableFloat(dashObj?.ObjectPropertyOrNull("v"));
break;
case "d":
case "g":
dashPattern.Add(ReadAnimatableFloat(dashObj?.ObjectOrNullProperty("v")).InitialValue);
dashPattern.Add(ReadAnimatableFloat(dashObj?.ObjectPropertyOrNull("v")).InitialValue);
break;
}
}
@ -134,7 +134,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
in LottieJsonObjectElement obj,
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
{
switch (TToGradientType(obj.DoubleOrNullProperty("t")))
switch (TToGradientType(obj.DoublePropertyOrNull("t")))
{
case GradientType.Linear:
return ReadLinearGradientStroke(obj, in shapeLayerContentArgs);
@ -152,14 +152,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("hd", "t", "1");
var opacity = ReadOpacityFromO(obj);
var strokeWidth = ReadAnimatableFloat(obj.ObjectOrNullProperty("w"));
var capType = LcToLineCapType(obj.DoubleOrNullProperty("lc"));
var joinType = LjToLineJoinType(obj.DoubleOrNullProperty("lj"));
var miterLimit = obj.DoubleOrNullProperty("ml") ?? 4; // Default miter limit in After Effects is 4
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var strokeWidth = ReadAnimatableFloat(obj.ObjectPropertyOrNull("w"));
var capType = LcToLineCapType(obj.DoublePropertyOrNull("lc"));
var joinType = LjToLineJoinType(obj.DoublePropertyOrNull("lj"));
var miterLimit = obj.DoublePropertyOrNull("ml") ?? 4; // Default miter limit in After Effects is 4
var startPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectPropertyOrNull("g"));
obj.AssertAllPropertiesRead();
return new LinearGradientStroke(
@ -181,20 +181,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("t", "h", "1");
var highlightLengthObject = obj.ObjectOrNullProperty("h");
var highlightLengthObject = obj.ObjectPropertyOrNull("h");
var highlightLength = ReadAnimatableFloat(highlightLengthObject);
var highlightAngleObject = obj.ObjectOrNullProperty("a");
var highlightAngleObject = obj.ObjectPropertyOrNull("a");
var highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
var opacity = ReadOpacityFromO(obj);
var strokeWidth = ReadAnimatableFloat(obj.ObjectOrNullProperty("w"));
var capType = LcToLineCapType(obj.DoubleOrNullProperty("lc"));
var joinType = LjToLineJoinType(obj.DoubleOrNullProperty("lj"));
var miterLimit = obj.DoubleOrNullProperty("ml") ?? 4; // Default miter limit in After Effects is 4
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var strokeWidth = ReadAnimatableFloat(obj.ObjectPropertyOrNull("w"));
var capType = LcToLineCapType(obj.DoublePropertyOrNull("lc"));
var joinType = LjToLineJoinType(obj.DoublePropertyOrNull("lj"));
var miterLimit = obj.DoublePropertyOrNull("ml") ?? 4; // Default miter limit in After Effects is 4
var startPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectPropertyOrNull("g"));
obj.AssertAllPropertiesRead();
return new RadialGradientStroke(
@ -220,8 +220,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
obj.IgnorePropertyThatIsNotYetSupported("fillEnabled", "cl", "hd");
var fillType = ReadFillType(obj);
var opacity = ReadOpacityFromO(obj);
var color = ReadColorFromC(obj);
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var color = ReadAnimatableColor(obj.ObjectPropertyOrNull("c"));
obj.AssertAllPropertiesRead();
return new SolidColorFill(in shapeLayerContentArgs, fillType, opacity, color);
@ -232,7 +232,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
in LottieJsonObjectElement obj,
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
{
switch (TToGradientType(obj.DoubleOrNullProperty("t")))
switch (TToGradientType(obj.DoublePropertyOrNull("t")))
{
case GradientType.Linear:
return ReadLinearGradientFill(obj, in shapeLayerContentArgs);
@ -251,15 +251,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
obj.IgnorePropertyThatIsNotYetSupported("hd", "1");
var fillType = ReadFillType(obj);
var opacity = ReadOpacityFromO(obj);
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var startPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectPropertyOrNull("g"));
var highlightLengthObject = obj.ObjectOrNullProperty("h");
var highlightLengthObject = obj.ObjectPropertyOrNull("h");
var highlightLength = ReadAnimatableFloat(highlightLengthObject);
var highlightAngleObject = obj.ObjectOrNullProperty("a");
var highlightAngleObject = obj.ObjectPropertyOrNull("a");
var highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
obj.AssertAllPropertiesRead();
@ -282,10 +282,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
obj.IgnorePropertyThatIsNotYetSupported("hd");
var fillType = ReadFillType(obj);
var opacity = ReadOpacityFromO(obj);
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
var startPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var endPoint = ReadAnimatableVector3(obj.ObjectPropertyOrNull("e"));
var gradientStops = ReadAnimatableGradientStops(obj.ObjectPropertyOrNull("g"));
obj.AssertAllPropertiesRead();
return new LinearGradientFill(
@ -304,9 +304,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("closed", "hd");
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
var diameter = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var direction = obj.BoolOrNullProperty("d") == true;
var position = ReadAnimatableVector3(obj.ObjectPropertyOrNull("p"));
var diameter = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var direction = obj.BoolPropertyOrNull("d") == true;
obj.AssertAllPropertiesRead();
return new Ellipse(in shapeLayerContentArgs, direction, position, diameter);
}
@ -318,33 +318,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("ix");
var direction = obj.BoolOrNullProperty("d") == true;
var direction = obj.BoolPropertyOrNull("d") == true;
var points = ReadAnimatableFloat(obj.ObjectOrNullProperty("pt"));
var points = ReadAnimatableFloat(obj.ObjectPropertyOrNull("pt"));
if (points.IsAnimated)
{
_issues.PolystarAnimation("points");
}
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
var position = ReadAnimatableVector3(obj.ObjectPropertyOrNull("p"));
if (position.IsAnimated)
{
_issues.PolystarAnimation("position");
}
var rotation = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
var rotation = ReadAnimatableFloat(obj.ObjectPropertyOrNull("r"));
if (rotation.IsAnimated)
{
_issues.PolystarAnimation("rotation");
}
var outerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("or"));
var outerRadius = ReadAnimatableFloat(obj.ObjectPropertyOrNull("or"));
if (outerRadius.IsAnimated)
{
_issues.PolystarAnimation("outer radius");
}
var outerRoundedness = ReadAnimatableFloat(obj.ObjectOrNullProperty("os"));
var outerRoundedness = ReadAnimatableFloat(obj.ObjectPropertyOrNull("os"));
if (outerRoundedness.IsAnimated)
{
_issues.PolystarAnimation("outer roundedness");
@ -353,18 +353,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
Animatable<double> innerRadius;
Animatable<double> innerRoundedness;
var polystarType = SyToPolystarType(obj.DoubleOrNullProperty("sy")) ?? Polystar.PolyStarType.Polygon;
var polystarType = SyToPolystarType(obj.DoublePropertyOrNull("sy")) ?? Polystar.PolyStarType.Polygon;
switch (polystarType)
{
case Polystar.PolyStarType.Star:
innerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("ir"));
innerRadius = ReadAnimatableFloat(obj.ObjectPropertyOrNull("ir"));
if (innerRadius.IsAnimated)
{
_issues.PolystarAnimation("inner radius");
}
innerRoundedness = ReadAnimatableFloat(obj.ObjectOrNullProperty("is"));
innerRoundedness = ReadAnimatableFloat(obj.ObjectPropertyOrNull("is"));
if (innerRoundedness.IsAnimated)
{
_issues.PolystarAnimation("inner roundedness");
@ -399,10 +399,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("hd");
var direction = obj.BoolOrNullProperty("d") == true;
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
var size = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
var cornerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
var direction = obj.BoolPropertyOrNull("d") == true;
var position = ReadAnimatableVector3(obj.ObjectPropertyOrNull("p"));
var size = ReadAnimatableVector3(obj.ObjectPropertyOrNull("s"));
var cornerRadius = ReadAnimatableFloat(obj.ObjectPropertyOrNull("r"));
obj.AssertAllPropertiesRead();
return new Rectangle(in shapeLayerContentArgs, direction, position, size, cornerRadius);
@ -415,8 +415,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("ind", "ix", "hd", "cl", "closed");
var geometry = ReadAnimatableGeometry(obj.ObjectOrNullProperty("ks"));
var direction = obj.BoolOrNullProperty("d") == true;
var geometry = ReadAnimatableGeometry(obj.ObjectPropertyOrNull("ks"));
var direction = obj.BoolPropertyOrNull("d") == true;
obj.AssertAllPropertiesRead();
return new Path(in shapeLayerContentArgs, direction, geometry);
}
@ -428,10 +428,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("ix", "hd");
var startTrim = ReadAnimatableTrim(obj.ObjectOrNullProperty("s"));
var endTrim = ReadAnimatableTrim(obj.ObjectOrNullProperty("e"));
var offset = ReadAnimatableRotation(obj.ObjectOrNullProperty("o"));
var trimType = MToTrimType(obj.DoubleOrNullProperty("m"));
var startTrim = ReadAnimatableTrim(obj.ObjectPropertyOrNull("s"));
var endTrim = ReadAnimatableTrim(obj.ObjectPropertyOrNull("e"));
var offset = ReadAnimatableRotation(obj.ObjectPropertyOrNull("o"));
var trimType = MToTrimType(obj.DoublePropertyOrNull("m"));
obj.AssertAllPropertiesRead();
return new TrimPath(
in shapeLayerContentArgs,
@ -445,9 +445,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
in LottieJsonObjectElement obj,
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
{
var count = ReadAnimatableFloat(obj.ObjectOrNullProperty("c"));
var offset = ReadAnimatableFloat(obj.ObjectOrNullProperty("o"));
var transform = ReadRepeaterTransform(obj.ObjectOrNullProperty("tr"), in shapeLayerContentArgs);
var count = ReadAnimatableFloat(obj.ObjectPropertyOrNull("c"));
var offset = ReadAnimatableFloat(obj.ObjectPropertyOrNull("o"));
var transform = ReadRepeaterTransform(obj.ObjectPropertyOrNull("tr"), in shapeLayerContentArgs);
return new Repeater(in shapeLayerContentArgs, count, offset, transform);
}
@ -458,7 +458,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("hd");
var mergeMode = MmToMergeMode(obj.DoubleOrNullProperty("mm"));
var mergeMode = MmToMergeMode(obj.DoublePropertyOrNull("mm"));
obj.AssertAllPropertiesRead();
return new MergePaths(
in shapeLayerContentArgs,
@ -472,7 +472,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Not clear whether we need to read these properties.
obj.IgnorePropertyThatIsNotYetSupported("hd", "ix");
var radius = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
var radius = ReadAnimatableFloat(obj.ObjectPropertyOrNull("r"));
obj.AssertAllPropertiesRead();
return new RoundedCorner(
in shapeLayerContentArgs,
@ -482,7 +482,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
ShapeFill.PathFillType ReadFillType(in LottieJsonObjectElement obj)
{
// If not specified, the fill type is EvenOdd.
var windingValue = obj.Int32OrNullProperty("r");
var windingValue = obj.Int32PropertyOrNull("r");
switch (windingValue)
{
case 0: return ShapeFill.PathFillType.EvenOdd;
@ -524,8 +524,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
{
obj.IgnorePropertyThatIsNotYetSupported("nm", "ty");
var startOpacity = ReadOpacityFromObject(obj.ObjectOrNullProperty("so"));
var endOpacity = ReadOpacityFromObject(obj.ObjectOrNullProperty("eo"));
var startOpacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("so"));
var endOpacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("eo"));
var transform = ReadTransform(obj, in shapeLayerContentArgs);
obj.AssertAllPropertiesRead();
@ -569,23 +569,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
obj.IgnorePropertyThatIsNotYetSupported("or", "rx", "ry");
obj.IgnorePropertyThatIsNotYetSupported("nm", "ty");
var anchorJson = obj.ObjectOrNullProperty("a");
var anchorJson = obj.ObjectPropertyOrNull("a");
var anchor = ReadAnimatableVector3(anchorJson);
var positionJson = obj.ObjectOrNullProperty("p");
var positionJson = obj.ObjectPropertyOrNull("p");
var position = ReadAnimatableVector3(positionJson);
var scaleJson = obj.ObjectOrNullProperty("s");
var scaleJson = obj.ObjectPropertyOrNull("s");
var scalePercent = ReadAnimatableVector3(scaleJson, s_animatableVector3OneHundred);
var rotation = ReadAnimatableRotation(obj.ObjectPropertyOrNull("r") ?? obj.ObjectPropertyOrNull("rz"));
var scalePercent =
scaleJson != null
? ReadAnimatableVector3(scaleJson)
: new AnimatableVector3(new Vector3(100, 100, 100), null);
var rotationJson = obj.ObjectOrNullProperty("r") ?? obj.ObjectOrNullProperty("rz");
var rotation = ReadAnimatableRotation(rotationJson);
var opacity = ReadOpacityFromO(obj);
var opacity = ReadAnimatableOpacity(obj.ObjectPropertyOrNull("o"));
obj.AssertAllPropertiesRead();
return new Transform(in shapeLayerContentArgs, anchor, position, scalePercent, rotation, opacity);