More json refactoring towards System.Text.Json (#244)
This commit is contained in:
Родитель
ab94c4525a
Коммит
061ab6a318
|
@ -131,8 +131,8 @@
|
|||
<Choose>
|
||||
<When Condition="'$(IsTestProject)' != 'true' and '$(IsSampleProject)' != 'true' and '$(IsDesignProject)' != 'true'">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="15.8.168" PrivateAssets="all" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.5.132" PrivateAssets="all" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
|
||||
|
||||
<!-- <EmbeddedResource Include="**\*.rd.xml" />
|
||||
<Page Include="**\*.xaml" Exclude="**\bin\**\*.xaml;**\obj\**\*.xaml" SubType="Designer" Generator="MSBuild:Compile" />
|
||||
|
|
|
@ -31,18 +31,19 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.UI.Xaml">
|
||||
<Version>2.2.190917002</Version>
|
||||
<Version>2.3.200213001</Version>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>12.0.2</Version>
|
||||
<Version>12.0.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Memory" Version="4.5.3" />
|
||||
<PackageReference Include="System.Numerics.Vectors">
|
||||
<Version>4.5.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="4.7.1" />
|
||||
<PackageReference Include="System.ValueTuple">
|
||||
<Version>4.5.0</Version>
|
||||
</PackageReference>
|
||||
|
|
|
@ -23,14 +23,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<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.6.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\source\LottieData\LottieData.projitems" Label="Shared" />
|
||||
|
|
|
@ -224,16 +224,19 @@
|
|||
<Version>6.2.9</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.UI.Xaml">
|
||||
<Version>2.2.190917002</Version>
|
||||
<Version>2.3.200213001</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>12.0.2</Version>
|
||||
<Version>12.0.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Memory">
|
||||
<Version>4.5.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe">
|
||||
<Version>4.6.0</Version>
|
||||
<Version>4.7.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Text.Json">
|
||||
<Version>4.7.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Win2D.uwp">
|
||||
<Version>1.24.0</Version>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="4.7.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -676,7 +676,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie
|
|||
await task;
|
||||
await Task.Delay(_checkedDelayMs);
|
||||
#else
|
||||
#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks
|
||||
return task;
|
||||
#pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -13,17 +13,15 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Enums.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Exceptions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Fonts.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JCArray.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JCObject.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JCObjectExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JsonToGenericData.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\JTokenExtensions.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\LottieJsonElement.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\LottieJsonObjectElement.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Markers.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\ParsingIssues.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Primitives.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\Reader.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Serialization\ShapeLayerContents.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
using PathGeometry = Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Sequence<Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.BezierSegment>;
|
||||
|
||||
|
@ -15,79 +15,108 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
sealed partial class LottieCompositionReader
|
||||
{
|
||||
static readonly AnimatableParser<double> s_animatableFloatParser = CreateAnimatableParser(ReadFloat);
|
||||
static readonly AnimatableParser<Opacity> s_animatableOpacityParser = CreateAnimatableParser(ReadOpacity);
|
||||
static readonly AnimatableParser<PathGeometry> s_animatableGeometryParser = CreateAnimatableParser(ReadGeometry);
|
||||
static readonly AnimatableParser<Rotation> s_animatableRotationParser = CreateAnimatableParser(ReadRotation);
|
||||
static readonly AnimatableParser<Trim> s_animatableTrimParser = CreateAnimatableParser(ReadTrim);
|
||||
static readonly AnimatableParser<Vector3> s_animatableVector3Parser = CreateAnimatableParser(ReadVector3);
|
||||
static readonly Sequence<GradientStop> s_defaultGradientStops =
|
||||
new Sequence<GradientStop>(new[] { new ColorGradientStop(0, Color.Black) });
|
||||
|
||||
static readonly Animatable<double> s_animatableDoubleZero = CreateNonAnimatedAnimatable(0.0);
|
||||
static readonly Animatable<Color> s_animatableColorBlack = CreateNonAnimatedAnimatable(Color.Black);
|
||||
static readonly Animatable<Opacity> s_animatableOpacityOpaque = CreateNonAnimatedAnimatable(Opacity.Opaque);
|
||||
static readonly Animatable<PathGeometry> s_animatableGeometryEmpty = CreateNonAnimatedAnimatable(Sequence<BezierSegment>.Empty);
|
||||
static readonly Animatable<Rotation> s_animatableRotationNone = CreateNonAnimatedAnimatable(Rotation.None);
|
||||
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 AnimatableParser<double> s_animatableFloatParser =
|
||||
CreateAnimatableParser((in LottieJsonElement element) => element.AsDouble() ?? 0);
|
||||
|
||||
static readonly AnimatableParser<Opacity> s_animatableOpacityParser = CreateAnimatableParser(ParseOpacity);
|
||||
static readonly AnimatableParser<PathGeometry> s_animatableGeometryParser = CreateAnimatableParser(ParseGeometry);
|
||||
static readonly AnimatableParser<Rotation> s_animatableRotationParser = CreateAnimatableParser(ParseRotation);
|
||||
static readonly AnimatableParser<Trim> s_animatableTrimParser = CreateAnimatableParser(ParseTrim);
|
||||
static readonly AnimatableParser<Vector3> s_animatableVector3Parser = CreateAnimatableParser(ParseVector3);
|
||||
readonly AnimatableParser<Color> _animatableColorParser;
|
||||
|
||||
static Animatable<T> CreateNonAnimatedAnimatable<T>(T value)
|
||||
where T : IEquatable<T>
|
||||
=> new Animatable<T>(value, null);
|
||||
|
||||
static Animatable<T> CreateAnimatable<T>(T initialValue, IEnumerable<KeyFrame<T>> keyFrames, int? propertyIndex)
|
||||
where T : IEquatable<T>
|
||||
=> keyFrames.Any()
|
||||
? new Animatable<T>(keyFrames, propertyIndex)
|
||||
: new Animatable<T>(initialValue, propertyIndex);
|
||||
|
||||
static SimpleAnimatableParser<T> CreateAnimatableParser<T>(Func<JToken, T> valueReader)
|
||||
static SimpleAnimatableParser<T> CreateAnimatableParser<T>(LottieJsonElementReader<T> valueReader)
|
||||
where T : IEquatable<T>
|
||||
=> new SimpleAnimatableParser<T>(valueReader);
|
||||
|
||||
Animatable<T> ReadAnimatable<T>(AnimatableParser<T> parser, JCObject obj)
|
||||
Animatable<T> ReadAnimatable<T>(AnimatableParser<T> parser, in LottieJsonObjectElement obj)
|
||||
where T : IEquatable<T>
|
||||
{
|
||||
parser.ParseJson(this, obj, out var keyFrames, out var initialValue);
|
||||
return CreateAnimatable(initialValue, keyFrames, ReadInt(obj, "ix"));
|
||||
return CreateAnimatable(initialValue, keyFrames, obj.Int32OrNullProperty("ix"));
|
||||
}
|
||||
|
||||
Animatable<Color> ReadAnimatableColor(JCObject obj)
|
||||
Animatable<Color> ReadAnimatableColor(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableColorBlack
|
||||
: ReadAnimatable(_animatableColorParser, obj.Value);
|
||||
|
||||
Animatable<double> ReadAnimatableFloat(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableDoubleZero
|
||||
: ReadAnimatable(s_animatableFloatParser, obj.Value);
|
||||
|
||||
Animatable<PathGeometry> ReadAnimatableGeometry(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableGeometryEmpty
|
||||
: ReadAnimatable(s_animatableGeometryParser, obj.Value);
|
||||
|
||||
Animatable<Sequence<GradientStop>> ReadAnimatableGradientStops(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableGradientStopsSingle
|
||||
: ReadAnimatableGradientStops(obj.Value);
|
||||
|
||||
Animatable<Sequence<GradientStop>> ReadAnimatableGradientStops(in LottieJsonObjectElement obj)
|
||||
{
|
||||
if (obj is null)
|
||||
// If the "k" doesn't exist, we can't parse.
|
||||
var kObj = obj.ObjectOrNullProperty("k");
|
||||
|
||||
if (kObj is null)
|
||||
{
|
||||
return new Animatable<Color>(Color.Black, null);
|
||||
return s_animatableGradientStopsSingle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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");
|
||||
return ReadAnimatableGradientStops(kObj.Value, numberOfColorStops, propertyIndex);
|
||||
}
|
||||
|
||||
_animatableColorParser.ParseJson(this, obj, out var keyFrames, out var initialValue);
|
||||
return CreateAnimatable(initialValue, keyFrames, ReadInt(obj, "ix"));
|
||||
}
|
||||
|
||||
Animatable<double> ReadAnimatableFloat(JCObject obj) => ReadAnimatable(s_animatableFloatParser, obj);
|
||||
|
||||
Animatable<PathGeometry> ReadAnimatableGeometry(JCObject obj)
|
||||
Animatable<Sequence<GradientStop>> ReadAnimatableGradientStops(
|
||||
in LottieJsonObjectElement obj,
|
||||
int? numberOfColorStops,
|
||||
int? propertyIndex)
|
||||
{
|
||||
s_animatableGeometryParser.ParseJson(this, obj, out var keyFrames, out var initialValue);
|
||||
var propertyIndex = ReadInt(obj, "ix");
|
||||
|
||||
return keyFrames.Any()
|
||||
? new Animatable<PathGeometry>(keyFrames, propertyIndex)
|
||||
: new Animatable<PathGeometry>(initialValue, propertyIndex);
|
||||
}
|
||||
|
||||
void ReadAnimatableGradientStops(
|
||||
JCObject obj,
|
||||
out Animatable<Sequence<GradientStop>> gradientStops)
|
||||
{
|
||||
// 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 = ReadInt(obj, "p");
|
||||
|
||||
var animatableColorStopsParser = new AnimatableColorStopsParser(numberOfColorStops);
|
||||
|
||||
animatableColorStopsParser.ParseJson(
|
||||
this,
|
||||
obj.GetNamedObject("k"),
|
||||
obj,
|
||||
out var colorKeyFrames,
|
||||
out var colorInitialValue);
|
||||
|
||||
var propertyIndex = ReadInt(obj, "ix");
|
||||
|
||||
if (numberOfColorStops.HasValue)
|
||||
{
|
||||
// There may be opacity stops. Read them.
|
||||
var animatableOpacityStopsParser = new AnimatableOpacityStopsParser(numberOfColorStops.Value);
|
||||
animatableOpacityStopsParser.ParseJson(
|
||||
this,
|
||||
obj.GetNamedObject("k"),
|
||||
obj,
|
||||
out var opacityKeyFrames,
|
||||
out var opacityInitialValue);
|
||||
|
||||
|
@ -95,7 +124,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
// There are opacity key frames. The number of color key frames should be the same
|
||||
// (this is asserted in ConcatGradientStopKeyFrames).
|
||||
gradientStops = new Animatable<Sequence<GradientStop>>(
|
||||
return new Animatable<Sequence<GradientStop>>(
|
||||
ConcatGradientStopKeyFrames(colorKeyFrames, opacityKeyFrames),
|
||||
propertyIndex);
|
||||
}
|
||||
|
@ -109,7 +138,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
"Numbers of key frames in opacity gradient stops and color gradient stops are unequal.");
|
||||
}
|
||||
|
||||
gradientStops = new Animatable<Sequence<GradientStop>>(
|
||||
return new Animatable<Sequence<GradientStop>>(
|
||||
new Sequence<GradientStop>(colorInitialValue.Concat(opacityInitialValue)),
|
||||
propertyIndex);
|
||||
}
|
||||
|
@ -117,7 +146,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
else
|
||||
{
|
||||
// There are only color stops.
|
||||
gradientStops = colorKeyFrames.Any()
|
||||
return colorKeyFrames.Any()
|
||||
? new Animatable<Sequence<GradientStop>>(colorKeyFrames, propertyIndex)
|
||||
: new Animatable<Sequence<GradientStop>>(colorInitialValue, propertyIndex);
|
||||
}
|
||||
|
@ -158,24 +187,38 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
Animatable<Opacity> ReadAnimatableOpacity(JCObject obj) => ReadAnimatable(s_animatableOpacityParser, obj);
|
||||
Animatable<Opacity> ReadAnimatableOpacity(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableOpacityOpaque
|
||||
: ReadAnimatable(s_animatableOpacityParser, obj.Value);
|
||||
|
||||
Animatable<Rotation> ReadAnimatableRotation(JCObject obj) => ReadAnimatable(s_animatableRotationParser, obj);
|
||||
Animatable<Rotation> ReadAnimatableRotation(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableRotationNone
|
||||
: ReadAnimatable(s_animatableRotationParser, obj.Value);
|
||||
|
||||
Animatable<Trim> ReadAnimatableTrim(JCObject obj) => ReadAnimatable(s_animatableTrimParser, obj);
|
||||
Animatable<Trim> ReadAnimatableTrim(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableTrimNone
|
||||
: ReadAnimatable(s_animatableTrimParser, obj.Value);
|
||||
|
||||
IAnimatableVector3 ReadAnimatableVector3(JCObject obj)
|
||||
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement? obj)
|
||||
=> obj is null
|
||||
? s_animatableVector3Zero
|
||||
: ReadAnimatableVector3(obj.Value);
|
||||
|
||||
IAnimatableVector3 ReadAnimatableVector3(in LottieJsonObjectElement obj)
|
||||
{
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "s");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("s");
|
||||
|
||||
// Expressions not supported.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "x");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("x");
|
||||
|
||||
var propertyIndex = ReadInt(obj, "ix");
|
||||
if (obj.ContainsKey("k"))
|
||||
var propertyIndex = obj.Int32OrNullProperty("ix");
|
||||
if (obj.ContainsProperty("k"))
|
||||
{
|
||||
s_animatableVector3Parser.ParseJson(this, obj, out var keyFrames, out var initialValue);
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
|
||||
return keyFrames.Any()
|
||||
? new AnimatableVector3(keyFrames, propertyIndex)
|
||||
|
@ -184,11 +227,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
else
|
||||
{
|
||||
// Split X and Y dimensions
|
||||
var x = ReadAnimatableFloat(obj.GetNamedObject("x"));
|
||||
var y = ReadAnimatableFloat(obj.GetNamedObject("y"));
|
||||
AssertAllFieldsRead(obj);
|
||||
var x = ReadAnimatableFloat(obj.ObjectOrNullProperty("x"));
|
||||
var y = ReadAnimatableFloat(obj.ObjectOrNullProperty("y"));
|
||||
obj.AssertAllPropertiesRead();
|
||||
|
||||
return new AnimatableXYZ(x, y, s_animatable_0);
|
||||
return new AnimatableXYZ(x, y, s_animatableDoubleZero);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,24 +244,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
_ignoreAlpha = ignoreAlpha;
|
||||
}
|
||||
|
||||
protected override Color ReadValue(JToken obj)
|
||||
protected override Color ReadValue(in LottieJsonElement element)
|
||||
{
|
||||
var array = element.AsArray();
|
||||
|
||||
// Colors are expected to be arrays.
|
||||
// If we can't parse it at least return a color.
|
||||
return array is null
|
||||
? Color.Black
|
||||
: ReadValue(array.Value);
|
||||
}
|
||||
|
||||
Color ReadValue(in LottieJsonArrayElement array)
|
||||
{
|
||||
var colorArray = obj.AsArray();
|
||||
double a = 0;
|
||||
double r = 0;
|
||||
double g = 0;
|
||||
double b = 0;
|
||||
int i = 0;
|
||||
var count = colorArray.Count;
|
||||
var count = array.Count;
|
||||
for (; i < count; i++)
|
||||
{
|
||||
// Note: indexing a JsonArray is faster than enumerating.
|
||||
var jsonValue = colorArray[i];
|
||||
var jsonValue = array[i];
|
||||
|
||||
switch (jsonValue.Type)
|
||||
switch (jsonValue.Kind)
|
||||
{
|
||||
case JTokenType.Float:
|
||||
case JTokenType.Integer:
|
||||
case JsonValueKind.Number:
|
||||
break;
|
||||
default:
|
||||
if (_ignoreAlpha && i == 3)
|
||||
|
@ -228,10 +280,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
goto AllColorChannelsRead;
|
||||
}
|
||||
|
||||
throw UnexpectedTokenException(jsonValue.Type);
|
||||
throw UnexpectedTokenException(jsonValue.Kind);
|
||||
}
|
||||
|
||||
var number = (double)jsonValue;
|
||||
var number = jsonValue.AsDouble() ?? 0;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
|
@ -277,15 +329,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
_colorStopCount = colorStopCount;
|
||||
}
|
||||
|
||||
protected override Sequence<GradientStop> ReadValue(JToken obj)
|
||||
protected override Sequence<GradientStop> ReadValue(in LottieJsonElement element)
|
||||
{
|
||||
var gradientStopsData = obj.AsArray().Select(v => (double)v).ToArray();
|
||||
var array = element.AsArray();
|
||||
return array is null
|
||||
? s_defaultGradientStops
|
||||
: ReadValue(array.Value);
|
||||
}
|
||||
|
||||
Sequence<GradientStop> ReadValue(in LottieJsonArrayElement array)
|
||||
{
|
||||
var gradientStopsData = array.Select((in LottieJsonElement v) => v.AsDouble() ?? 0).ToArray();
|
||||
|
||||
// Get the number of color stops. If _colorStopCount wasn't specified, all of
|
||||
// the data in the array is for color stops.
|
||||
var colorStopsDataLength = _colorStopCount.HasValue
|
||||
? _colorStopCount.Value * 4
|
||||
: gradientStopsData.Length;
|
||||
var colorStopsDataLength = _colorStopCount * 4 ?? gradientStopsData.Length;
|
||||
|
||||
if (gradientStopsData.Length < colorStopsDataLength)
|
||||
{
|
||||
|
@ -344,9 +402,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
_colorStopCount = colorStopCount;
|
||||
}
|
||||
|
||||
protected override Sequence<GradientStop> ReadValue(JToken obj)
|
||||
protected override Sequence<GradientStop> ReadValue(in LottieJsonElement element)
|
||||
{
|
||||
var gradientStopsData = obj.AsArray().Skip(_colorStopCount * 4).Select(v => (double)v).ToArray();
|
||||
var array = element.AsArray();
|
||||
return array is null
|
||||
? s_defaultGradientStops
|
||||
: ReadValue(array.Value);
|
||||
}
|
||||
|
||||
Sequence<GradientStop> ReadValue(in LottieJsonArrayElement array)
|
||||
{
|
||||
var gradientStopsData = array.Select((in LottieJsonElement v) => v.AsDouble() ?? 0).Skip(_colorStopCount * 4).ToArray();
|
||||
var gradientStops = new OpacityGradientStop[gradientStopsData.Length / 2];
|
||||
var offset = 0.0;
|
||||
for (int i = 0; i < gradientStopsData.Length; i++)
|
||||
|
@ -383,29 +449,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
}
|
||||
|
||||
protected abstract T ReadValue(JToken obj);
|
||||
protected abstract T ReadValue(in LottieJsonElement element);
|
||||
|
||||
internal void ParseJson(LottieCompositionReader reader, JCObject obj, out IEnumerable<KeyFrame<T>> keyFrames, out T initialValue)
|
||||
internal void ParseJson(
|
||||
LottieCompositionReader reader,
|
||||
in LottieJsonObjectElement obj,
|
||||
out IEnumerable<KeyFrame<T>> keyFrames,
|
||||
out T initialValue)
|
||||
{
|
||||
// Deprecated "a" field meant "isAnimated". The existence of key frames means the same thing.
|
||||
reader.IgnoreFieldIntentionally(obj, "a");
|
||||
// Deprecated "a" property meant "isAnimated". The existence of key frames means the same thing.
|
||||
obj.IgnorePropertyIntentionally("a");
|
||||
|
||||
keyFrames = Array.Empty<KeyFrame<T>>();
|
||||
initialValue = default(T);
|
||||
|
||||
foreach (var field in obj)
|
||||
foreach (var property in obj)
|
||||
{
|
||||
switch (field.Key)
|
||||
switch (property.Key)
|
||||
{
|
||||
case "k":
|
||||
{
|
||||
var k = field.Value;
|
||||
if (k.Type == JTokenType.Array)
|
||||
var k = property.Value;
|
||||
if (k.Kind == JsonValueKind.Array)
|
||||
{
|
||||
var kArray = k.AsArray();
|
||||
if (HasKeyframes(kArray))
|
||||
{
|
||||
keyFrames = ReadKeyFrames(reader, kArray).ToArray();
|
||||
keyFrames = ReadKeyFrames(kArray.Value).ToArray();
|
||||
initialValue = keyFrames.First().Value;
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +498,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
// Do not report it as an issue - existence of "ix" doesn't mean that an expression is actually used.
|
||||
break;
|
||||
|
||||
// Extremely rare fields seen in 1 Lottie file. Ignore.
|
||||
// Extremely rare properties seen in 1 Lottie file. Ignore.
|
||||
case "nm": // Name
|
||||
case "mn": // MatchName
|
||||
case "hd": // IsHidden
|
||||
|
@ -439,19 +509,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
reader._issues.Expressions();
|
||||
break;
|
||||
default:
|
||||
reader._issues.UnexpectedField(field.Key);
|
||||
reader._issues.UnexpectedField(property.Key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool HasKeyframes(JCArray array)
|
||||
static bool HasKeyframes(in LottieJsonArrayElement? array)
|
||||
{
|
||||
var firstItem = array[0];
|
||||
return firstItem.Type == JTokenType.Object && firstItem.AsObject().ContainsKey("t");
|
||||
return array?[0].AsObject()?.ContainsProperty("t") == true;
|
||||
}
|
||||
|
||||
IEnumerable<KeyFrame<T>> ReadKeyFrames(LottieCompositionReader reader, JCArray jsonArray)
|
||||
IEnumerable<KeyFrame<T>> ReadKeyFrames(
|
||||
LottieJsonArrayElement jsonArray)
|
||||
{
|
||||
int count = jsonArray.Count;
|
||||
|
||||
|
@ -478,8 +548,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
// { startFrame_4 }
|
||||
// ]
|
||||
//
|
||||
// In order to handle the current and old formats, we detect the presence of the endValue field.
|
||||
// If there's an endValue field, the keyframes are using the old format.
|
||||
// In order to handle the current and old formats, we detect the presence of the endValue property.
|
||||
// If there's an endValue property, the keyframes are using the old format.
|
||||
//
|
||||
// We convert these to keyframes that match the Windows.UI.Composition notion of a keyframe,
|
||||
// which is a triple: {endValue, endTime, easingFunction}.
|
||||
|
@ -500,20 +570,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var lottieKeyFrame = jsonArray[i].AsObject();
|
||||
if (lottieKeyFrame is null)
|
||||
{
|
||||
throw UnexpectedTokenException(jsonArray[i].Kind);
|
||||
}
|
||||
|
||||
var lottieKeyFrameObj = lottieKeyFrame.Value;
|
||||
|
||||
// "n" is a name on the keyframe. It is not useful and has been deprecated in Bodymovin.
|
||||
reader.IgnoreFieldIntentionally(lottieKeyFrame, "n");
|
||||
lottieKeyFrameObj.IgnorePropertyIntentionally("n");
|
||||
|
||||
// Read the start frame.
|
||||
var startFrame = lottieKeyFrame.GetNamedNumber("t", 0);
|
||||
var startFrame = lottieKeyFrameObj.DoubleOrNullProperty("t") ?? 0;
|
||||
|
||||
if (i == count - 1)
|
||||
{
|
||||
// This is the final key frame.
|
||||
// If parsing the old format, this key frame will just have the "t" startFrame value.
|
||||
// If parsing the new format, this key frame will also have the "s" startValue.
|
||||
var finalStartValue = lottieKeyFrame.GetNamedValue("s");
|
||||
if (finalStartValue is null)
|
||||
if (!lottieKeyFrameObj.TryGetProperty("s", out var finalStartValue))
|
||||
{
|
||||
// Old format.
|
||||
yield return new KeyFrame<T>(startFrame, endValue, to, ti, easing);
|
||||
|
@ -529,21 +604,22 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
// Read the start value.
|
||||
var startValue = ReadValue(lottieKeyFrame.GetNamedValue("s"));
|
||||
lottieKeyFrameObj.TryGetProperty("s", out var startValueToken);
|
||||
var startValue = ReadValue(startValueToken);
|
||||
|
||||
// Output a keyframe that describes how to interpolate to this start value. The easing information
|
||||
// comes from the previous Lottie keyframe.
|
||||
yield return new KeyFrame<T>(startFrame, startValue, to, ti, easing);
|
||||
|
||||
// Spatial control points.
|
||||
if (lottieKeyFrame.ContainsKey("ti"))
|
||||
if (lottieKeyFrameObj.ContainsProperty("ti"))
|
||||
{
|
||||
ti = ReadVector3FromJsonArray(lottieKeyFrame.GetNamedArray("ti"));
|
||||
to = ReadVector3FromJsonArray(lottieKeyFrame.GetNamedArray("to"));
|
||||
ti = lottieKeyFrameObj.AsArrayProperty("ti")?.AsVector3() ?? Vector3.Zero;
|
||||
to = lottieKeyFrameObj.AsArrayProperty("to")?.AsVector3() ?? Vector3.Zero;
|
||||
}
|
||||
|
||||
// Get the easing to the end value, and get the end value.
|
||||
if (ReadBool(lottieKeyFrame, "h") == true)
|
||||
if (lottieKeyFrameObj.BoolOrNullProperty("h") == true)
|
||||
{
|
||||
// Hold the current value. The next value comes from the start
|
||||
// of the next entry.
|
||||
|
@ -555,28 +631,29 @@ 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 = lottieKeyFrame.GetNamedObject("o", null);
|
||||
var cp2Json = lottieKeyFrame.GetNamedObject("i", null);
|
||||
var cp1Json = lottieKeyFrameObj.ObjectOrNullProperty("o");
|
||||
var cp2Json = lottieKeyFrameObj.ObjectOrNullProperty("i");
|
||||
if (cp1Json != null && cp2Json != null)
|
||||
{
|
||||
var cp1 = new Vector3(ReadFloat(cp1Json.GetNamedValue("x")), ReadFloat(cp1Json.GetNamedValue("y")), 0);
|
||||
var cp2 = new Vector3(ReadFloat(cp2Json.GetNamedValue("x")), ReadFloat(cp2Json.GetNamedValue("y")), 0);
|
||||
easing = new CubicBezierEasing(cp1, cp2);
|
||||
var cp1 = cp1Json.Value.AsVector3();
|
||||
var cp2 = cp2Json.Value.AsVector3();
|
||||
easing = new CubicBezierEasing(cp1 ?? Vector3.Zero, cp2 ?? Vector3.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
easing = LinearEasing.Instance;
|
||||
}
|
||||
|
||||
var endValueObject = lottieKeyFrame.GetNamedValue("e");
|
||||
endValue = endValueObject != null ? ReadValue(endValueObject) : default(T);
|
||||
endValue = lottieKeyFrameObj.TryGetProperty("e", out var endValueObject)
|
||||
? ReadValue(endValueObject)
|
||||
: default(T);
|
||||
}
|
||||
|
||||
// "e" is the end value of a key frame but has been deprecated because it should always be equal
|
||||
// to the start value of the next key frame.
|
||||
reader.IgnoreFieldIntentionally(lottieKeyFrame, "e");
|
||||
lottieKeyFrameObj.IgnorePropertyIntentionally("e");
|
||||
|
||||
reader.AssertAllFieldsRead(lottieKeyFrame);
|
||||
lottieKeyFrameObj.AssertAllPropertiesRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -586,14 +663,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
sealed class SimpleAnimatableParser<T> : AnimatableParser<T>
|
||||
where T : IEquatable<T>
|
||||
{
|
||||
readonly Func<JToken, T> _valueReader;
|
||||
readonly LottieJsonElementReader<T> _valueReader;
|
||||
|
||||
internal SimpleAnimatableParser(Func<JToken, T> valueReader)
|
||||
internal SimpleAnimatableParser(LottieJsonElementReader<T> valueReader)
|
||||
{
|
||||
_valueReader = valueReader;
|
||||
}
|
||||
|
||||
protected override T ReadValue(JToken obj) => _valueReader(obj);
|
||||
protected override T ReadValue(in LottieJsonElement element) => _valueReader(element);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
Asset ParseAsset(ref Reader reader)
|
||||
{
|
||||
ExpectToken(ref reader, JsonToken.StartObject);
|
||||
ExpectToken(ref reader, JsonTokenType.StartObject);
|
||||
|
||||
int e = 0;
|
||||
string id = null;
|
||||
|
@ -28,7 +28,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
case JsonTokenType.PropertyName:
|
||||
{
|
||||
var currentProperty = reader.GetString();
|
||||
ConsumeToken(ref reader);
|
||||
|
@ -37,20 +37,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
case "e":
|
||||
// TODO: unknown what this is. It shows up in image assets.
|
||||
e = ParseInt(ref reader);
|
||||
e = reader.ParseInt();
|
||||
break;
|
||||
case "h":
|
||||
height = ParseDouble(ref reader);
|
||||
height = reader.ParseDouble();
|
||||
break;
|
||||
case "id":
|
||||
// Older lotties use a string. New lotties use an int. Handle either as strings.
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.String:
|
||||
case JsonTokenType.String:
|
||||
id = reader.GetString();
|
||||
break;
|
||||
case JsonToken.Integer:
|
||||
id = ParseInt(ref reader).ToString();
|
||||
case JsonTokenType.Number:
|
||||
id = reader.ParseInt().ToString();
|
||||
break;
|
||||
default:
|
||||
throw UnexpectedTokenException(ref reader);
|
||||
|
@ -67,7 +67,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
imagePath = reader.GetString();
|
||||
break;
|
||||
case "w":
|
||||
width = ParseDouble(ref reader);
|
||||
width = reader.ParseDouble();
|
||||
break;
|
||||
|
||||
// Report but ignore unexpected fields.
|
||||
|
@ -81,7 +81,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
case JsonTokenType.EndObject:
|
||||
{
|
||||
if (id is null)
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
Char ParseChar(ref Reader reader)
|
||||
{
|
||||
ExpectToken(ref reader, JsonToken.StartObject);
|
||||
ExpectToken(ref reader, JsonTokenType.StartObject);
|
||||
|
||||
string ch = null;
|
||||
string fFamily = null;
|
||||
|
@ -26,7 +26,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
case JsonTokenType.PropertyName:
|
||||
{
|
||||
var currentProperty = reader.GetString();
|
||||
ConsumeToken(ref reader);
|
||||
|
@ -37,19 +37,19 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
ch = reader.GetString();
|
||||
break;
|
||||
case "data":
|
||||
shapes = ReadShapes(JCObject.Load(ref reader, s_jsonLoadSettings));
|
||||
shapes = ReadShapes(LottieJsonObjectElement.Load(this, ref reader, s_jsonLoadSettings));
|
||||
break;
|
||||
case "fFamily":
|
||||
fFamily = reader.GetString();
|
||||
break;
|
||||
case "size":
|
||||
size = ParseDouble(ref reader);
|
||||
size = reader.ParseDouble();
|
||||
break;
|
||||
case "style":
|
||||
style = reader.GetString();
|
||||
break;
|
||||
case "w":
|
||||
width = ParseDouble(ref reader);
|
||||
width = reader.ParseDouble();
|
||||
break;
|
||||
default:
|
||||
_issues.UnexpectedField(currentProperty);
|
||||
|
@ -58,7 +58,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
case JsonTokenType.EndObject:
|
||||
{
|
||||
return new Char(ch, fFamily, style, size ?? 0, width ?? 0, shapes);
|
||||
}
|
||||
|
|
|
@ -8,165 +8,207 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
sealed partial class LottieCompositionReader
|
||||
{
|
||||
BlendMode BmToBlendMode(double bm)
|
||||
BlendMode BmToBlendMode(double? bm)
|
||||
{
|
||||
if (bm == (int)bm)
|
||||
if (bm.HasValue)
|
||||
{
|
||||
switch ((int)bm)
|
||||
if (TryAsExactInt(bm.Value, out var intValue))
|
||||
{
|
||||
case 0: return BlendMode.Normal;
|
||||
case 1: return BlendMode.Multiply;
|
||||
case 2: return BlendMode.Screen;
|
||||
case 3: return BlendMode.Overlay;
|
||||
case 4: return BlendMode.Darken;
|
||||
case 5: return BlendMode.Lighten;
|
||||
case 6: return BlendMode.ColorDodge;
|
||||
case 7: return BlendMode.ColorBurn;
|
||||
case 8: return BlendMode.HardLight;
|
||||
case 9: return BlendMode.SoftLight;
|
||||
case 10: return BlendMode.Difference;
|
||||
case 11: return BlendMode.Exclusion;
|
||||
case 12: return BlendMode.Hue;
|
||||
case 13: return BlendMode.Saturation;
|
||||
case 14: return BlendMode.Color;
|
||||
case 15: return BlendMode.Luminosity;
|
||||
switch (intValue)
|
||||
{
|
||||
case 0: return BlendMode.Normal;
|
||||
case 1: return BlendMode.Multiply;
|
||||
case 2: return BlendMode.Screen;
|
||||
case 3: return BlendMode.Overlay;
|
||||
case 4: return BlendMode.Darken;
|
||||
case 5: return BlendMode.Lighten;
|
||||
case 6: return BlendMode.ColorDodge;
|
||||
case 7: return BlendMode.ColorBurn;
|
||||
case 8: return BlendMode.HardLight;
|
||||
case 9: return BlendMode.SoftLight;
|
||||
case 10: return BlendMode.Difference;
|
||||
case 11: return BlendMode.Exclusion;
|
||||
case 12: return BlendMode.Hue;
|
||||
case 13: return BlendMode.Saturation;
|
||||
case 14: return BlendMode.Color;
|
||||
case 15: return BlendMode.Luminosity;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("BlendMode", bm.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("BlendMode", bm.ToString());
|
||||
return BlendMode.Normal;
|
||||
}
|
||||
|
||||
ShapeStroke.LineCapType LcToLineCapType(double lc)
|
||||
ShapeStroke.LineCapType LcToLineCapType(double? lc)
|
||||
{
|
||||
if (lc == (int)lc)
|
||||
if (lc.HasValue)
|
||||
{
|
||||
switch ((int)lc)
|
||||
if (TryAsExactInt(lc.Value, out var intValue))
|
||||
{
|
||||
case 1: return ShapeStroke.LineCapType.Butt;
|
||||
case 2: return ShapeStroke.LineCapType.Round;
|
||||
case 3: return ShapeStroke.LineCapType.Projected;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return ShapeStroke.LineCapType.Butt;
|
||||
case 2: return ShapeStroke.LineCapType.Round;
|
||||
case 3: return ShapeStroke.LineCapType.Projected;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LineCapType", lc.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LineCapType", lc.ToString());
|
||||
return ShapeStroke.LineCapType.Butt;
|
||||
}
|
||||
|
||||
ShapeStroke.LineJoinType LjToLineJoinType(double lj)
|
||||
ShapeStroke.LineJoinType LjToLineJoinType(double? lj)
|
||||
{
|
||||
if (lj == (int)lj)
|
||||
if (lj.HasValue)
|
||||
{
|
||||
switch ((int)lj)
|
||||
if (TryAsExactInt(lj.Value, out var intValue))
|
||||
{
|
||||
case 1: return ShapeStroke.LineJoinType.Miter;
|
||||
case 2: return ShapeStroke.LineJoinType.Round;
|
||||
case 3: return ShapeStroke.LineJoinType.Bevel;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return ShapeStroke.LineJoinType.Miter;
|
||||
case 2: return ShapeStroke.LineJoinType.Round;
|
||||
case 3: return ShapeStroke.LineJoinType.Bevel;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LineJoinType", lj.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LineJoinType", lj.ToString());
|
||||
return ShapeStroke.LineJoinType.Miter;
|
||||
}
|
||||
|
||||
MergePaths.MergeMode MmToMergeMode(double mm)
|
||||
MergePaths.MergeMode MmToMergeMode(double? mm)
|
||||
{
|
||||
if (mm == (int)mm)
|
||||
if (mm.HasValue)
|
||||
{
|
||||
switch ((int)mm)
|
||||
if (TryAsExactInt(mm.Value, out var intValue))
|
||||
{
|
||||
case 1: return MergePaths.MergeMode.Merge;
|
||||
case 2: return MergePaths.MergeMode.Add;
|
||||
case 3: return MergePaths.MergeMode.Subtract;
|
||||
case 4: return MergePaths.MergeMode.Intersect;
|
||||
case 5: return MergePaths.MergeMode.ExcludeIntersections;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return MergePaths.MergeMode.Merge;
|
||||
case 2: return MergePaths.MergeMode.Add;
|
||||
case 3: return MergePaths.MergeMode.Subtract;
|
||||
case 4: return MergePaths.MergeMode.Intersect;
|
||||
case 5: return MergePaths.MergeMode.ExcludeIntersections;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("MergeMode", mm.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("MergeMode", mm.ToString());
|
||||
return MergePaths.MergeMode.Merge;
|
||||
}
|
||||
|
||||
TrimPath.TrimType MToTrimType(double m)
|
||||
TrimPath.TrimType MToTrimType(double? m)
|
||||
{
|
||||
if (m == (int)m)
|
||||
if (m.HasValue)
|
||||
{
|
||||
switch ((int)m)
|
||||
if (TryAsExactInt(m.Value, out var intValue))
|
||||
{
|
||||
case 1: return TrimPath.TrimType.Simultaneously;
|
||||
case 2: return TrimPath.TrimType.Individually;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return TrimPath.TrimType.Simultaneously;
|
||||
case 2: return TrimPath.TrimType.Individually;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("TrimType", m.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("TrimType", m.ToString());
|
||||
return TrimPath.TrimType.Simultaneously;
|
||||
}
|
||||
|
||||
Polystar.PolyStarType? SyToPolystarType(double sy)
|
||||
Polystar.PolyStarType? SyToPolystarType(double? sy)
|
||||
{
|
||||
if (sy == (int)sy)
|
||||
if (sy.HasValue)
|
||||
{
|
||||
switch ((int)sy)
|
||||
if (TryAsExactInt(sy.Value, out var intValue))
|
||||
{
|
||||
case 1: return Polystar.PolyStarType.Star;
|
||||
case 2: return Polystar.PolyStarType.Polygon;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return Polystar.PolyStarType.Star;
|
||||
case 2: return Polystar.PolyStarType.Polygon;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("PolyStartType", sy.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("PolyStartType", sy.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
GradientType TToGradientType(double t)
|
||||
GradientType TToGradientType(double? t)
|
||||
{
|
||||
if (t == (int)t)
|
||||
if (t.HasValue)
|
||||
{
|
||||
switch ((int)t)
|
||||
if (TryAsExactInt(t.Value, out var intValue))
|
||||
{
|
||||
case 1: return GradientType.Linear;
|
||||
case 2: return GradientType.Radial;
|
||||
switch (intValue)
|
||||
{
|
||||
case 1: return GradientType.Linear;
|
||||
case 2: return GradientType.Radial;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("GradientType", t.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("GradientType", t.ToString());
|
||||
return GradientType.Linear;
|
||||
}
|
||||
|
||||
Layer.MatteType TTToMatteType(double tt)
|
||||
Layer.MatteType TTToMatteType(double? tt)
|
||||
{
|
||||
if (tt == (int)tt)
|
||||
if (tt.HasValue)
|
||||
{
|
||||
switch ((int)tt)
|
||||
if (TryAsExactInt(tt.Value, out var intValue))
|
||||
{
|
||||
case 0: return Layer.MatteType.None;
|
||||
case 1: return Layer.MatteType.Add;
|
||||
case 2: return Layer.MatteType.Invert;
|
||||
switch (intValue)
|
||||
{
|
||||
case 0: return Layer.MatteType.None;
|
||||
case 1: return Layer.MatteType.Add;
|
||||
case 2: return Layer.MatteType.Invert;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("MatteType", tt.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("MatteType", tt.ToString());
|
||||
return Layer.MatteType.None;
|
||||
}
|
||||
|
||||
Layer.LayerType? TyToLayerType(double ty)
|
||||
Layer.LayerType? TyToLayerType(double? ty)
|
||||
{
|
||||
if (ty == (int)ty)
|
||||
if (ty.HasValue)
|
||||
{
|
||||
switch ((int)ty)
|
||||
if (TryAsExactInt(ty.Value, out var intValue))
|
||||
{
|
||||
case 0: return Layer.LayerType.PreComp;
|
||||
case 1: return Layer.LayerType.Solid;
|
||||
case 2: return Layer.LayerType.Image;
|
||||
case 3: return Layer.LayerType.Null;
|
||||
case 4: return Layer.LayerType.Shape;
|
||||
case 5: return Layer.LayerType.Text;
|
||||
switch (intValue)
|
||||
{
|
||||
case 0: return Layer.LayerType.PreComp;
|
||||
case 1: return Layer.LayerType.Solid;
|
||||
case 2: return Layer.LayerType.Image;
|
||||
case 3: return Layer.LayerType.Null;
|
||||
case 4: return Layer.LayerType.Shape;
|
||||
case 5: return Layer.LayerType.Text;
|
||||
}
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LayerType", ty.ToString());
|
||||
}
|
||||
|
||||
_issues.UnexpectedValueForType("LayerType", ty.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool TryAsExactInt(double value, out int intValue)
|
||||
{
|
||||
intValue = (int)value;
|
||||
return value == intValue;
|
||||
}
|
||||
|
||||
enum GradientType
|
||||
{
|
||||
Linear,
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
|
@ -16,14 +15,22 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
IList<Font> list = EmptyList<Font>.Singleton;
|
||||
|
||||
var fontsObject = JCObject.Load(ref reader, s_jsonLoadSettings);
|
||||
foreach (JCObject item in fontsObject.GetNamedArray("list"))
|
||||
var fontsObject = LottieJsonObjectElement.Load(this, ref reader, s_jsonLoadSettings);
|
||||
|
||||
foreach (var item in fontsObject.AsArrayProperty("list"))
|
||||
{
|
||||
var fName = item.GetNamedString("fName");
|
||||
var fFamily = item.GetNamedString("fFamily");
|
||||
var fStyle = item.GetNamedString("fStyle");
|
||||
var ascent = ReadFloat(item.GetNamedValue("ascent"));
|
||||
AssertAllFieldsRead(item);
|
||||
var element = item.AsObject();
|
||||
if (!element.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
obj.AssertAllPropertiesRead();
|
||||
if (list == EmptyList<Font>.Singleton)
|
||||
{
|
||||
list = new List<Font>();
|
||||
|
@ -32,7 +39,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
list.Add(new Font(fName, fFamily, fStyle, ascent));
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(fontsObject);
|
||||
fontsObject.AssertAllPropertiesRead();
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
|
|
@ -1,79 +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;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
sealed class JCArray : IList<JToken>
|
||||
{
|
||||
internal Newtonsoft.Json.Linq.JArray Wrapped { get; }
|
||||
|
||||
internal JCArray(Newtonsoft.Json.Linq.JArray wrapped)
|
||||
{
|
||||
Wrapped = wrapped;
|
||||
}
|
||||
|
||||
internal static JCArray Load(JsonReader reader, JsonLoadSettings settings)
|
||||
{
|
||||
return new JCArray(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 JCArray(Newtonsoft.Json.Linq.JArray value)
|
||||
{
|
||||
return value is null ? null : new JCArray(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
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(ref Reader reader, JsonLoadSettings settings)
|
||||
{
|
||||
return new JCObject(Newtonsoft.Json.Linq.JObject.Load(reader.NewtonsoftReader, 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +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 Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
static class JCObjectExtensions
|
||||
{
|
||||
internal static JToken GetNamedValue(this JCObject obj, string name, JToken defaultValue = null)
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? value : defaultValue;
|
||||
}
|
||||
|
||||
internal static string GetNamedString(this JCObject obj, string name, string defaultValue = "")
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? (string)value : defaultValue;
|
||||
}
|
||||
|
||||
internal static double GetNamedNumber(this JCObject obj, string name, double defaultValue = double.NaN)
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? (double)value : defaultValue;
|
||||
}
|
||||
|
||||
internal static JCArray GetNamedArray(this JCObject obj, string name, JCArray defaultValue = null)
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? value.AsArray() : defaultValue;
|
||||
}
|
||||
|
||||
internal static JCObject GetNamedObject(this JCObject obj, string name, JCObject defaultValue = null)
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? value.AsObject() : defaultValue;
|
||||
}
|
||||
|
||||
internal static bool GetNamedBoolean(this JCObject obj, string name, bool defaultValue = false)
|
||||
{
|
||||
return obj.TryGetValue(name, out JToken value) ? (bool)value : defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +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 Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
static class JTokenExtensions
|
||||
{
|
||||
internal static JCObject AsObject(this JToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (JCObject)token;
|
||||
}
|
||||
catch (InvalidCastException ex)
|
||||
{
|
||||
var exceptionString = ex.Message;
|
||||
if (!string.IsNullOrWhiteSpace(token.Path))
|
||||
{
|
||||
exceptionString += $" Failed to cast to correct type for token in path: {token.Path}.";
|
||||
}
|
||||
|
||||
throw new LottieCompositionReaderException(exceptionString, ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal static JCArray AsArray(this JToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (JCArray)token;
|
||||
}
|
||||
catch (InvalidCastException ex)
|
||||
{
|
||||
var exceptionString = ex.Message;
|
||||
if (!string.IsNullOrWhiteSpace(token.Path))
|
||||
{
|
||||
exceptionString += $" Failed to cast to correct type for token in path: {token.Path}.";
|
||||
}
|
||||
|
||||
throw new LottieCompositionReaderException(exceptionString, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,23 +12,21 @@ 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(JCObject.Load(ref reader, s_jsonLoadSettings));
|
||||
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 ReadLayer(JCObject obj)
|
||||
Layer ReadLayer(in LottieJsonObjectElement obj)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "bounds");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "sy");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "td");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("bounds", "sy", "td");
|
||||
|
||||
// Field 'hasMask' is deprecated and thus we are intentionally ignoring it
|
||||
IgnoreFieldIntentionally(obj, "hasMask");
|
||||
// Property 'hasMask' is deprecated and thus we are intentionally ignoring it.
|
||||
obj.IgnorePropertyIntentionally("hasMask");
|
||||
|
||||
var layerArgs = default(Layer.LayerArgs);
|
||||
|
||||
layerArgs.Name = ReadName(obj);
|
||||
var index = ReadInt(obj, "ind");
|
||||
var index = obj.Int32OrNullProperty("ind");
|
||||
|
||||
if (!index.HasValue)
|
||||
{
|
||||
|
@ -36,12 +34,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
layerArgs.Index = index.Value;
|
||||
layerArgs.Parent = ReadInt(obj, "parent");
|
||||
layerArgs.Is3d = ReadBool(obj, "ddd") == true;
|
||||
layerArgs.AutoOrient = ReadBool(obj, "ao") == true;
|
||||
layerArgs.BlendMode = BmToBlendMode(obj.GetNamedNumber("bm", 0));
|
||||
layerArgs.IsHidden = ReadBool(obj, "hd") == true;
|
||||
var render = ReadBool(obj, "render") != false;
|
||||
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;
|
||||
|
||||
if (!render)
|
||||
{
|
||||
|
@ -50,12 +48,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
// Warnings
|
||||
if (layerArgs.Name.EndsWith(".ai") || obj.GetNamedString("cl", string.Empty) == "ai")
|
||||
if (layerArgs.Name.EndsWith(".ai") || obj.StringOrNullProperty("cl") == "ai")
|
||||
{
|
||||
_issues.IllustratorLayers();
|
||||
}
|
||||
|
||||
if (obj.ContainsKey("ef"))
|
||||
if (obj.ContainsProperty("ef"))
|
||||
{
|
||||
_issues.LayerEffectsIsNotSupported(layerArgs.Name);
|
||||
}
|
||||
|
@ -65,112 +63,109 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
// ----------------------
|
||||
var shapeLayerContentArgs = default(ShapeLayerContent.ShapeLayerContentArgs);
|
||||
ReadShapeLayerContentArgs(obj, ref shapeLayerContentArgs);
|
||||
layerArgs.Transform = ReadTransform(obj.GetNamedObject("ks"), in shapeLayerContentArgs);
|
||||
layerArgs.Transform = ReadTransform(obj.ObjectOrNullProperty("ks"), in shapeLayerContentArgs);
|
||||
|
||||
// ------------------------------
|
||||
// Layer Animation
|
||||
// ------------------------------
|
||||
layerArgs.TimeStretch = obj.GetNamedNumber("sr", 1.0);
|
||||
layerArgs.TimeStretch = obj.DoubleOrNullProperty("sr") ?? 1;
|
||||
|
||||
// Time when the layer starts
|
||||
layerArgs.StartFrame = obj.GetNamedNumber("st");
|
||||
layerArgs.StartFrame = obj.DoubleOrNullProperty( "st") ?? double.NaN;
|
||||
|
||||
// Time when the layer becomes visible.
|
||||
layerArgs.InFrame = obj.GetNamedNumber("ip");
|
||||
layerArgs.OutFrame = obj.GetNamedNumber("op");
|
||||
layerArgs.InFrame = obj.DoubleOrNullProperty("ip") ?? double.NaN;
|
||||
layerArgs.OutFrame = obj.DoubleOrNullProperty( "op") ?? double.NaN;
|
||||
|
||||
// NOTE: The spec specifies this as 'maskProperties' but the BodyMovin tool exports
|
||||
// 'masksProperties' with the plural 'masks'.
|
||||
var maskProperties = obj.GetNamedArray("masksProperties", null);
|
||||
layerArgs.Masks = maskProperties != null ? ReadMaskProperties(maskProperties) : null;
|
||||
var maskProperties = obj.AsArrayProperty("masksProperties");
|
||||
layerArgs.Masks = maskProperties != null ? ReadMaskProperties(maskProperties.Value) : null;
|
||||
|
||||
layerArgs.LayerMatteType = TTToMatteType(obj.GetNamedNumber("tt", (double)Layer.MatteType.None));
|
||||
layerArgs.LayerMatteType = TTToMatteType(obj.DoubleOrNullProperty( "tt"));
|
||||
|
||||
Layer.LayerType? layerType = TyToLayerType(obj.GetNamedNumber("ty", double.NaN));
|
||||
Layer.LayerType? layerType = TyToLayerType(obj.DoubleOrNullProperty( "ty"));
|
||||
|
||||
if (!layerType.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (layerType)
|
||||
try
|
||||
{
|
||||
case Layer.LayerType.PreComp:
|
||||
{
|
||||
var refId = obj.GetNamedString("refId", string.Empty);
|
||||
var width = obj.GetNamedNumber("w");
|
||||
var height = obj.GetNamedNumber("h");
|
||||
var tm = obj.GetNamedObject("tm", null);
|
||||
if (tm != null)
|
||||
switch (layerType)
|
||||
{
|
||||
case Layer.LayerType.PreComp:
|
||||
{
|
||||
_issues.TimeRemappingOfPreComps();
|
||||
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");
|
||||
if (tm != null)
|
||||
{
|
||||
_issues.TimeRemappingOfPreComps();
|
||||
}
|
||||
|
||||
return new PreCompLayer(in layerArgs, refId, width, height);
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
return new PreCompLayer(in layerArgs, refId, width, height);
|
||||
}
|
||||
case Layer.LayerType.Solid:
|
||||
{
|
||||
var solidWidth = obj.Int32OrNullProperty("sw") ?? 0;
|
||||
var solidHeight = obj.Int32OrNullProperty("sh") ?? 0;
|
||||
var solidColor = ReadColorFromString(obj.StringOrNullProperty("sc") ?? string.Empty);
|
||||
return new SolidLayer(in layerArgs, solidWidth, solidHeight, solidColor);
|
||||
}
|
||||
|
||||
case Layer.LayerType.Solid:
|
||||
{
|
||||
var solidWidth = ReadInt(obj, "sw").Value;
|
||||
var solidHeight = ReadInt(obj, "sh").Value;
|
||||
var solidColor = ReadColorFromString(obj.GetNamedString("sc"));
|
||||
case Layer.LayerType.Image:
|
||||
{
|
||||
var refId = obj.StringOrNullProperty("refId") ?? string.Empty;
|
||||
return new ImageLayer(in layerArgs, refId);
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
return new SolidLayer(in layerArgs, solidWidth, solidHeight, solidColor);
|
||||
}
|
||||
case Layer.LayerType.Null:
|
||||
return new NullLayer(in layerArgs);
|
||||
|
||||
case Layer.LayerType.Image:
|
||||
{
|
||||
var refId = obj.GetNamedString("refId", string.Empty);
|
||||
case Layer.LayerType.Shape:
|
||||
{
|
||||
var shapes = ReadShapes(obj);
|
||||
return new ShapeLayer(in layerArgs, shapes);
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
return new ImageLayer(in layerArgs, refId);
|
||||
}
|
||||
case Layer.LayerType.Text:
|
||||
{
|
||||
// Text layer references an asset.
|
||||
var refId = obj.StringOrNullProperty("refId") ?? string.Empty;
|
||||
|
||||
case Layer.LayerType.Null:
|
||||
AssertAllFieldsRead(obj);
|
||||
return new NullLayer(in layerArgs);
|
||||
// Text data.
|
||||
ReadTextData(obj.ObjectOrNullProperty("t").Value);
|
||||
return new TextLayer(in layerArgs, refId);
|
||||
}
|
||||
|
||||
case Layer.LayerType.Shape:
|
||||
{
|
||||
var shapes = ReadShapes(obj);
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
return new ShapeLayer(in layerArgs, shapes);
|
||||
}
|
||||
|
||||
case Layer.LayerType.Text:
|
||||
{
|
||||
// Text layer references an asset.
|
||||
var refId = obj.GetNamedString("refId", string.Empty);
|
||||
|
||||
// Text data.
|
||||
ReadTextData(obj.GetNamedObject("t"));
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
return new TextLayer(in layerArgs, refId);
|
||||
}
|
||||
|
||||
default: throw Unreachable;
|
||||
default: throw Unreachable;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
obj.AssertAllPropertiesRead();
|
||||
}
|
||||
}
|
||||
|
||||
List<ShapeLayerContent> ReadShapes(JCObject obj)
|
||||
List<ShapeLayerContent> ReadShapes(in LottieJsonObjectElement obj)
|
||||
{
|
||||
return ReadShapesList(obj.GetNamedArray("shapes", null));
|
||||
return ReadShapesList(obj.AsArrayProperty("shapes"));
|
||||
}
|
||||
|
||||
List<ShapeLayerContent> ReadShapesList(JCArray shapesJson)
|
||||
List<ShapeLayerContent> ReadShapesList(in LottieJsonArrayElement? shapesJson)
|
||||
{
|
||||
var shapes = new List<ShapeLayerContent>();
|
||||
if (shapesJson != null)
|
||||
{
|
||||
var shapesJsonCount = shapesJson.Count;
|
||||
var shapesJsonCount = shapesJson.Value.Count;
|
||||
shapes.Capacity = shapesJsonCount;
|
||||
for (var i = 0; i < shapesJsonCount; i++)
|
||||
{
|
||||
var item = ReadShapeContent(shapesJson[i].AsObject());
|
||||
var item = ReadShapeContent(shapesJson.Value[i].AsObject().Value);
|
||||
if (item != null)
|
||||
{
|
||||
shapes.Add(item);
|
||||
|
@ -181,7 +176,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
return shapes;
|
||||
}
|
||||
|
||||
void ReadTextData(JCObject obj)
|
||||
void ReadTextData(in LottieJsonObjectElement obj)
|
||||
{
|
||||
// TODO - read text data
|
||||
|
||||
|
@ -197,33 +192,31 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
// "sc":strokeColor
|
||||
// "sw":strokeWidth
|
||||
// "of":(bool)strokeOverFill
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "d");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("d", "p", "m");
|
||||
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "p");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "m");
|
||||
|
||||
// Array of animatable text properties (fc:fill color, sc:stroke color, sw:stroke width, t:tracking (float))
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "a");
|
||||
AssertAllFieldsRead(obj);
|
||||
// Array of animatable text properties (fc:fill color, sc:stroke color, sw:stroke width, t:tracking (float)).
|
||||
obj.IgnorePropertyThatIsNotYetSupported("a");
|
||||
obj.AssertAllPropertiesRead();
|
||||
}
|
||||
|
||||
IEnumerable<Mask> ReadMaskProperties(JCArray array)
|
||||
IEnumerable<Mask> ReadMaskProperties(LottieJsonArrayElement array)
|
||||
{
|
||||
foreach (var elem in array)
|
||||
{
|
||||
var obj = elem.AsObject();
|
||||
var obj = elem.AsObject().Value;
|
||||
|
||||
// Ignoring field 'x' because it is not in the official spec
|
||||
// Ignoring property 'x' because it is not in the official spec.
|
||||
// The x property refers to the mask expansion. In AE you can
|
||||
// expand or shrink a mask getting a reduced or expanded version of the same shape.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "x");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("x");
|
||||
|
||||
var inverted = obj.GetNamedBoolean("inv");
|
||||
var inverted = obj.BoolOrNullProperty("inv") ?? false;
|
||||
var name = ReadName(obj);
|
||||
var animatedGeometry = ReadAnimatableGeometry(obj.GetNamedObject("pt"));
|
||||
var animatedGeometry = ReadAnimatableGeometry(obj.ObjectOrNullProperty("pt"));
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var mode = Mask.MaskMode.None;
|
||||
var maskMode = obj.GetNamedString("mode");
|
||||
var maskMode = obj.StringOrNullProperty("mode") ?? string.Empty;
|
||||
|
||||
Mask.MaskMode mode;
|
||||
switch (maskMode)
|
||||
{
|
||||
case "a":
|
||||
|
@ -252,7 +245,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
continue;
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
yield return new Mask(
|
||||
inverted,
|
||||
name,
|
||||
|
@ -271,9 +264,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
else
|
||||
{
|
||||
var index = 1; // Skip '#'
|
||||
var index = 1; // Skip '#'.
|
||||
|
||||
// '#AARRGGBB'
|
||||
// '#AARRGGBB'.
|
||||
byte a = 255;
|
||||
if (hex.Length == 9)
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Toolkit.Uwp.UI.Lottie.GenericData;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
@ -24,8 +24,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
sealed partial class LottieCompositionReader
|
||||
{
|
||||
static readonly Animatable<double> s_animatable_0 = new Animatable<double>(0, null);
|
||||
|
||||
static readonly JsonLoadSettings s_jsonLoadSettings = new JsonLoadSettings
|
||||
{
|
||||
// Ignore commands and line info. Not needed and makes the parser a bit faster.
|
||||
|
@ -147,30 +145,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.StartObject:
|
||||
case JsonToken.StartArray:
|
||||
case JsonToken.StartConstructor:
|
||||
case JsonToken.Raw:
|
||||
case JsonToken.Integer:
|
||||
case JsonToken.Float:
|
||||
case JsonToken.String:
|
||||
case JsonToken.Boolean:
|
||||
case JsonToken.Null:
|
||||
case JsonToken.Undefined:
|
||||
case JsonToken.EndArray:
|
||||
case JsonToken.EndConstructor:
|
||||
case JsonToken.Date:
|
||||
case JsonToken.Bytes:
|
||||
// Here means the JSON was invalid or our parser got confused. There is no way to
|
||||
// recover from this, so throw.
|
||||
throw UnexpectedTokenException(ref reader);
|
||||
|
||||
case JsonToken.Comment:
|
||||
case JsonTokenType.Comment:
|
||||
// Ignore comments.
|
||||
ConsumeToken(ref reader);
|
||||
break;
|
||||
|
||||
case JsonToken.PropertyName:
|
||||
case JsonTokenType.PropertyName:
|
||||
var currentProperty = reader.GetString();
|
||||
|
||||
ConsumeToken(ref reader);
|
||||
|
@ -184,10 +164,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
chars = ParseArray(ref reader, ParseChar);
|
||||
break;
|
||||
case "ddd":
|
||||
is3d = ParseBool(ref reader);
|
||||
is3d = reader.ParseBool();
|
||||
break;
|
||||
case "fr":
|
||||
framesPerSecond = ParseDouble(ref reader);
|
||||
framesPerSecond = reader.ParseDouble();
|
||||
break;
|
||||
case "fonts":
|
||||
fonts = ParseFonts(ref reader);
|
||||
|
@ -196,13 +176,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
layers = ParseArray(ref reader, ParseLayer);
|
||||
break;
|
||||
case "h":
|
||||
height = ParseDouble(ref reader);
|
||||
height = reader.ParseDouble();
|
||||
break;
|
||||
case "ip":
|
||||
inPoint = ParseDouble(ref reader);
|
||||
inPoint = reader.ParseDouble();
|
||||
break;
|
||||
case "op":
|
||||
outPoint = ParseDouble(ref reader);
|
||||
outPoint = reader.ParseDouble();
|
||||
break;
|
||||
case "markers":
|
||||
markers = ParseArray(ref reader, ParseMarker);
|
||||
|
@ -214,7 +194,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
version = reader.GetString();
|
||||
break;
|
||||
case "w":
|
||||
width = ParseDouble(ref reader);
|
||||
width = reader.ParseDouble();
|
||||
break;
|
||||
|
||||
// Treat any other property as an extension of the BodyMovin format.
|
||||
|
@ -231,9 +211,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
|
||||
break;
|
||||
|
||||
case JsonToken.EndObject:
|
||||
case JsonTokenType.EndObject:
|
||||
{
|
||||
// Check that the required fields were found. If any are missing, throw.
|
||||
// Check that the required properties were found. If any are missing, throw.
|
||||
if (version is null)
|
||||
{
|
||||
throw Exception("Version parameter not found.", ref reader);
|
||||
|
@ -298,6 +278,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
return result;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
@ -306,64 +288,58 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
throw EofException;
|
||||
}
|
||||
|
||||
string ReadName(JCObject obj)
|
||||
string ReadName(in LottieJsonObjectElement obj)
|
||||
{
|
||||
if (_options.HasFlag(Options.IgnoreNames))
|
||||
{
|
||||
IgnoreFieldIntentionally(obj, "nm");
|
||||
obj.IgnorePropertyIntentionally("nm");
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.GetNamedString("nm", string.Empty);
|
||||
return obj.StringOrNullProperty("nm") ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
string ReadMatchName(JCObject obj)
|
||||
string ReadMatchName(in LottieJsonObjectElement obj)
|
||||
{
|
||||
if (_options.HasFlag(Options.IgnoreMatchNames))
|
||||
{
|
||||
IgnoreFieldIntentionally(obj, "mn");
|
||||
obj.IgnorePropertyIntentionally("mn");
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.GetNamedString("mn", string.Empty);
|
||||
return obj.StringOrNullProperty("mn") ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
Animatable<Color> ReadColorFromC(JCObject obj) =>
|
||||
ReadAnimatableColor(obj.GetNamedObject("c", null));
|
||||
Animatable<Color> ReadColorFromC(in LottieJsonObjectElement obj)
|
||||
=> ReadAnimatableColor(obj.ObjectOrNullProperty("c"));
|
||||
|
||||
Animatable<Opacity> ReadOpacityFromO(JCObject obj)
|
||||
Animatable<Opacity> ReadOpacityFromO(in LottieJsonObjectElement obj)
|
||||
=> ReadOpacityFromObject(obj.ObjectOrNullProperty("o"));
|
||||
|
||||
Animatable<Opacity> ReadOpacityFromObject(in LottieJsonObjectElement? obj)
|
||||
{
|
||||
var jsonOpacity = obj.GetNamedObject("o", null);
|
||||
return ReadOpacityFromObject(jsonOpacity);
|
||||
return ReadAnimatableOpacity(obj);
|
||||
}
|
||||
|
||||
Animatable<Opacity> ReadOpacityFromObject(JCObject obj)
|
||||
static PathGeometry ParseGeometry(in LottieJsonElement element)
|
||||
{
|
||||
var result = obj != null
|
||||
? ReadAnimatableOpacity(obj)
|
||||
: new Animatable<Opacity>(Opacity.Opaque, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PathGeometry ReadGeometry(JToken value)
|
||||
{
|
||||
JCObject pointsData = null;
|
||||
if (value.Type == JTokenType.Array)
|
||||
LottieJsonObjectElement? pointsData = null;
|
||||
if (element.Kind == JsonValueKind.Array)
|
||||
{
|
||||
var firstItem = value.AsArray().First();
|
||||
var firstItemAsObject = firstItem.AsObject();
|
||||
if (firstItem.Type == JTokenType.Object && firstItemAsObject.ContainsKey("v"))
|
||||
var firstItem = element.AsArray()?[0];
|
||||
var firstItemAsObject = firstItem?.AsObject();
|
||||
if (firstItemAsObject?.ContainsProperty("v") == true)
|
||||
{
|
||||
pointsData = firstItemAsObject;
|
||||
}
|
||||
}
|
||||
else if (value.Type == JTokenType.Object && value.AsObject().ContainsKey("v"))
|
||||
else if (element.AsObject()?.ContainsProperty("v") == true)
|
||||
{
|
||||
pointsData = value.AsObject();
|
||||
pointsData = element.AsObject();
|
||||
}
|
||||
|
||||
if (pointsData is null)
|
||||
|
@ -371,31 +347,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
return null;
|
||||
}
|
||||
|
||||
var vertices = pointsData.GetNamedArray("v", null);
|
||||
var inTangents = pointsData.GetNamedArray("i", null);
|
||||
var outTangents = pointsData.GetNamedArray("o", null);
|
||||
var isClosed = pointsData.GetNamedBoolean("c", false);
|
||||
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;
|
||||
|
||||
if (vertices is null || inTangents is null || outTangents is null)
|
||||
{
|
||||
throw new LottieCompositionReaderException($"Unable to process points array or tangents. {pointsData}");
|
||||
throw new LottieCompositionReaderException($"Unable to process points array or tangents. {points}");
|
||||
}
|
||||
|
||||
var beziers = new BezierSegment[isClosed ? vertices.Count : Math.Max(vertices.Count - 1, 0)];
|
||||
var beziers = new BezierSegment[isClosed ? vertices.Value.Count : Math.Max(vertices.Value.Count - 1, 0)];
|
||||
|
||||
if (beziers.Length > 0)
|
||||
{
|
||||
// The vertices for the figure.
|
||||
var verticesAsVector2 = ReadVector2Array(vertices);
|
||||
var verticesAsVector2 = ReadArrayOfVector2(vertices.Value);
|
||||
|
||||
// The control points that define the cubic beziers between the vertices.
|
||||
var inTangentsAsVector2 = ReadVector2Array(inTangents);
|
||||
var outTangentsAsVector2 = ReadVector2Array(outTangents);
|
||||
var inTangentsAsVector2 = ReadArrayOfVector2(inTangents.Value);
|
||||
var outTangentsAsVector2 = ReadArrayOfVector2(outTangents.Value);
|
||||
|
||||
if (verticesAsVector2.Length != inTangentsAsVector2.Length ||
|
||||
verticesAsVector2.Length != outTangentsAsVector2.Length)
|
||||
{
|
||||
throw new LottieCompositionReaderException($"Invalid path data. {pointsData}");
|
||||
throw new LottieCompositionReaderException($"Invalid path data. {points}");
|
||||
}
|
||||
|
||||
var cp3 = verticesAsVector2[0];
|
||||
|
@ -425,45 +403,37 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
return new PathGeometry(beziers);
|
||||
}
|
||||
|
||||
static Opacity ReadOpacity(JToken jsonValue) => Opacity.FromPercent(ReadFloat(jsonValue));
|
||||
|
||||
static Rotation ReadRotation(JToken jsonValue) => Rotation.FromDegrees(ReadFloat(jsonValue));
|
||||
|
||||
static Trim ReadTrim(JToken jsonValue) => Trim.FromPercent(ReadFloat(jsonValue));
|
||||
|
||||
// Indicates that the given field will not be read because we don't yet support it.
|
||||
void IgnoreFieldThatIsNotYetSupported(JCObject obj, string fieldName)
|
||||
static Vector2[] ReadArrayOfVector2(in LottieJsonArrayElement array)
|
||||
{
|
||||
obj.ReadFields.Add(fieldName);
|
||||
}
|
||||
|
||||
// Indicates that the given field is not read because we don't need to read it.
|
||||
void IgnoreFieldIntentionally(JCObject obj, string fieldName)
|
||||
{
|
||||
obj.ReadFields.Add(fieldName);
|
||||
}
|
||||
|
||||
// Reports an issue if the given JsonObject has fields that were not read.
|
||||
void AssertAllFieldsRead(JCObject obj, [CallerMemberName]string memberName = "")
|
||||
{
|
||||
var read = obj.ReadFields;
|
||||
var unread = new List<string>();
|
||||
foreach (var pair in obj)
|
||||
var len = array.Count;
|
||||
var result = new Vector2[len];
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (!read.Contains(pair.Key))
|
||||
{
|
||||
unread.Add(pair.Key);
|
||||
}
|
||||
result[i] = array[i].AsArray()?.AsVector2() ?? default(Vector2);
|
||||
}
|
||||
|
||||
unread.Sort();
|
||||
foreach (var unreadField in unread)
|
||||
return result;
|
||||
}
|
||||
|
||||
static Opacity ParseOpacity(in LottieJsonElement jsonValue) => Opacity.FromPercent(jsonValue.AsDouble() ?? 0);
|
||||
|
||||
static Rotation ParseRotation(in LottieJsonElement jsonValue) => Rotation.FromDegrees(jsonValue.AsDouble() ?? 0);
|
||||
|
||||
static Trim ParseTrim(in LottieJsonElement jsonValue) => Trim.FromPercent(jsonValue.AsDouble() ?? 0);
|
||||
|
||||
static Vector3 ParseVector3(in LottieJsonElement jsonValue)
|
||||
{
|
||||
switch (jsonValue.Kind)
|
||||
{
|
||||
_issues.IgnoredField($"{memberName}.{unreadField}");
|
||||
case JsonValueKind.Object:
|
||||
return jsonValue.AsObject()?.AsVector3() ?? Vector3.Zero;
|
||||
case JsonValueKind.Array:
|
||||
return jsonValue.AsArray()?.AsVector3() ?? Vector3.Zero;
|
||||
default: return default(Vector3);
|
||||
}
|
||||
}
|
||||
|
||||
static void ExpectToken(ref Reader reader, JsonToken token)
|
||||
static void ExpectToken(ref Reader reader, JsonTokenType token)
|
||||
{
|
||||
if (reader.TokenType != token)
|
||||
{
|
||||
|
@ -475,7 +445,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
|
||||
T[] ParseArray<T>(ref Reader reader, Parser<T> parser)
|
||||
{
|
||||
ExpectToken(ref reader, JsonToken.StartArray);
|
||||
ExpectToken(ref reader, JsonTokenType.StartArray);
|
||||
|
||||
IList<T> list = EmptyList<T>.Singleton;
|
||||
|
||||
|
@ -483,7 +453,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.StartObject:
|
||||
case JsonTokenType.StartObject:
|
||||
var result = parser(ref reader);
|
||||
if (result != null)
|
||||
{
|
||||
|
@ -497,7 +467,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
|
||||
break;
|
||||
|
||||
case JsonToken.EndArray:
|
||||
case JsonTokenType.EndArray:
|
||||
return list.ToArray();
|
||||
|
||||
default:
|
||||
|
@ -523,7 +493,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
// 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(JTokenType tokenType) => Exception($"Unexpected token: {tokenType}");
|
||||
static LottieCompositionReaderException UnexpectedTokenException(JsonValueKind kind) => Exception($"Unexpected token: {kind}");
|
||||
|
||||
static LottieCompositionReaderException Exception(string message, ref Reader reader) => Exception($"{message} @ {reader.NewtonsoftReader.Path}");
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
// 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.Diagnostics;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
internal readonly struct LottieJsonArrayElement
|
||||
{
|
||||
readonly LottieCompositionReader _owner;
|
||||
readonly JArray _wrapped;
|
||||
|
||||
internal LottieJsonArrayElement(LottieCompositionReader owner, JToken wrapped)
|
||||
{
|
||||
Debug.Assert(wrapped.Type == JTokenType.Array, "Precondition");
|
||||
_owner = owner;
|
||||
_wrapped = (JArray)wrapped;
|
||||
}
|
||||
|
||||
internal int Count => _wrapped.Count;
|
||||
|
||||
public LottieJsonElement this[int index] => new LottieJsonElement(_owner, _wrapped[index]);
|
||||
|
||||
public Vector2? AsVector2()
|
||||
{
|
||||
double? x = null;
|
||||
double? y = null;
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
x = this[0].AsDouble();
|
||||
break;
|
||||
case 1:
|
||||
y = this[1].AsDouble();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return x is null
|
||||
? (Vector2?)null
|
||||
: new Vector2(x.Value, y ?? 0);
|
||||
}
|
||||
|
||||
public Vector3? AsVector3()
|
||||
{
|
||||
double? x = null;
|
||||
double? y = null;
|
||||
double? z = null;
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
x = this[0].AsDouble();
|
||||
break;
|
||||
case 1:
|
||||
y = this[1].AsDouble();
|
||||
break;
|
||||
case 2:
|
||||
z = this[2].AsDouble();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return x is null
|
||||
? (Vector3?)null
|
||||
: new Vector3(x.Value, y ?? 0, z ?? 0);
|
||||
}
|
||||
|
||||
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 struct Enumerator
|
||||
{
|
||||
readonly LottieJsonArrayElement _owner;
|
||||
int _currentIndex;
|
||||
|
||||
internal Enumerator(LottieJsonArrayElement owner)
|
||||
{
|
||||
_owner = owner;
|
||||
_currentIndex = -1;
|
||||
}
|
||||
|
||||
public LottieJsonElement Current => new LottieJsonElement(_owner._owner, _owner._wrapped[_currentIndex]);
|
||||
|
||||
public bool MoveNext() => _owner._wrapped.Count > ++_currentIndex;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
// 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;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
internal delegate T LottieJsonElementReader<T>(in LottieJsonElement element);
|
||||
|
||||
internal readonly struct LottieJsonElement
|
||||
{
|
||||
readonly LottieCompositionReader _owner;
|
||||
readonly JToken _wrapped;
|
||||
|
||||
internal LottieJsonElement(LottieCompositionReader owner, JToken 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal LottieJsonArrayElement? AsArray()
|
||||
=> _wrapped.Type == JTokenType.Array
|
||||
? new LottieJsonArrayElement(_owner, _wrapped)
|
||||
: (LottieJsonArrayElement?)null;
|
||||
|
||||
internal LottieJsonObjectElement? AsObject()
|
||||
=> _wrapped.Type == JTokenType.Object
|
||||
? new LottieJsonObjectElement(_owner, _wrapped)
|
||||
: (LottieJsonObjectElement?)null;
|
||||
|
||||
internal bool? AsBoolean()
|
||||
{
|
||||
switch (_wrapped.Type)
|
||||
{
|
||||
case JTokenType.Boolean:
|
||||
return (bool)_wrapped;
|
||||
default:
|
||||
var number = AsDouble();
|
||||
return number.HasValue
|
||||
? number != 0
|
||||
: (bool?)null;
|
||||
}
|
||||
}
|
||||
|
||||
internal double? AsDouble()
|
||||
{
|
||||
switch (_wrapped.Type)
|
||||
{
|
||||
case JTokenType.Float:
|
||||
case JTokenType.Integer:
|
||||
return (double)_wrapped;
|
||||
case JTokenType.String:
|
||||
return double.TryParse((string)_wrapped, out var result)
|
||||
? result
|
||||
: (double?)null;
|
||||
|
||||
case JTokenType.Array:
|
||||
{
|
||||
var array = AsArray().Value;
|
||||
switch (array.Count)
|
||||
{
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return array[0].AsDouble();
|
||||
default:
|
||||
// Some Lottie files have multiple values in arrays that should only have one. Just
|
||||
// take the first value.
|
||||
return array[0].AsDouble();
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal int? AsInt32()
|
||||
{
|
||||
var value = AsDouble();
|
||||
if (value.HasValue)
|
||||
{
|
||||
var intValue = unchecked((int)Math.Round(value.Value));
|
||||
if (intValue == value.Value)
|
||||
{
|
||||
return intValue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string AsString()
|
||||
{
|
||||
switch (_wrapped.Type)
|
||||
{
|
||||
case JTokenType.String:
|
||||
return (string)_wrapped;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
internal readonly struct LottieJsonObjectElement
|
||||
{
|
||||
readonly LottieCompositionReader _owner;
|
||||
readonly JObject _wrapped;
|
||||
readonly (bool unread, string propertyName)[] _propertyNames;
|
||||
|
||||
internal LottieJsonObjectElement(LottieCompositionReader owner, JToken wrapped)
|
||||
{
|
||||
Debug.Assert(wrapped.Type == JTokenType.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();
|
||||
|
||||
// Sort the names so they can be binary searched.
|
||||
Array.Sort(_propertyNames, Comparer.Instance);
|
||||
}
|
||||
|
||||
public Vector3? AsVector3()
|
||||
{
|
||||
var x = DoubleOrNullProperty("x");
|
||||
var y = DoubleOrNullProperty("y");
|
||||
var z = DoubleOrNullProperty("z");
|
||||
return x is null
|
||||
? (Vector3?)null
|
||||
: new Vector3(x.Value, y ?? 0, z ?? 0);
|
||||
}
|
||||
|
||||
internal LottieJsonArrayElement? AsArrayProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out var value) ? value.AsArray() : null;
|
||||
|
||||
internal bool? BoolOrNullProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out var value) ? value.AsBoolean() : null;
|
||||
|
||||
internal double? DoubleOrNullProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out var value) ? value.AsDouble() : null;
|
||||
|
||||
internal int? Int32OrNullProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out var value) ? value.AsInt32() : null;
|
||||
|
||||
internal LottieJsonObjectElement? ObjectOrNullProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out var value) ? value.AsObject() : null;
|
||||
|
||||
internal string StringOrNullProperty(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.
|
||||
internal void IgnorePropertyThatIsNotYetSupported(string propertyName)
|
||||
=> MarkPropertyAsRead(propertyName);
|
||||
|
||||
internal void IgnorePropertyThatIsNotYetSupported(
|
||||
string propertyName1,
|
||||
string propertyName2)
|
||||
{
|
||||
MarkPropertyAsRead(propertyName1);
|
||||
MarkPropertyAsRead(propertyName2);
|
||||
}
|
||||
|
||||
internal void IgnorePropertyThatIsNotYetSupported(
|
||||
string propertyName1,
|
||||
string propertyName2,
|
||||
string propertyName3)
|
||||
{
|
||||
MarkPropertyAsRead(propertyName1);
|
||||
MarkPropertyAsRead(propertyName2);
|
||||
MarkPropertyAsRead(propertyName3);
|
||||
}
|
||||
|
||||
internal void IgnorePropertyThatIsNotYetSupported(
|
||||
string propertyName1,
|
||||
string propertyName2,
|
||||
string propertyName3,
|
||||
string propertyName4)
|
||||
{
|
||||
MarkPropertyAsRead(propertyName1);
|
||||
MarkPropertyAsRead(propertyName2);
|
||||
MarkPropertyAsRead(propertyName3);
|
||||
MarkPropertyAsRead(propertyName4);
|
||||
}
|
||||
|
||||
internal void IgnorePropertyThatIsNotYetSupported(
|
||||
string propertyName1,
|
||||
string propertyName2,
|
||||
string propertyName3,
|
||||
string propertyName4,
|
||||
string propertyName5)
|
||||
{
|
||||
MarkPropertyAsRead(propertyName1);
|
||||
MarkPropertyAsRead(propertyName2);
|
||||
MarkPropertyAsRead(propertyName3);
|
||||
MarkPropertyAsRead(propertyName4);
|
||||
MarkPropertyAsRead(propertyName5);
|
||||
}
|
||||
|
||||
// Indicates that the given property is not read because we don't need to read it.
|
||||
internal void IgnorePropertyIntentionally(string propertyName)
|
||||
=> MarkPropertyAsRead(propertyName);
|
||||
|
||||
internal bool ContainsProperty(string propertyName)
|
||||
=> TryGetProperty(propertyName, out _);
|
||||
|
||||
internal bool TryGetProperty(string propertyName, out LottieJsonElement value)
|
||||
{
|
||||
if (_wrapped.TryGetValue(propertyName, out var jtokenValue))
|
||||
{
|
||||
MarkPropertyAsRead(propertyName);
|
||||
value = new LottieJsonElement(_owner, jtokenValue);
|
||||
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)
|
||||
{
|
||||
if (pair.unread)
|
||||
{
|
||||
_owner._issues.IgnoredField($"{memberName}.{pair.propertyName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator() => new Enumerator(this, _wrapped);
|
||||
|
||||
// Marks the given property name as being read.
|
||||
void MarkPropertyAsRead(string propertyName)
|
||||
{
|
||||
int min = 0;
|
||||
int max = _propertyNames.Length - 1;
|
||||
while (min <= max)
|
||||
{
|
||||
int mid = (min + max) / 2;
|
||||
var current = _propertyNames[mid].propertyName;
|
||||
if (propertyName == current)
|
||||
{
|
||||
_propertyNames[mid].unread = false;
|
||||
return;
|
||||
}
|
||||
else if (string.CompareOrdinal(propertyName, current) < 0)
|
||||
{
|
||||
// Look left.
|
||||
max = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look right.
|
||||
min = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found.
|
||||
}
|
||||
|
||||
sealed class Comparer : IComparer<(bool, string)>
|
||||
{
|
||||
internal static readonly Comparer Instance = new Comparer();
|
||||
|
||||
int IComparer<(bool, string)>.Compare((bool, string) x, (bool, string) y)
|
||||
=> string.CompareOrdinal(x.Item2, y.Item2);
|
||||
}
|
||||
|
||||
public struct Enumerator
|
||||
{
|
||||
readonly LottieJsonObjectElement _owner;
|
||||
readonly IEnumerator<KeyValuePair<string, JToken>> _wrapped;
|
||||
|
||||
internal Enumerator(LottieJsonObjectElement owner, JObject wrapped)
|
||||
{
|
||||
_owner = owner;
|
||||
_wrapped = wrapped.GetEnumerator();
|
||||
}
|
||||
|
||||
public KeyValuePair<string, LottieJsonElement> Current
|
||||
=> new KeyValuePair<string, LottieJsonElement>(
|
||||
_wrapped.Current.Key,
|
||||
new LottieJsonElement(_owner._owner, _wrapped.Current.Value));
|
||||
|
||||
public void Dispose() => _wrapped.Dispose();
|
||||
|
||||
public bool MoveNext() => _wrapped.MoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +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 Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
Marker ParseMarker(ref Reader reader)
|
||||
{
|
||||
ExpectToken(ref reader, JsonToken.StartObject);
|
||||
ExpectToken(ref reader, JsonTokenType.StartObject);
|
||||
|
||||
string name = null;
|
||||
double durationMilliseconds = 0;
|
||||
|
@ -22,7 +22,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
case JsonTokenType.PropertyName:
|
||||
var currentProperty = reader.GetString();
|
||||
|
||||
ConsumeToken(ref reader);
|
||||
|
@ -33,10 +33,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
name = reader.GetString();
|
||||
break;
|
||||
case "dr":
|
||||
durationMilliseconds = ParseDouble(ref reader);
|
||||
durationMilliseconds = reader.ParseDouble();
|
||||
break;
|
||||
case "tm":
|
||||
frame = ParseDouble(ref reader);
|
||||
frame = reader.ParseDouble();
|
||||
break;
|
||||
default:
|
||||
_issues.IgnoredField(currentProperty);
|
||||
|
@ -45,7 +45,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
case JsonTokenType.EndObject:
|
||||
return new Marker(name: name, frame: frame, durationMilliseconds: durationMilliseconds);
|
||||
default:
|
||||
throw UnexpectedTokenException(ref reader);
|
||||
|
|
|
@ -1,223 +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 Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
static bool ParseBool(ref Reader reader)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
return reader.GetInt64() != 0;
|
||||
case JsonToken.Float:
|
||||
return reader.GetDouble() != 0;
|
||||
case JsonToken.Boolean:
|
||||
return reader.GetBoolean();
|
||||
default:
|
||||
throw Exception($"Expected a bool, but got {reader.TokenType}", ref reader);
|
||||
}
|
||||
}
|
||||
|
||||
static double ParseDouble(ref Reader reader)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
return reader.GetInt64();
|
||||
case JsonToken.Float:
|
||||
return reader.GetDouble();
|
||||
case JsonToken.String:
|
||||
if (double.TryParse(reader.GetString(), out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
throw Exception($"Expected a number, but got {reader.TokenType}", ref reader);
|
||||
}
|
||||
|
||||
static int ParseInt(ref Reader reader)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
return checked((int)reader.GetInt64());
|
||||
case JsonToken.Float:
|
||||
return checked((int)(long)Math.Round(reader.GetDouble()));
|
||||
case JsonToken.String:
|
||||
if (double.TryParse(reader.GetString(), out var result))
|
||||
{
|
||||
return checked((int)(long)Math.Round(result));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
throw Exception($"Expected a number, but got {reader.TokenType}", ref reader);
|
||||
}
|
||||
|
||||
static bool? ReadBool(JCObject obj, string name)
|
||||
{
|
||||
if (!obj.ContainsKey(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = obj.GetNamedValue(name);
|
||||
|
||||
switch (value.Type)
|
||||
{
|
||||
case JTokenType.Boolean:
|
||||
return obj.GetNamedBoolean(name);
|
||||
case JTokenType.Integer:
|
||||
case JTokenType.Float:
|
||||
return ReadInt(obj, name)?.Equals(1);
|
||||
case JTokenType.Null:
|
||||
// Treat a missing value as false.
|
||||
return false;
|
||||
case JTokenType.String:
|
||||
case JTokenType.Array:
|
||||
case JTokenType.Object:
|
||||
default:
|
||||
throw UnexpectedTokenException(value.Type);
|
||||
}
|
||||
}
|
||||
|
||||
static double ReadFloat(JToken jsonValue)
|
||||
{
|
||||
switch (jsonValue.Type)
|
||||
{
|
||||
case JTokenType.Float:
|
||||
case JTokenType.Integer:
|
||||
return (double)jsonValue;
|
||||
case JTokenType.Array:
|
||||
{
|
||||
var array = jsonValue.AsArray();
|
||||
switch (array.Count)
|
||||
{
|
||||
case 0:
|
||||
throw UnexpectedTokenException(jsonValue.Type);
|
||||
case 1:
|
||||
return (double)array[0];
|
||||
default:
|
||||
// Some Lottie files have multiple values in arrays that should only have one. Just
|
||||
// take the first value.
|
||||
return (double)array[0];
|
||||
}
|
||||
}
|
||||
|
||||
case JTokenType.Null:
|
||||
// Treat a missing value as 0.
|
||||
return 0.0;
|
||||
|
||||
case JTokenType.Boolean:
|
||||
case JTokenType.String:
|
||||
case JTokenType.Object:
|
||||
default:
|
||||
throw UnexpectedTokenException(jsonValue.Type);
|
||||
}
|
||||
}
|
||||
|
||||
static int? ReadInt(JCObject obj, string name)
|
||||
{
|
||||
var value = obj.GetNamedNumber(name, double.NaN);
|
||||
if (double.IsNaN(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Newtonsoft has its own casting logic so to bypass this, we first cast to a double and then round
|
||||
// because the desired behavior is to round doubles to the nearest value.
|
||||
var intValue = unchecked((int)Math.Round((double)value));
|
||||
if (value != intValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
static Vector2[] ReadVector2Array(JCArray array)
|
||||
{
|
||||
IEnumerable<Vector2> ToVector2Enumerable()
|
||||
{
|
||||
var count = array.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return ReadVector2FromJsonArray(array[i].AsArray());
|
||||
}
|
||||
}
|
||||
|
||||
return ToVector2Enumerable().ToArray();
|
||||
}
|
||||
|
||||
static Vector2 ReadVector2FromJsonArray(JCArray array)
|
||||
{
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
int i = 0;
|
||||
var count = array.Count;
|
||||
for (; i < count; i++)
|
||||
{
|
||||
// NOTE: indexing JsonArray is faster than enumerating it.
|
||||
var number = (double)array[i];
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
x = number;
|
||||
break;
|
||||
case 1:
|
||||
y = number;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow any number of values to be specified. Assume 0 for any missing values.
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
static Vector3 ReadVector3(JToken jsonValue) => ReadVector3FromJsonArray(jsonValue.AsArray());
|
||||
|
||||
static Vector3 ReadVector3FromJsonArray(JCArray array)
|
||||
{
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double z = 0;
|
||||
int i = 0;
|
||||
var count = array.Count;
|
||||
for (; i < count; i++)
|
||||
{
|
||||
// NOTE: indexing JsonArray is faster than enumerating it.
|
||||
var number = (double)array[i];
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
x = number;
|
||||
break;
|
||||
case 1:
|
||||
y = number;
|
||||
break;
|
||||
case 2:
|
||||
z = number;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow any number of values to be specified. Assume 0 for any missing values.
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
||||
|
@ -19,7 +19,46 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
_reader = reader;
|
||||
}
|
||||
|
||||
internal JsonToken TokenType => _reader.TokenType;
|
||||
internal JsonTokenType TokenType
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_reader.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Read() => _reader.Read();
|
||||
|
||||
|
@ -33,6 +72,61 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
|
||||
internal string GetString() => (string)_reader.Value;
|
||||
|
||||
internal bool ParseBool()
|
||||
{
|
||||
switch (_reader.TokenType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
throw new LottieCompositionReaderException($"Expected a number, but got {_reader.TokenType}");
|
||||
}
|
||||
|
||||
internal int ParseInt()
|
||||
{
|
||||
switch (_reader.TokenType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
throw new LottieCompositionReaderException($"Expected a number, but got {_reader.TokenType}");
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -11,12 +11,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
#pragma warning disable SA1601 // Partial elements should be documented
|
||||
sealed partial class LottieCompositionReader
|
||||
{
|
||||
ShapeLayerContent ReadShapeContent(JCObject obj)
|
||||
ShapeLayerContent ReadShapeContent(in LottieJsonObjectElement obj)
|
||||
{
|
||||
var args = default(ShapeLayerContent.ShapeLayerContentArgs);
|
||||
ReadShapeLayerContentArgs(obj, ref args);
|
||||
|
||||
var type = obj.GetNamedString("ty");
|
||||
var type = obj.StringOrNullProperty("ty") ?? string.Empty;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -54,68 +54,72 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
ShapeGroup ReadShapeGroup(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
ShapeGroup ReadShapeGroup(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "cix");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "cl");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ix");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("cix", "cl", "ix", "hd");
|
||||
|
||||
var numberOfProperties = ReadInt(obj, "np");
|
||||
var items = ReadShapesList(obj.GetNamedArray("it", null));
|
||||
AssertAllFieldsRead(obj);
|
||||
var numberOfProperties = obj.Int32OrNullProperty("np");
|
||||
var items = ReadShapesList(obj.AsArrayProperty("it"));
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new ShapeGroup(in shapeLayerContentArgs, items);
|
||||
}
|
||||
|
||||
void ReadShapeLayerContentArgs(JCObject obj, ref ShapeLayerContent.ShapeLayerContentArgs args)
|
||||
void ReadShapeLayerContentArgs(
|
||||
in LottieJsonObjectElement obj,
|
||||
ref ShapeLayerContent.ShapeLayerContentArgs args)
|
||||
{
|
||||
args.Name = ReadName(obj);
|
||||
args.MatchName = ReadMatchName(obj);
|
||||
args.BlendMode = BmToBlendMode(obj.GetNamedNumber("bm", 0));
|
||||
args.BlendMode = BmToBlendMode(obj.DoubleOrNullProperty("bm"));
|
||||
}
|
||||
|
||||
// "st"
|
||||
SolidColorStroke ReadSolidColorStroke(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
SolidColorStroke ReadSolidColorStroke(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "fillEnabled");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// 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.GetNamedObject("w"));
|
||||
var capType = LcToLineCapType(obj.GetNamedNumber("lc"));
|
||||
var joinType = LjToLineJoinType(obj.GetNamedNumber("lj"));
|
||||
var miterLimit = obj.GetNamedNumber("ml", 4); // Default miter limit in After Effects is 4
|
||||
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
|
||||
|
||||
// Get dash pattern to be set as StrokeDashArray
|
||||
Animatable<double> offset = null;
|
||||
var dashPattern = new List<double>();
|
||||
var dashesJson = obj.GetNamedArray("d", null);
|
||||
if (dashesJson != null)
|
||||
var dashes = obj.AsArrayProperty("d");
|
||||
if (dashes != null)
|
||||
{
|
||||
for (int i = 0; i < dashesJson.Count; i++)
|
||||
{
|
||||
var dashObj = dashesJson[i].AsObject();
|
||||
var dashesArray = dashes.Value;
|
||||
|
||||
switch (dashObj.GetNamedString("n"))
|
||||
for (int i = 0; i < dashesArray.Count; i++)
|
||||
{
|
||||
var dashObj = dashesArray[i].AsObject();
|
||||
|
||||
switch (dashObj?.StringOrNullProperty("n"))
|
||||
{
|
||||
case "o":
|
||||
offset = ReadAnimatableFloat(dashObj.GetNamedObject("v"));
|
||||
offset = ReadAnimatableFloat(dashObj?.ObjectOrNullProperty("v"));
|
||||
break;
|
||||
case "d":
|
||||
case "g":
|
||||
dashPattern.Add(ReadAnimatableFloat(dashObj.GetNamedObject("v")).InitialValue);
|
||||
dashPattern.Add(ReadAnimatableFloat(dashObj?.ObjectOrNullProperty("v")).InitialValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new SolidColorStroke(
|
||||
in shapeLayerContentArgs,
|
||||
offset ?? s_animatable_0,
|
||||
offset ?? s_animatableDoubleZero,
|
||||
dashPattern,
|
||||
color,
|
||||
opacity,
|
||||
|
@ -126,9 +130,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
// gs
|
||||
ShapeLayerContent ReadGradientStroke(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
ShapeLayerContent ReadGradientStroke(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
switch (TToGradientType(obj.GetNamedNumber("t")))
|
||||
switch (TToGradientType(obj.DoubleOrNullProperty("t")))
|
||||
{
|
||||
case GradientType.Linear:
|
||||
return ReadLinearGradientStroke(obj, in shapeLayerContentArgs);
|
||||
|
@ -139,23 +145,23 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
LinearGradientStroke ReadLinearGradientStroke(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
LinearGradientStroke ReadLinearGradientStroke(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "t");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "1");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd", "t", "1");
|
||||
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var strokeWidth = ReadAnimatableFloat(obj.GetNamedObject("w"));
|
||||
var capType = LcToLineCapType(obj.GetNamedNumber("lc"));
|
||||
var joinType = LjToLineJoinType(obj.GetNamedNumber("lj"));
|
||||
var miterLimit = obj.GetNamedNumber("ml", 4); // Default miter limit in After Effects is 4
|
||||
var startPoint = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.GetNamedObject("e"));
|
||||
ReadAnimatableGradientStops(obj.GetNamedObject("g"), out var gradientStops);
|
||||
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"));
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new LinearGradientStroke(
|
||||
in shapeLayerContentArgs,
|
||||
opacity,
|
||||
|
@ -168,39 +174,29 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
gradientStops);
|
||||
}
|
||||
|
||||
RadialGradientStroke ReadRadialGradientStroke(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
RadialGradientStroke ReadRadialGradientStroke(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "t");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("t", "h", "1");
|
||||
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "h");
|
||||
var highlightLengthObject = obj.ObjectOrNullProperty("h");
|
||||
var highlightLength = ReadAnimatableFloat(highlightLengthObject);
|
||||
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "1");
|
||||
|
||||
Animatable<double> highlightLength = null;
|
||||
var highlightLengthObject = obj.GetNamedObject("h");
|
||||
if (highlightLengthObject != null)
|
||||
{
|
||||
highlightLength = ReadAnimatableFloat(highlightLengthObject);
|
||||
}
|
||||
|
||||
Animatable<double> highlightDegrees = null;
|
||||
var highlightAngleObject = obj.GetNamedObject("a");
|
||||
if (highlightAngleObject != null)
|
||||
{
|
||||
highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
|
||||
}
|
||||
var highlightAngleObject = obj.ObjectOrNullProperty("a");
|
||||
var highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
|
||||
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var strokeWidth = ReadAnimatableFloat(obj.GetNamedObject("w"));
|
||||
var capType = LcToLineCapType(obj.GetNamedNumber("lc"));
|
||||
var joinType = LjToLineJoinType(obj.GetNamedNumber("lj"));
|
||||
var miterLimit = obj.GetNamedNumber("ml", 4); // Default miter limit in After Effects is 4
|
||||
var startPoint = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.GetNamedObject("e"));
|
||||
ReadAnimatableGradientStops(obj.GetNamedObject("g"), out var gradientStops);
|
||||
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"));
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new RadialGradientStroke(
|
||||
in shapeLayerContentArgs,
|
||||
opacity: opacity,
|
||||
|
@ -216,25 +212,27 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
|
||||
// "fl"
|
||||
SolidColorFill ReadSolidColorFill(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
SolidColorFill ReadSolidColorFill(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "fillEnabled");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "cl");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("fillEnabled", "cl", "hd");
|
||||
|
||||
var fillType = ReadFillType(obj);
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var color = ReadColorFromC(obj);
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new SolidColorFill(in shapeLayerContentArgs, fillType, opacity, color);
|
||||
}
|
||||
|
||||
// gf
|
||||
ShapeLayerContent ReadGradientFill(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
ShapeLayerContent ReadGradientFill(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
switch (TToGradientType(obj.GetNamedNumber("t")))
|
||||
switch (TToGradientType(obj.DoubleOrNullProperty("t")))
|
||||
{
|
||||
case GradientType.Linear:
|
||||
return ReadLinearGradientFill(obj, in shapeLayerContentArgs);
|
||||
|
@ -245,33 +243,26 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
RadialGradientFill ReadRadialGradientFill(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
RadialGradientFill ReadRadialGradientFill(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "1");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd", "1");
|
||||
|
||||
var fillType = ReadFillType(obj);
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var startPoint = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.GetNamedObject("e"));
|
||||
ReadAnimatableGradientStops(obj.GetNamedObject("g"), out var gradientStops);
|
||||
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
|
||||
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
|
||||
|
||||
Animatable<double> highlightLength = null;
|
||||
var highlightLengthObject = obj.GetNamedObject("h");
|
||||
if (highlightLengthObject != null)
|
||||
{
|
||||
highlightLength = ReadAnimatableFloat(highlightLengthObject);
|
||||
}
|
||||
var highlightLengthObject = obj.ObjectOrNullProperty("h");
|
||||
var highlightLength = ReadAnimatableFloat(highlightLengthObject);
|
||||
|
||||
Animatable<double> highlightDegrees = null;
|
||||
var highlightAngleObject = obj.GetNamedObject("a");
|
||||
if (highlightAngleObject != null)
|
||||
{
|
||||
highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
|
||||
}
|
||||
var highlightAngleObject = obj.ObjectOrNullProperty("a");
|
||||
var highlightDegrees = ReadAnimatableFloat(highlightAngleObject);
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new RadialGradientFill(
|
||||
in shapeLayerContentArgs,
|
||||
fillType: fillType,
|
||||
|
@ -279,22 +270,24 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
startPoint: startPoint,
|
||||
endPoint: endPoint,
|
||||
gradientStops: gradientStops,
|
||||
highlightLength: null,
|
||||
highlightDegrees: null);
|
||||
highlightLength: highlightLength,
|
||||
highlightDegrees: highlightDegrees);
|
||||
}
|
||||
|
||||
LinearGradientFill ReadLinearGradientFill(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
LinearGradientFill ReadLinearGradientFill(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd");
|
||||
|
||||
var fillType = ReadFillType(obj);
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
var startPoint = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.GetNamedObject("e"));
|
||||
ReadAnimatableGradientStops(obj.GetNamedObject("g"), out var gradientStops);
|
||||
var startPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
|
||||
var endPoint = ReadAnimatableVector3(obj.ObjectOrNullProperty("e"));
|
||||
var gradientStops = ReadAnimatableGradientStops(obj.ObjectOrNullProperty("g"));
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new LinearGradientFill(
|
||||
in shapeLayerContentArgs,
|
||||
fillType: fillType,
|
||||
|
@ -304,58 +297,54 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
gradientStops: gradientStops);
|
||||
}
|
||||
|
||||
Ellipse ReadEllipse(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Ellipse ReadEllipse(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "closed");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("closed", "hd");
|
||||
|
||||
var position = ReadAnimatableVector3(obj.GetNamedObject("p"));
|
||||
var diameter = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var direction = ReadBool(obj, "d") == true;
|
||||
AssertAllFieldsRead(obj);
|
||||
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
|
||||
var diameter = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
|
||||
var direction = obj.BoolOrNullProperty("d") == true;
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new Ellipse(in shapeLayerContentArgs, direction, position, diameter);
|
||||
}
|
||||
|
||||
Polystar ReadPolystar(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Polystar ReadPolystar(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ix");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("ix");
|
||||
|
||||
var direction = ReadBool(obj, "d") == true;
|
||||
var direction = obj.BoolOrNullProperty("d") == true;
|
||||
|
||||
var type = SyToPolystarType(obj.GetNamedNumber("sy", double.NaN));
|
||||
|
||||
if (!type.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var points = ReadAnimatableFloat(obj.GetNamedObject("pt"));
|
||||
var points = ReadAnimatableFloat(obj.ObjectOrNullProperty("pt"));
|
||||
if (points.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("points");
|
||||
}
|
||||
|
||||
var position = ReadAnimatableVector3(obj.GetNamedObject("p"));
|
||||
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
|
||||
if (position.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("position");
|
||||
}
|
||||
|
||||
var rotation = ReadAnimatableFloat(obj.GetNamedObject("r"));
|
||||
var rotation = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
|
||||
if (rotation.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("rotation");
|
||||
}
|
||||
|
||||
var outerRadius = ReadAnimatableFloat(obj.GetNamedObject("or"));
|
||||
var outerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("or"));
|
||||
if (outerRadius.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("outer radius");
|
||||
}
|
||||
|
||||
var outerRoundedness = ReadAnimatableFloat(obj.GetNamedObject("os"));
|
||||
var outerRoundedness = ReadAnimatableFloat(obj.ObjectOrNullProperty("os"));
|
||||
if (outerRoundedness.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("outer roundedness");
|
||||
|
@ -364,31 +353,36 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
Animatable<double> innerRadius;
|
||||
Animatable<double> innerRoundedness;
|
||||
|
||||
if (type == Polystar.PolyStarType.Star)
|
||||
{
|
||||
innerRadius = ReadAnimatableFloat(obj.GetNamedObject("ir"));
|
||||
if (innerRadius.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("inner radius");
|
||||
}
|
||||
var polystarType = SyToPolystarType(obj.DoubleOrNullProperty("sy")) ?? Polystar.PolyStarType.Polygon;
|
||||
|
||||
innerRoundedness = ReadAnimatableFloat(obj.GetNamedObject("is"));
|
||||
if (innerRoundedness.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("inner roundedness");
|
||||
}
|
||||
}
|
||||
else
|
||||
switch (polystarType)
|
||||
{
|
||||
innerRadius = null;
|
||||
innerRoundedness = null;
|
||||
case Polystar.PolyStarType.Star:
|
||||
innerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("ir"));
|
||||
if (innerRadius.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("inner radius");
|
||||
}
|
||||
|
||||
innerRoundedness = ReadAnimatableFloat(obj.ObjectOrNullProperty("is"));
|
||||
if (innerRoundedness.IsAnimated)
|
||||
{
|
||||
_issues.PolystarAnimation("inner roundedness");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
innerRadius = null;
|
||||
innerRoundedness = null;
|
||||
break;
|
||||
}
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new Polystar(
|
||||
in shapeLayerContentArgs,
|
||||
direction,
|
||||
type.Value,
|
||||
polystarType,
|
||||
points,
|
||||
position,
|
||||
rotation,
|
||||
|
@ -398,46 +392,47 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
outerRoundedness);
|
||||
}
|
||||
|
||||
Rectangle ReadRectangle(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Rectangle ReadRectangle(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd");
|
||||
|
||||
var direction = ReadBool(obj, "d") == true;
|
||||
var position = ReadAnimatableVector3(obj.GetNamedObject("p"));
|
||||
var size = ReadAnimatableVector3(obj.GetNamedObject("s"));
|
||||
var cornerRadius = ReadAnimatableFloat(obj.GetNamedObject("r"));
|
||||
var direction = obj.BoolOrNullProperty("d") == true;
|
||||
var position = ReadAnimatableVector3(obj.ObjectOrNullProperty("p"));
|
||||
var size = ReadAnimatableVector3(obj.ObjectOrNullProperty("s"));
|
||||
var cornerRadius = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
|
||||
|
||||
AssertAllFieldsRead(obj);
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new Rectangle(in shapeLayerContentArgs, direction, position, size, cornerRadius);
|
||||
}
|
||||
|
||||
Path ReadPath(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Path ReadPath(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ind");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ix");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "cl");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "closed");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("ind", "ix", "hd", "cl", "closed");
|
||||
|
||||
var geometry = ReadAnimatableGeometry(obj.GetNamedObject("ks"));
|
||||
var direction = ReadBool(obj, "d") == true;
|
||||
AssertAllFieldsRead(obj);
|
||||
var geometry = ReadAnimatableGeometry(obj.ObjectOrNullProperty("ks"));
|
||||
var direction = obj.BoolOrNullProperty("d") == true;
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new Path(in shapeLayerContentArgs, direction, geometry);
|
||||
}
|
||||
|
||||
TrimPath ReadTrimPath(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
TrimPath ReadTrimPath(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ix");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("ix", "hd");
|
||||
|
||||
var startTrim = ReadAnimatableTrim(obj.GetNamedObject("s"));
|
||||
var endTrim = ReadAnimatableTrim(obj.GetNamedObject("e"));
|
||||
var offset = ReadAnimatableRotation(obj.GetNamedObject("o"));
|
||||
var trimType = MToTrimType(obj.GetNamedNumber("m", 1));
|
||||
AssertAllFieldsRead(obj);
|
||||
var startTrim = ReadAnimatableTrim(obj.ObjectOrNullProperty("s"));
|
||||
var endTrim = ReadAnimatableTrim(obj.ObjectOrNullProperty("e"));
|
||||
var offset = ReadAnimatableRotation(obj.ObjectOrNullProperty("o"));
|
||||
var trimType = MToTrimType(obj.DoubleOrNullProperty("m"));
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new TrimPath(
|
||||
in shapeLayerContentArgs,
|
||||
trimType,
|
||||
|
@ -446,52 +441,94 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
offset);
|
||||
}
|
||||
|
||||
Repeater ReadRepeater(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Repeater ReadRepeater(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
var count = ReadAnimatableFloat(obj.GetNamedObject("c"));
|
||||
var offset = ReadAnimatableFloat(obj.GetNamedObject("o"));
|
||||
var transform = ReadRepeaterTransform(obj.GetNamedObject("tr"), in shapeLayerContentArgs);
|
||||
var count = ReadAnimatableFloat(obj.ObjectOrNullProperty("c"));
|
||||
var offset = ReadAnimatableFloat(obj.ObjectOrNullProperty("o"));
|
||||
var transform = ReadRepeaterTransform(obj.ObjectOrNullProperty("tr"), in shapeLayerContentArgs);
|
||||
return new Repeater(in shapeLayerContentArgs, count, offset, transform);
|
||||
}
|
||||
|
||||
MergePaths ReadMergePaths(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
MergePaths ReadMergePaths(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd");
|
||||
|
||||
var mergeMode = MmToMergeMode(obj.GetNamedNumber("mm"));
|
||||
AssertAllFieldsRead(obj);
|
||||
var mergeMode = MmToMergeMode(obj.DoubleOrNullProperty("mm"));
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new MergePaths(
|
||||
in shapeLayerContentArgs,
|
||||
mergeMode);
|
||||
}
|
||||
|
||||
RoundedCorner ReadRoundedCorner(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
RoundedCorner ReadRoundedCorner(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these fields.
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "hd");
|
||||
IgnoreFieldThatIsNotYetSupported(obj, "ix");
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("hd", "ix");
|
||||
|
||||
var radius = ReadAnimatableFloat(obj.GetNamedObject("r"));
|
||||
AssertAllFieldsRead(obj);
|
||||
var radius = ReadAnimatableFloat(obj.ObjectOrNullProperty("r"));
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new RoundedCorner(
|
||||
in shapeLayerContentArgs,
|
||||
radius);
|
||||
}
|
||||
|
||||
ShapeFill.PathFillType ReadFillType(JCObject obj)
|
||||
ShapeFill.PathFillType ReadFillType(in LottieJsonObjectElement obj)
|
||||
{
|
||||
var isWindingFill = ReadBool(obj, "r") == true;
|
||||
return isWindingFill ? ShapeFill.PathFillType.Winding : ShapeFill.PathFillType.EvenOdd;
|
||||
// If not specified, the fill type is EvenOdd.
|
||||
var windingValue = obj.Int32OrNullProperty("r");
|
||||
switch (windingValue)
|
||||
{
|
||||
case 0: return ShapeFill.PathFillType.EvenOdd;
|
||||
case 1: return ShapeFill.PathFillType.Winding;
|
||||
|
||||
// TODO - some files have a "2" value. There
|
||||
// may be another fill type.
|
||||
default: return ShapeFill.PathFillType.EvenOdd;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the transform for a repeater. Repeater transforms are the same as regular transforms
|
||||
// except they have an extra couple properties.
|
||||
RepeaterTransform ReadRepeaterTransform(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
RepeaterTransform ReadRepeaterTransform(
|
||||
in LottieJsonObjectElement? obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
var startOpacity = ReadOpacityFromObject(obj.GetNamedObject("so", null));
|
||||
var endOpacity = ReadOpacityFromObject(obj.GetNamedObject("eo", null));
|
||||
if (obj is null)
|
||||
{
|
||||
return new RepeaterTransform(
|
||||
shapeLayerContentArgs,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableRotationNone,
|
||||
s_animatableOpacityOpaque,
|
||||
s_animatableOpacityOpaque,
|
||||
s_animatableOpacityOpaque);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReadRepeaterTransform(obj.Value, shapeLayerContentArgs);
|
||||
}
|
||||
}
|
||||
|
||||
RepeaterTransform ReadRepeaterTransform(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
obj.IgnorePropertyThatIsNotYetSupported("nm", "ty");
|
||||
|
||||
var startOpacity = ReadOpacityFromObject(obj.ObjectOrNullProperty("so"));
|
||||
var endOpacity = ReadOpacityFromObject(obj.ObjectOrNullProperty("eo"));
|
||||
var transform = ReadTransform(obj, in shapeLayerContentArgs);
|
||||
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new RepeaterTransform(
|
||||
in shapeLayerContentArgs,
|
||||
transform.Anchor,
|
||||
|
@ -503,38 +540,54 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
|
|||
endOpacity);
|
||||
}
|
||||
|
||||
Transform ReadTransform(JCObject obj, in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
Transform ReadTransform(
|
||||
in LottieJsonObjectElement? obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
var anchorJson = obj.GetNamedObject("a", null);
|
||||
if (obj is null)
|
||||
{
|
||||
return new Transform(
|
||||
shapeLayerContentArgs,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableVector3Zero,
|
||||
s_animatableRotationNone,
|
||||
s_animatableOpacityOpaque);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReadTransform(obj.Value, shapeLayerContentArgs);
|
||||
}
|
||||
}
|
||||
|
||||
var anchor =
|
||||
anchorJson != null
|
||||
? ReadAnimatableVector3(anchorJson)
|
||||
: new AnimatableVector3(Vector3.Zero, null);
|
||||
Transform ReadTransform(
|
||||
in LottieJsonObjectElement obj,
|
||||
in ShapeLayerContent.ShapeLayerContentArgs shapeLayerContentArgs)
|
||||
{
|
||||
// Not clear whether we need to read these properties.
|
||||
obj.IgnorePropertyThatIsNotYetSupported("sa", "sk");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("or", "rx", "ry");
|
||||
obj.IgnorePropertyThatIsNotYetSupported("nm", "ty");
|
||||
|
||||
var positionJson = obj.GetNamedObject("p", null);
|
||||
var anchorJson = obj.ObjectOrNullProperty("a");
|
||||
|
||||
var position =
|
||||
positionJson != null
|
||||
? ReadAnimatableVector3(positionJson)
|
||||
: new AnimatableVector3(Vector3.Zero, null);
|
||||
|
||||
var scaleJson = obj.GetNamedObject("s", null);
|
||||
var anchor = ReadAnimatableVector3(anchorJson);
|
||||
var positionJson = obj.ObjectOrNullProperty("p");
|
||||
var position = ReadAnimatableVector3(positionJson);
|
||||
var scaleJson = obj.ObjectOrNullProperty("s");
|
||||
|
||||
var scalePercent =
|
||||
scaleJson != null
|
||||
? ReadAnimatableVector3(scaleJson)
|
||||
: new AnimatableVector3(new Vector3(100, 100, 100), null);
|
||||
|
||||
var rotationJson = obj.GetNamedObject("r", null) ?? obj.GetNamedObject("rz", null);
|
||||
var rotationJson = obj.ObjectOrNullProperty("r") ?? obj.ObjectOrNullProperty("rz");
|
||||
|
||||
var rotation =
|
||||
rotationJson != null
|
||||
? ReadAnimatableRotation(rotationJson)
|
||||
: new Animatable<Rotation>(Rotation.None, null);
|
||||
var rotation = ReadAnimatableRotation(rotationJson);
|
||||
|
||||
var opacity = ReadOpacityFromO(obj);
|
||||
|
||||
obj.AssertAllPropertiesRead();
|
||||
return new Transform(in shapeLayerContentArgs, anchor, position, scalePercent, rotation, opacity);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче