Parse After Effects blur effects from Lottie files. (#393)

* Parse After Effects blur effects from Lottie files.

Blur is not yet supported in the translator.
This commit is contained in:
Simeon 2020-11-24 12:30:09 -08:00 коммит произвёл GitHub
Родитель e1ed8f24aa
Коммит 385187aba9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 245 добавлений и 18 удалений

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

@ -1,6 +1,5 @@
[comment]: # (deprecated)
[comment]: # (name:LayerEffectsIsNotSupported)
[comment]: # (text:{layer} has an unsupported layer effects, type = {type}.)
[comment]: # (name:LayerEffectsIsNotSupported)
[comment]: # (text:{layer} has an unsupported layer effect, type = {type}.)
# Lottie-Windows Warning LP0008
@ -8,7 +7,8 @@ The Lottie file contains Layer Effects. Not all Layer Effects are supported
by Lottie-Windows.
## Remarks
This issue does not apply to the latest version. Please upgrade to a newer version of Lottie-Windows.
Lottie does not support Layer Effects on most platforms. It may be possible to
work around this by modifying your After Effects project to avoid the use of Layer Effects.
## Resources

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

@ -0,0 +1,19 @@
// 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.
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData
{
/// <summary>
/// The dimensions affected by a blur.
/// </summary>
#if PUBLIC_LottieData
public
#endif
enum BlurDimension
{
Horizontal,
Vertical,
HorizontalAndVertical,
}
}

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

@ -2,6 +2,8 @@
// 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;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData
{
/// <summary>
@ -31,7 +33,27 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData
public enum EffectType
{
// Type = 25.
DropShadow,
// Type = 29.
GaussianBlur,
}
/// <summary>
/// Represents an effect that was not recognized by the parser.
/// </summary>
public sealed class Unknown : Effect
{
readonly double _type;
public Unknown(double type, string name, bool isEnabled)
: base(name, isEnabled)
{
_type = type;
}
public override EffectType Type => (EffectType)Math.Round(_type);
}
}
}

40
source/LottieData/Enum.cs Normal file
Просмотреть файл

@ -0,0 +1,40 @@
// 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.
#nullable enable
using System;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData
{
/// <summary>
/// Wraps an enum, but implemented as a struct so that
/// it can implement <see cref="IEquatable{T}"/> as required by
/// <see cref="Animatable{T}"/>.
/// </summary>
/// <typeparam name="T">An enum type.</typeparam>
#if PUBLIC_LottieData
public
#endif
readonly struct Enum<T> : IEquatable<Enum<T>>
where T : struct, IComparable
{
readonly T _value;
Enum(T value)
{
_value = value;
}
public T Value => _value;
public static implicit operator Enum<T>(T value) => new Enum<T>(value);
public static implicit operator T(Enum<T> value) => value;
public bool Equals(Enum<T> other) => other._value.CompareTo(_value) == 0;
public override string? ToString() => _value.ToString();
}
}

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

@ -0,0 +1,47 @@
// 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.
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData
{
/// <summary>
/// A Gaussian blur effect.
/// </summary>
#if PUBLIC_LottieData
public
#endif
sealed class GaussianBlurEffect : Effect
{
public GaussianBlurEffect(
string name,
bool isEnabled,
Animatable<double> blurriness,
Animatable<Enum<BlurDimension>> blurDimensions,
Animatable<bool> repeatEdgePixels)
: base(
name,
isEnabled)
{
Blurriness = blurriness;
BlurDimensions = blurDimensions;
RepeatEdgePixels = repeatEdgePixels;
}
/// <summary>
/// The intensity of the blur.
/// </summary>
public Animatable<double> Blurriness { get; }
/// <summary>
/// Whether the blur is horizontal, vertical, or both.
/// </summary>
public Animatable<Enum<BlurDimension>> BlurDimensions { get; }
/// <summary>
/// Whether or not the blur repeats the pixels at the edge.
/// </summary>
public Animatable<bool> RepeatEdgePixels { get; }
public override EffectType Type => EffectType.GaussianBlur;
}
}

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

@ -13,6 +13,7 @@
<Compile Include="$(MSBuildThisFileDirectory)AssetCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BezierSegment.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BlendMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BlurDimension.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Color.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ColorGradientStop.cs" />
@ -24,9 +25,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Effect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Ellipse.cs" />
<Compile Include="$(MSBuildThisFileDirectory)EmbeddedImageAsset.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Enum.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExtensionMethods.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExternalImageAsset.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Font.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GaussianBlurEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GradientStop.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HoldEasing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IAnimatableValue.cs" />

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

@ -344,10 +344,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
YamlObject FromEffect(Effect effect, YamlMap superclassContent)
{
superclassContent.Add(nameof(effect.Type), effect.Type.ToString());
superclassContent.Add(nameof(effect.IsEnabled), effect.IsEnabled);
return effect.Type switch
{
Effect.EffectType.DropShadow => FromDropShadowEffect((DropShadowEffect)effect, superclassContent),
_ => throw Unreachable,
Effect.EffectType.GaussianBlur => FromGaussianBlurEffect((GaussianBlurEffect)effect, superclassContent),
// Handle all unknown effect types.
_ => superclassContent,
};
}
@ -363,6 +367,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
return result;
}
YamlObject FromGaussianBlurEffect(GaussianBlurEffect effect, YamlMap superclassContent)
{
var result = superclassContent;
result.Add(nameof(effect.BlurDimensions), FromAnimatable(effect.BlurDimensions));
result.Add(nameof(effect.Blurriness), FromAnimatable(effect.Blurriness));
result.Add(nameof(effect.RepeatEdgePixels), FromAnimatable(effect.RepeatEdgePixels));
return result;
}
YamlObject FromMask(Mask mask)
=> new YamlMap
{
@ -468,6 +481,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
YamlObject FromAnimatable(Animatable<double> animatable) => FromAnimatable(animatable, Scalar);
YamlObject FromAnimatable<T>(Animatable<Enum<T>> animatable)
where T : struct, IComparable => FromAnimatable(animatable, Scalar);
YamlObject FromAnimatable(Animatable<Opacity> animatable) => FromAnimatable(animatable, Scalar);
YamlObject FromAnimatable(Animatable<Rotation> animatable) => FromAnimatable(animatable, Scalar);
@ -676,6 +692,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
YamlScalar Scalar(Enum type) => Scalar(type, type.ToString());
YamlObject Scalar<T>(Enum<T> value)
where T : struct, IComparable => Scalar(value, value.ToString()!);
YamlScalar Scalar(Mask.MaskMode type) => Scalar(type, type.ToString());
YamlScalar Scalar(Opacity value) => Scalar(value, $"{value.Percent}%");

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

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
@ -17,6 +16,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
static readonly Sequence<GradientStop> s_defaultGradientStops =
new Sequence<GradientStop>(new ColorGradientStop(0, Color.Black));
static readonly Animatable<Enum<BlurDimension>> s_animatableBlurDimensionHorizontalAndVertical =
CreateNonAnimatedAnimatable((Enum<BlurDimension>)BlurDimension.HorizontalAndVertical);
static readonly Animatable<Color> s_animatableColorBlack = CreateNonAnimatedAnimatable(Color.Black);
static readonly Animatable<double> s_animatableDoubleZero = CreateNonAnimatedAnimatable(0.0);
static readonly Animatable<bool> s_animatableFalse = CreateNonAnimatedAnimatable(false);
@ -28,6 +30,15 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
static readonly AnimatableVector3 s_animatableVector3Zero = new AnimatableVector3(Vector3.Zero, null);
static readonly AnimatableVector3 s_animatableVector3OneHundred = new AnimatableVector3(new Vector3(100, 100, 100), null);
static readonly AnimatableParser<Enum<BlurDimension>> s_animatableBlurDimensionParser =
CreateAnimatableParser((in LottieJsonElement element) => element.AsInt32() switch
{
1 => (Enum<BlurDimension>)BlurDimension.HorizontalAndVertical,
2 => (Enum<BlurDimension>)BlurDimension.Horizontal,
3 => (Enum<BlurDimension>)BlurDimension.Vertical,
_ => throw ReaderException("Unexpected blur dimension value."),
});
// Parses boolean values as 0 or non-0 integers.
static readonly AnimatableParser<bool> s_animatableBoolParser =
CreateAnimatableParser((in LottieJsonElement element) => (element.AsInt32() ?? 0) != 0);
@ -69,6 +80,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
? s_animatableFalse
: ReadAnimatable(s_animatableBoolParser, obj.Value);
Animatable<Enum<BlurDimension>> ReadAnimatableBlurDimension(in LottieJsonObjectElement? obj)
=> obj is null
? s_animatableBlurDimensionHorizontalAndVertical
: ReadAnimatable(s_animatableBlurDimensionParser, obj.Value);
Animatable<Color> ReadAnimatableColor(in LottieJsonObjectElement? obj)
=> obj is null
? s_animatableColorBlack

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

@ -38,29 +38,32 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
Effect? ReadEffect(in LottieJsonObjectElement obj, string layerName)
{
var effectType = obj.DoublePropertyOrNull("ty") ?? throw ReaderException("Invalid effect");
var effectType = obj.DoublePropertyOrNull("ty") ?? throw ReaderException("Invalid effect.");
var effectName = obj.StringPropertyOrNull("nm") ?? string.Empty;
var isEnabled = obj.BoolPropertyOrNull("en") ?? true;
switch (effectType)
{
// DropShadows are type 25. This is the only type we currently support.
case 25:
return ReadDropShadowEffect(obj);
return ReadDropShadowEffect(obj, effectName, isEnabled);
case 29:
return ReadGaussianBlurEffect(obj, effectName, isEnabled);
default:
obj.IgnorePropertyThatIsNotYetSupported(
"nm", // name.
"mn", // match name.
"en", // enabled.
"np", // unknown.
"ix", // index.
"ef"); // effect parameters.
_issues.LayerEffectsIsNotSupported(layerName, effectType.ToString());
return null;
return new Effect.Unknown(effectType, effectName, isEnabled);
}
}
DropShadowEffect ReadDropShadowEffect(in LottieJsonObjectElement obj)
// Layer effect type 29.
GaussianBlurEffect ReadGaussianBlurEffect(in LottieJsonObjectElement obj, string effectName, bool isEnabled)
{
// Index.
obj.IgnorePropertyIntentionally("ix");
@ -71,9 +74,67 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
// Unknown.
obj.IgnorePropertyIntentionally("np");
var effectName = obj.StringPropertyOrNull("nm") ?? string.Empty;
var parameters = obj.ArrayPropertyOrNull("ef") ?? throw ParseFailure();
var isEnabled = obj.BoolPropertyOrNull("en") ?? true;
Animatable<double>? blurriness = null;
Animatable<Enum<BlurDimension>>? blurDimensions = null;
Animatable<bool>? repeatEdgePixels = null;
for (var i = 0; i < parameters.Count; i++)
{
var p = parameters[i].AsObject() ?? throw ParseFailure();
var value = p.ObjectPropertyOrNull("v") ?? throw ParseFailure();
switch (i)
{
case 0:
// Blurriness - float 0..50
blurriness = ReadAnimatableFloat(value) ?? throw ParseFailure();
break;
case 1:
blurDimensions = ReadAnimatableBlurDimension(value) ?? throw ParseFailure();
break;
case 2:
repeatEdgePixels = ReadAnimatableBool(value) ?? throw ParseFailure();
break;
default:
throw ParseFailure();
}
}
// Ensure all parameter values were provided.
if (blurriness is null ||
blurDimensions is null ||
repeatEdgePixels is null)
{
throw ParseFailure();
}
return new GaussianBlurEffect(
effectName,
isEnabled,
blurriness: blurriness,
blurDimensions: blurDimensions,
repeatEdgePixels: repeatEdgePixels);
static Exception ParseFailure() => ReaderException("Invalid Gaussian blur effect.");
}
// Layer effect type 25.
DropShadowEffect ReadDropShadowEffect(in LottieJsonObjectElement obj, string effectName, bool isEnabled)
{
// Index.
obj.IgnorePropertyIntentionally("ix");
// Match name.
obj.IgnorePropertyIntentionally("mn");
// Unknown.
obj.IgnorePropertyIntentionally("np");
var parameters = obj.ArrayPropertyOrNull("ef") ?? throw ParseFailure();
@ -112,7 +173,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
break;
default:
throw ReaderException("Invalid drop shadow effect");
throw ParseFailure();
}
}
@ -137,7 +198,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
opacity: opacity,
softness: softness);
static Exception ParseFailure() => ReaderException("Invalid drop shadow effect");
static Exception ParseFailure() => ReaderException("Invalid drop shadow effect.");
}
}
}

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

@ -43,7 +43,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieData.Serialization
internal void IllustratorLayers() => Report("LP0007", "Illustrator layers.");
internal void LayerEffectsIsNotSupported(string layer, string type) => Report("LP0008", $"{layer} has an unsupported layer effects, type = {type}.");
internal void LayerEffectsIsNotSupported(string layer, string type) => Report("LP0008", $"{layer} has an unsupported layer effect, type = {type}.");
// LP0009 has been deprecated.
// Was: Mattes.