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:
Родитель
47790ee0d3
Коммит
76565f1182
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче