static void EnsureScalarThemePropertyExists(TranslationContext context, string bindingName, string displayName, double defaultValue) { var defaultValueAsFloat = ConvertTo.Float(defaultValue); var themePropertySet = GetThemePropertySet(context); // Insert a property set value for the scalar if one hasn't yet been added. switch (themePropertySet.TryGetScalar(bindingName, out var existingValueAsFloat)) { case CompositionGetValueStatus.NotFound: // The property hasn't been added yet. Add it. themePropertySet.InsertScalar(bindingName, defaultValueAsFloat); context.PropertyBindings.AddPropertyBinding(new CompMetadata.PropertyBinding { BindingName = bindingName, DisplayName = displayName, ActualType = PropertySetValueType.Scalar, ExposedType = PropertySetValueType.Scalar, DefaultValue = ConvertTo.Float(defaultValue), }); break; case CompositionGetValueStatus.Succeeded: // The property has already been added. if (existingValueAsFloat != defaultValueAsFloat) { context.Issues.ThemePropertyValuesAreInconsistent(bindingName, existingValueAsFloat.ToString(), defaultValueAsFloat.ToString()); } break; case CompositionGetValueStatus.TypeMismatch: default: throw new InvalidOperationException(); } }
static void TranslateAndApplyTrimPath( ShapeContext context, CompositionGeometry geometry, bool reverseDirection, double trimOffsetDegrees) { var trimPath = context.TrimPath; if (trimPath is null) { return; } if (reverseDirection) { trimPath = trimPath.CloneWithReversedDirection(); } var startTrim = Optimizer.TrimAnimatable(context, trimPath.Start); var endTrim = Optimizer.TrimAnimatable(context, trimPath.End); var trimPathOffset = Optimizer.TrimAnimatable(context, trimPath.Offset); if (!startTrim.IsAnimated && !endTrim.IsAnimated) { // Handle some well-known static cases. if (startTrim.InitialValue.Value == 0 && endTrim.InitialValue.Value == 1) { // The trim does nothing. return; } else if (startTrim.InitialValue == endTrim.InitialValue) { // TODO - the trim trims away all of the path. } } var order = GetAnimatableOrder(in startTrim, in endTrim); switch (order) { case AnimatableOrder.Before: case AnimatableOrder.Equal: break; case AnimatableOrder.After: { // Swap is necessary to match the WinComp semantics. var temp = startTrim; startTrim = endTrim; endTrim = temp; } break; case AnimatableOrder.BeforeAndAfter: break; default: throw new InvalidOperationException(); } if (order == AnimatableOrder.BeforeAndAfter) { // Add properties that will be animated. The TrimStart and TrimEnd properties // will be set by these values through an expression. Animate.TrimStartOrTrimEndPropertySetValue(context, startTrim, geometry, "TStart"); var trimStartExpression = context.ObjectFactory.CreateExpressionAnimation(ExpressionFactory.MinTStartTEnd); trimStartExpression.SetReferenceParameter("my", geometry); Animate.WithExpression(geometry, trimStartExpression, nameof(geometry.TrimStart)); Animate.TrimStartOrTrimEndPropertySetValue(context, endTrim, geometry, "TEnd"); var trimEndExpression = context.ObjectFactory.CreateExpressionAnimation(ExpressionFactory.MaxTStartTEnd); trimEndExpression.SetReferenceParameter("my", geometry); Animate.WithExpression(geometry, trimEndExpression, nameof(geometry.TrimEnd)); } else { // Directly animate the TrimStart and TrimEnd properties. if (startTrim.IsAnimated) { Animate.TrimStartOrTrimEnd(context, startTrim, geometry, nameof(geometry.TrimStart), "TrimStart", null); } else { geometry.TrimStart = ConvertTo.Float(startTrim.InitialValue); } if (endTrim.IsAnimated) { Animate.TrimStartOrTrimEnd(context, endTrim, geometry, nameof(geometry.TrimEnd), "TrimEnd", null); } else { geometry.TrimEnd = ConvertTo.Float(endTrim.InitialValue); } } if (trimOffsetDegrees != 0 && !trimPathOffset.IsAnimated) { // Rectangle shapes are treated specially here to account for Lottie rectangle 0,0 being // top right and WinComp rectangle 0,0 being top left. As long as the TrimOffset isn't // being animated we can simply add an offset to the trim path. geometry.TrimOffset = (float)((trimPathOffset.InitialValue.Degrees + trimOffsetDegrees) / 360); } else { if (trimOffsetDegrees != 0) { // TODO - can be handled with another property. context.Issues.AnimatedTrimOffsetWithStaticTrimOffsetIsNotSupported(); } if (trimPathOffset.IsAnimated) { Animate.ScaledRotation(context, trimPathOffset, 1 / 360.0, geometry, nameof(geometry.TrimOffset), "TrimOffset", null); } else { geometry.TrimOffset = ConvertTo.Float(trimPathOffset.InitialValue.Degrees / 360); } } }
public static Visual ApplyGaussianBlur( LayerContext context, Visual source, GaussianBlurEffect gaussianBlurEffect) { Debug.Assert(gaussianBlurEffect.IsEnabled, "Precondition"); Debug.Assert(context is PreCompLayerContext || context is ShapeLayerContext, "Precondition"); var factory = context.ObjectFactory; if (!factory.IsUapApiAvailable(nameof(CompositionVisualSurface), versionDependentFeatureDescription: "Gaussian blur")) { // The effect can't be displayed on the targeted version. return(source); } // Gaussian blur: // +--------------+ // | SpriteVisual | -- Has the final composited result. // +--------------+ // ^ // | // +--------------+ // | EffectBrush | -- Composition effect brush allows the composite effect result to be used as a brush. // +--------------+ // ^ // | // +--------------------+ // | GaussianBlurEffect | // +--------------------+ // ^ Source // | // +--------------+ // | SurfaceBrush | -- Surface brush that will paint with the output of the VisualSurface // +--------------+ that has the source visual assigned to it. // ^ CompositionEffectSourceParameter("source") // | // +---------------+ // | VisualSurface | -- The visual surface captures the renderable contents of its source visual. // +---------------+ // ^ // | // +--------+ // | Visual | -- The layer translated to a Visual. // +--------+ var size = context.CompositionContext.Size; if (context is PreCompLayerContext) { size = ConvertTo.Vector2(((PreCompLayerContext)context).Layer.Width, ((PreCompLayerContext)context).Layer.Height); } // Build from the bottom up. var visualSurface = factory.CreateVisualSurface(); visualSurface.SourceVisual = source; visualSurface.SourceSize = size; var surfaceBrush = factory.CreateSurfaceBrush(visualSurface); var effect = new WinCompData.Mgce.GaussianBlurEffect(); var blurriness = Optimizer.TrimAnimatable(context, gaussianBlurEffect.Blurriness); if (blurriness.IsAnimated) { context.Issues.AnimatedLayerEffectParameters("Gaussian blur"); } effect.BlurAmount = ConvertTo.Float(blurriness.InitialValue / 10.0); // We only support HorizontalAndVertical blur dimension. var blurDimensions = Optimizer.TrimAnimatable(context, gaussianBlurEffect.BlurDimensions); var unsupportedBlurDimensions = blurDimensions .KeyFrames .Select(kf => kf.Value) .Distinct() .Where(v => v.Value != BlurDimension.HorizontalAndVertical).ToArray(); foreach (var value in unsupportedBlurDimensions) { context.Issues.UnsupportedLayerEffectParameter("gaussian blur", "blur dimension", value.Value.ToString()); } effect.Source = new CompositionEffectSourceParameter("source"); var effectBrush = factory.CreateEffectFactory(effect).CreateBrush(); effectBrush.SetSourceParameter("source", surfaceBrush); var result = factory.CreateSpriteVisual(); result.Brush = effectBrush; result.Size = size; return(result); }