private SkiaGeometrySource2D GetGeometry(Size finalSize) { var strokeThickness = StrokeThickness; var radiusX = RadiusX; var radiusY = RadiusY; var offset = new Vector2((float)(strokeThickness * 0.5), (float)(strokeThickness * 0.5)); var size = new Vector2((float)finalSize.Width, (float)finalSize.Height); SkiaGeometrySource2D geometry; if (radiusX == 0 || radiusY == 0) { // Simple rectangle geometry = new SkiaGeometrySource2D( CompositionGeometry.BuildRectangleGeometry( offset, size)); } else { // Complex rectangle geometry = new SkiaGeometrySource2D( CompositionGeometry.BuildRoundedRectangleGeometry( offset, size, new Vector2((float)radiusX, (float)radiusY))); } return(geometry); }
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix) { var result = _c.CreateSpriteShape(geometry); result.TransformMatrix = transformMatrix; return(result); }
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix, CompositionBrush fillBrush) { var result = _c.CreateSpriteShape(geometry); result.TransformMatrix = transformMatrix; result.FillBrush = fillBrush; return(result); }
protected override void OnApplyTemplate() { Indicator = (ProgressBarRingSlice)GetTemplateChild("Indicator"); Rotation = (RotateTransform)GetTemplateChild("Rotation"); if (Indicator != null) { OnApplyLegacyTemplate(); } else if (ApiInfo.CanUseDirectComposition) { var ellipse = Window.Current.Compositor.CreateEllipseGeometry(); ellipse.Radius = new Vector2(21); ellipse.Center = new Vector2(24); ellipse.TrimEnd = 0f; var shape = Window.Current.Compositor.CreateSpriteShape(ellipse); shape.CenterPoint = new Vector2(24); shape.StrokeThickness = 2; shape.StrokeBrush = Window.Current.Compositor.CreateColorBrush(Windows.UI.Colors.Red); shape.StrokeStartCap = CompositionStrokeCap.Round; shape.StrokeEndCap = CompositionStrokeCap.Round; var visual = Window.Current.Compositor.CreateShapeVisual(); visual.Shapes.Add(shape); visual.Size = new Vector2(48); visual.CenterPoint = new Vector3(24); var easing = Window.Current.Compositor.CreateLinearEasingFunction(); var forever = Window.Current.Compositor.CreateScalarKeyFrameAnimation(); forever.InsertKeyFrame(1, 360, easing); forever.IterationBehavior = AnimationIterationBehavior.Forever; forever.Duration = TimeSpan.FromSeconds(3); var trimEnd = Window.Current.Compositor.CreateScalarKeyFrameAnimation(); trimEnd.Target = nameof(CompositionGeometry.TrimEnd); trimEnd.InsertExpressionKeyFrame(1.0f, "this.FinalValue", Window.Current.Compositor.CreateLinearEasingFunction()); var visibility = Window.Current.Compositor.CreateExpressionAnimation("target.TrimEnd < 1"); visibility.SetReferenceParameter("target", ellipse); var animations = Window.Current.Compositor.CreateImplicitAnimationCollection(); animations[nameof(CompositionGeometry.TrimEnd)] = trimEnd; ellipse.ImplicitAnimations = animations; visual.StartAnimation("IsVisible", visibility); visual.StartAnimation("RotationAngleInDegrees", forever); _ellipse = ellipse; ElementCompositionPreview.SetElementChildVisual(this, visual); } }
private void DrawShapeGeometry(CompositionGeometry geometry, IShape shape) { CompositionSpriteShape spriteShape = _compositor.CreateSpriteShape(geometry); IBrush?fill = shape.Fill; if (fill != null) { spriteShape.FillBrush = fill.ToCompositionBrush(_compositor); } IBrush?stroke = shape.Stroke; if (stroke != null) { spriteShape.StrokeBrush = stroke.ToCompositionBrush(_compositor); spriteShape.StrokeThickness = (float)shape.StrokeThickness; CompositionStrokeCap strokeCap = shape.StrokeLineCap switch { PenLineCap.Flat => CompositionStrokeCap.Flat, PenLineCap.Round => CompositionStrokeCap.Round, PenLineCap.Square => CompositionStrokeCap.Square, _ => throw new InvalidOperationException($"Unknown PenLineCap value {shape.StrokeLineCap}") }; spriteShape.StrokeStartCap = strokeCap; spriteShape.StrokeEndCap = strokeCap; spriteShape.StrokeLineJoin = shape.StrokeLineJoin switch { PenLineJoin.Miter => CompositionStrokeLineJoin.Miter, PenLineJoin.Bevel => CompositionStrokeLineJoin.Bevel, PenLineJoin.Round => CompositionStrokeLineJoin.Round, _ => throw new InvalidOperationException($"Unknown PenLineJoin value {shape.StrokeLineJoin}") }; // TODO: Check that miter limit definition matches StandardUI (half thickness vs full thickness) spriteShape.StrokeMiterLimit = (float)shape.StrokeMiterLimit; // TODO: Handle dash pattern } if (_shapeVisual == null) { _shapeVisual = _compositor.CreateShapeVisual(); } _shapeVisual.Shapes.Add(spriteShape); }
// Remove redundant TrimEnd, TrimOffset, and TrimStart properties. static void OptimizeGeometryProperties(CompositionGeometry obj) { // Unset properties that are set to their default values. if (obj.TrimEnd == 1) { obj.TrimEnd = null; } if (obj.TrimOffset == 0) { obj.TrimOffset = null; } if (obj.TrimStart == 0) { obj.TrimStart = null; } }
CompositionGeometry GetCompositionGeometry(CompositionGeometry obj) { switch (obj.Type) { case CompositionObjectType.CompositionPathGeometry: return(GetCompositionPathGeometry((CompositionPathGeometry)obj)); case CompositionObjectType.CompositionEllipseGeometry: return(GetCompositionEllipseGeometry((CompositionEllipseGeometry)obj)); case CompositionObjectType.CompositionRectangleGeometry: return(GetCompositionRectangleGeometry((CompositionRectangleGeometry)obj)); case CompositionObjectType.CompositionRoundedRectangleGeometry: return(GetCompositionRoundedRectangleGeometry((CompositionRoundedRectangleGeometry)obj)); default: throw new InvalidOperationException(); } }
T CacheAndInitializeCompositionGeometry <T>(CompositionGeometry source, T target) where T : CompositionGeometry { CacheAndInitializeCompositionObject(source, target); if (source.TrimStart != 0) { target.TrimStart = source.TrimStart; } if (source.TrimEnd != 1) { target.TrimEnd = source.TrimEnd; } if (source.TrimOffset != 0) { target.TrimOffset = source.TrimOffset; } return(target); }
internal static PropertyId GetNonDefaultGeometryProperties(CompositionGeometry obj) { var result = PropertyId.None; if (obj.TrimStart.HasValue) { result |= PropertyId.TrimStart; } if (obj.TrimEnd.HasValue) { result |= PropertyId.TrimEnd; } if (obj.TrimOffset.HasValue) { result |= PropertyId.TrimOffset; } return(result | GetNonDefaultCompositionObjectProperties(obj)); }
IEnumerable <XObject> GetCompositionGeometryContents(CompositionGeometry obj) { foreach (var item in GetCompositionObjectContents(obj)) { yield return(item); } foreach (var item in FromAnimatableScalar(nameof(obj.TrimStart), obj.Animators, obj.TrimStart)) { yield return(item); } foreach (var item in FromAnimatableScalar(nameof(obj.TrimEnd), obj.Animators, obj.TrimEnd)) { yield return(item); } foreach (var item in FromAnimatableScalar(nameof(obj.TrimOffset), obj.Animators, obj.TrimOffset)) { yield return(item); } }
internal override SKPath GetSKPath() => CompositionGeometry.BuildLineGeometry(StartPoint.ToVector2(), EndPoint.ToVector2());
internal override SKPath GetSKPath() => CompositionGeometry.BuildRectangleGeometry(offset: new Vector2((float)Rect.X, (float)Rect.Y), size: new Vector2((float)Rect.Width, (float)Rect.Height));
private void InitializeAnimation() { if (!ApiInfo.CanUseDirectComposition) { return; } var width = 18f; var height = 10f; var stroke = 2f; var distance = stroke * 2; var sqrt = (float)Math.Sqrt(2); var side = (stroke / sqrt) / 2f; var diagonal = height * sqrt; var length = (diagonal / 2f) / sqrt; var join = stroke / 2 * sqrt; var line11 = Window.Current.Compositor.CreateLineGeometry(); var line12 = Window.Current.Compositor.CreateLineGeometry(); line11.Start = new Vector2(width - height + side + join - length - distance, height - side - length); line11.End = new Vector2(width - height + side + join - distance, height - side); line12.Start = new Vector2(width - height + side - distance, height - side); line12.End = new Vector2(width - side - distance, side); var shape11 = Window.Current.Compositor.CreateSpriteShape(line11); shape11.StrokeThickness = 2; shape11.StrokeBrush = Window.Current.Compositor.CreateColorBrush(Windows.UI.Colors.Black); shape11.IsStrokeNonScaling = true; var shape12 = Window.Current.Compositor.CreateSpriteShape(line12); shape12.StrokeThickness = 2; shape12.StrokeBrush = Window.Current.Compositor.CreateColorBrush(Windows.UI.Colors.Black); shape12.IsStrokeNonScaling = true; var visual1 = Window.Current.Compositor.CreateShapeVisual(); visual1.Shapes.Add(shape12); visual1.Shapes.Add(shape11); visual1.Size = new Vector2(18, 10); visual1.CenterPoint = new Vector3(18, 5, 0); var line21 = Window.Current.Compositor.CreateLineGeometry(); var line22 = Window.Current.Compositor.CreateLineGeometry(); line21.Start = new Vector2(width - height + side + join - length, height - side - length); line21.End = new Vector2(width - height + side + join, height - side); line22.Start = new Vector2(width - height + side, height - side); line22.End = new Vector2(width - side, side); var shape21 = Window.Current.Compositor.CreateSpriteShape(line21); shape21.StrokeThickness = 2; shape21.StrokeBrush = Window.Current.Compositor.CreateColorBrush(Windows.UI.Colors.Black); var shape22 = Window.Current.Compositor.CreateSpriteShape(line22); shape22.StrokeThickness = 2; shape22.StrokeBrush = Window.Current.Compositor.CreateColorBrush(Windows.UI.Colors.Black); var visual2 = Window.Current.Compositor.CreateShapeVisual(); visual2.Shapes.Add(shape22); visual2.Shapes.Add(shape21); visual2.Size = new Vector2(18, 10); var container = Window.Current.Compositor.CreateContainerVisual(); container.Children.InsertAtTop(visual2); container.Children.InsertAtTop(visual1); container.Size = new Vector2(18, 10); ElementCompositionPreview.SetElementChildVisual(Label, container); _line11 = line11; _line12 = line12; _line21 = line21; _line22 = line22; _shapes = new[] { shape11, shape12, shape21, shape22 }; _visual1 = visual1; _container = container; }
internal CompositionSpriteShape(CompositionGeometry geometry = null) { Geometry = geometry; }
RectangleOrRoundedRectangleGeometry(CompositionGeometry compositionGeometry) { _compositionGeometry = compositionGeometry; }
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); } } }
internal override SKPath GetSKPath() => CompositionGeometry.BuildEllipseGeometry(Center.ToVector2(), new Vector2((float)RadiusX, (float)RadiusY));