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();
            }
        }
Пример #2
0
        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);
                }
            }
        }
Пример #3
0
        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);
        }