public static Sn.Matrix3x2 CreateMatrixFromTransform(LayerContext context, Transform transform) { if (transform is null) { return(Sn.Matrix3x2.Identity); } if (transform.IsAnimated) { // TODO - report an issue. We can't handle an animated transform. // TODO - we could handle it if the only thing that is animated is the Opacity. } var anchor = ConvertTo.Vector2(transform.Anchor.InitialValue); var position = ConvertTo.Vector2(transform.Position.InitialValue); var scale = ConvertTo.Vector2(transform.ScalePercent.InitialValue / 100.0); var rotation = (float)transform.Rotation.InitialValue.Radians; // Calculate the matrix that is equivalent to the properties. var combinedMatrix = Sn.Matrix3x2.CreateScale(scale, anchor) * Sn.Matrix3x2.CreateRotation(rotation, anchor) * Sn.Matrix3x2.CreateTranslation(position + anchor); return(combinedMatrix); }
internal override Visual?GetVisualRoot(CompositionContext context) { // Translate the SolidLayer to a Visual. if (_context.Layer.IsHidden || _context.Layer.Transform.Opacity.IsAlways(LottieData.Opacity.Transparent)) { // The layer does not render anything. Nothing to translate. This can happen when someone // creates a solid layer to act like a Null layer. return(null); } if (!Transforms.TryCreateContainerVisualTransformChain(_context, out var containerRootNode, out var containerContentNode)) { // The layer is never visible. return(null); } var rectangle = context.ObjectFactory.CreateSpriteVisual(); rectangle.Size = ConvertTo.Vector2(_context.Layer.Width, _context.Layer.Height); containerContentNode.Children.Add(rectangle); var layerHasMasks = false; #if !NoClipping layerHasMasks = _context.Layer.Masks.Any(); #endif rectangle.Brush = Brushes.CreateNonAnimatedColorBrush(_context, _context.Layer.Color); rectangle.SetDescription(context, () => "SolidLayerRectangle"); var result = layerHasMasks ? Masks.TranslateAndApplyMasksForLayer(_context, containerRootNode) : containerRootNode; Describe(context, result); return(result); }
public static CompositionShape TranslateEllipseContent(ShapeContext context, Ellipse shapeContent) { // An ellipse is represented as a SpriteShape with a CompositionEllipseGeometry. var compositionSpriteShape = context.ObjectFactory.CreateSpriteShape(); compositionSpriteShape.SetDescription(context, () => shapeContent.Name); var compositionEllipseGeometry = context.ObjectFactory.CreateEllipseGeometry(); compositionEllipseGeometry.SetDescription(context, () => $"{shapeContent.Name}.EllipseGeometry"); compositionSpriteShape.Geometry = compositionEllipseGeometry; var position = Optimizer.TrimAnimatable(context, shapeContent.Position); if (position.IsAnimated) { Animate.Vector2(context, position, compositionEllipseGeometry, "Center"); } else { compositionEllipseGeometry.Center = ConvertTo.Vector2(position.InitialValue); } // Ensure that the diameter is expressed in a form that has only one easing per channel. var diameter = AnimatableVector3Rewriter.EnsureOneEasingPerChannel(shapeContent.Diameter); if (diameter is AnimatableXYZ diameterXYZ) { var diameterX = Optimizer.TrimAnimatable(context, diameterXYZ.X); var diameterY = Optimizer.TrimAnimatable(context, diameterXYZ.Y); if (diameterX.IsAnimated) { Animate.ScaledScalar(context, diameterX, 0.5, compositionEllipseGeometry, $"{nameof(CompositionEllipseGeometry.Radius)}.X"); } if (diameterY.IsAnimated) { Animate.ScaledScalar(context, diameterY, 0.5, compositionEllipseGeometry, $"{nameof(CompositionEllipseGeometry.Radius)}.Y"); } if (!diameterX.IsAnimated || !diameterY.IsAnimated) { compositionEllipseGeometry.Radius = ConvertTo.Vector2(diameter.InitialValue) * 0.5F; } } else { var diameter3 = Optimizer.TrimAnimatable <Vector3>(context, (AnimatableVector3)diameter); if (diameter3.IsAnimated) { Animate.ScaledVector2(context, diameter3, 0.5, compositionEllipseGeometry, nameof(CompositionEllipseGeometry.Radius)); } else { compositionEllipseGeometry.Radius = ConvertTo.Vector2(diameter.InitialValue) * 0.5F; } } Shapes.TranslateAndApplyShapeContext( context, compositionSpriteShape, reverseDirection: shapeContent.DrawingDirection == DrawingDirection.Reverse); return(compositionSpriteShape); }
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); }
public static Visual ApplyDropShadow( LayerContext context, Visual source, DropShadowEffect dropShadowEffect) { if (!context.ObjectFactory.IsUapApiAvailable(nameof(CompositionVisualSurface), versionDependentFeatureDescription: "Drop Shadow")) { // The effect can't be displayed on the targeted version. return(source); } Debug.Assert(dropShadowEffect.IsEnabled, "Precondition"); Debug.Assert(context is PreCompLayerContext || context is ShapeLayerContext, "Precondition"); // Shadow: // +------------------+ // | Container Visual | -- Has the final composited result. // +------------------+ < // ^ Child #1 \ Child #2 (original layer) // | (shadow layer) \ // | \ // +---------------------+ \ // | ApplyGaussianBlur() | \ // +---------------------+ +-----------------+ // ^ | ContainerVisual | - Original Visual node. // | +-----------------+ // +----------------+ . // | SpriteVisual | . // +----------------+ . // ^ Source . // | . // +--------------+ . // | MaskBrush | . // +--------------+ . // ^ Source ^ Mask . Source // | \ V // +----------+ +---------------+ // |ColorBrush| | VisualSurface | // +----------+ +---------------+ GaussianBlurEffect gaussianBlurEffect = new GaussianBlurEffect( name: dropShadowEffect.Name + "_blur", isEnabled: true, blurriness: dropShadowEffect.Softness, blurDimensions: new Animatable <Enum <BlurDimension> >(BlurDimension.HorizontalAndVertical), repeatEdgePixels: new Animatable <bool>(true), forceGpuRendering: true); var factory = context.ObjectFactory; var size = context.CompositionContext.Size; if (context is PreCompLayerContext) { size = ConvertTo.Vector2(((PreCompLayerContext)context).Layer.Width, ((PreCompLayerContext)context).Layer.Height); } var visualSurface = factory.CreateVisualSurface(); visualSurface.SourceSize = size; visualSurface.SourceVisual = source; var maskBrush = factory.CreateMaskBrush(); var colorBrush = factory.CreateColorBrush(dropShadowEffect.Color.InitialValue); var color = Optimizer.TrimAnimatable(context, dropShadowEffect.Color); if (color.IsAnimated) { Animate.Color(context, color, colorBrush, nameof(colorBrush.Color)); } else { colorBrush.Color = ConvertTo.Color(color.InitialValue); } maskBrush.Source = colorBrush; maskBrush.Mask = factory.CreateSurfaceBrush(visualSurface); var shadowSpriteVisual = factory.CreateSpriteVisual(); shadowSpriteVisual.Size = size; shadowSpriteVisual.Brush = maskBrush; var blurResult = ApplyGaussianBlur(context, shadowSpriteVisual, gaussianBlurEffect); var opacity = Optimizer.TrimAnimatable(context, dropShadowEffect.Opacity); if (opacity.IsAnimated) { Animate.Opacity(context, opacity, blurResult, nameof(blurResult.Opacity)); } else { blurResult.Opacity = (float)opacity.InitialValue.Value; } // Convert direction and distance to a Vector3. var direction = Optimizer.TrimAnimatable(context, dropShadowEffect.Direction); var distance = Optimizer.TrimAnimatable(context, dropShadowEffect.Distance); if (direction.IsAnimated) { if (distance.IsAnimated) { // Direction and distance are animated. // NOTE: we could support this in some cases. The worst cases are // where the keyframes don't line up, and/or the easings are different // between distance and direction. context.Issues.AnimatedLayerEffectParameters("drop shadow"); } else { // Only direction is animated. var distanceValue = distance.InitialValue; var keyFrames = direction.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(kf.Value, distanceValue), kf.Easing)).ToArray(); var directionAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, directionAnimation, blurResult, nameof(blurResult.Offset)); } } else if (distance.IsAnimated) { // Only distance is animated. var directionRadians = direction.InitialValue.Radians; var keyFrames = distance.KeyFrames.Select( kf => new KeyFrame <Vector3>(kf.Frame, VectorFromRotationAndDistance(directionRadians, kf.Value), kf.Easing)).ToArray(); var distanceAnimation = new TrimmedAnimatable <Vector3>(context, keyFrames[0].Value, keyFrames); Animate.Vector3(context, distanceAnimation, blurResult, nameof(blurResult.Offset)); } else { // Direction and distance are both not animated. var directionRadians = direction.InitialValue.Radians; var distanceValue = distance.InitialValue; blurResult.Offset = ConvertTo.Vector3(VectorFromRotationAndDistance(direction.InitialValue, distance.InitialValue)); } var result = factory.CreateContainerVisual(); result.Size = size; result.Children.Add(blurResult); // Check if ShadowOnly can be false if (!dropShadowEffect.IsShadowOnly.IsAlways(true)) { // Check if ShadowOnly can be true if (!dropShadowEffect.IsShadowOnly.IsAlways(false)) { var isVisible = FlipBoolAnimatable(dropShadowEffect.IsShadowOnly); // isVisible = !isShadowOnly source.IsVisible = isVisible.InitialValue; if (isVisible.IsAnimated) { Animate.Boolean( context, Optimizer.TrimAnimatable(context, isVisible), source, nameof(blurResult.IsVisible)); } } result.Children.Add(source); } return(result); }