More json refactoring towards System.Text.Json (#244)

This commit is contained in:
Simeon 2020-02-19 13:51:08 -08:00 коммит произвёл GitHub
Родитель ab94c4525a
Коммит 061ab6a318
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
25 изменённых файлов: 1378 добавлений и 1111 удалений

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

@ -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);
}
}