From f663555948122f09e08489b78e502537bc7bb5e0 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 27 Jan 2024 20:15:52 +0200 Subject: [PATCH 01/31] feat(composition): Initial work on Composition Paths --- .../Interop/Direct2D/D2D1ArcSegment.cs | 13 +++++++++++ .../Graphics/Interop/Direct2D/D2D1ArcSize.cs | 7 ++++++ .../Interop/Direct2D/D2D1BezierSegment.cs | 11 ++++++++++ .../Graphics/Interop/Direct2D/D2D1Ellipse.cs | 11 ++++++++++ .../Interop/Direct2D/D2D1FigureBegin.cs | 7 ++++++ .../Interop/Direct2D/D2D1FigureEnd.cs | 7 ++++++ .../Graphics/Interop/Direct2D/D2D1FillMode.cs | 7 ++++++ .../Interop/Direct2D/D2D1PathSegment.cs | 8 +++++++ .../Direct2D/D2D1QuadraticBezierSegment.cs | 10 +++++++++ .../Interop/Direct2D/D2D1RoundedRect.cs | 11 ++++++++++ .../Interop/Direct2D/D2D1SweepDirection.cs | 7 ++++++ .../Interop/Direct2D/ID2D1EllipseGeometry.cs | 11 ++++++++++ .../Interop/Direct2D/ID2D1Geometry.cs | 8 +++++++ .../Interop/Direct2D/ID2D1GeometrySink.cs | 18 +++++++++++++++ .../Interop/Direct2D/ID2D1PathGeometry.cs | 17 ++++++++++++++ .../Direct2D/ID2D1RectangleGeometry.cs | 11 ++++++++++ .../Direct2D/ID2D1RoundedRectangleGeometry.cs | 11 ++++++++++ .../Direct2D/ID2D1SimplifiedGeometrySink.cs | 22 +++++++++++++++++++ .../Interop/IGeometrySource2DInterop.cs | 14 ++++++++++++ 19 files changed, 211 insertions(+) create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSegment.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSize.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1BezierSegment.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Ellipse.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureBegin.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureEnd.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FillMode.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1QuadraticBezierSegment.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1RoundedRect.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1SweepDirection.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1EllipseGeometry.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1Geometry.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1GeometrySink.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1PathGeometry.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RectangleGeometry.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RoundedRectangleGeometry.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1SimplifiedGeometrySink.cs create mode 100644 src/Uno.UWP/Graphics/Interop/IGeometrySource2DInterop.cs diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSegment.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSegment.cs new file mode 100644 index 000000000000..82c1044056af --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSegment.cs @@ -0,0 +1,13 @@ +using System; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal struct D2D1ArcSegment +{ + public Point Point; + public Size Size; + public float RotationAngle; + public D2D1SweepDirection SweepDirection; + public D2D1ArcSize ArcSize; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSize.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSize.cs new file mode 100644 index 000000000000..8c8c31f3c9d8 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1ArcSize.cs @@ -0,0 +1,7 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1ArcSize +{ + Small = 0, + Large = 1 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1BezierSegment.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1BezierSegment.cs new file mode 100644 index 000000000000..2d2c0ace0edc --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1BezierSegment.cs @@ -0,0 +1,11 @@ +using System; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal struct D2D1BezierSegment +{ + public Point Point1; + public Point Point2; + public Point Point3; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Ellipse.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Ellipse.cs new file mode 100644 index 000000000000..156b9e0e69bb --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Ellipse.cs @@ -0,0 +1,11 @@ +using System; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal struct D2D1Ellipse +{ + public Point Point; + public float RadiusX; + public float RadiusY; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureBegin.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureBegin.cs new file mode 100644 index 000000000000..60a608e0204d --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureBegin.cs @@ -0,0 +1,7 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1FigureBegin +{ + Filled = 0, + Hollow = 1 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureEnd.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureEnd.cs new file mode 100644 index 000000000000..307912257dd2 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FigureEnd.cs @@ -0,0 +1,7 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1FigureEnd +{ + Open = 0, + Closed = 1 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FillMode.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FillMode.cs new file mode 100644 index 000000000000..256b1cea394b --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1FillMode.cs @@ -0,0 +1,7 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1FillMode +{ + Alternate = 0, + Winding = 1 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs new file mode 100644 index 000000000000..1548cbb663bf --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs @@ -0,0 +1,8 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1PathSegment +{ + None = 0, + ForceUnstroked = 1, + ForceRoundLineJoin = 2 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1QuadraticBezierSegment.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1QuadraticBezierSegment.cs new file mode 100644 index 000000000000..c1a50b21475a --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1QuadraticBezierSegment.cs @@ -0,0 +1,10 @@ +using System; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal struct D2D1QuadraticBezierSegment +{ + public Point Point1; + public Point Point2; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1RoundedRect.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1RoundedRect.cs new file mode 100644 index 000000000000..fc191380fe0e --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1RoundedRect.cs @@ -0,0 +1,11 @@ +using System; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal struct D2D1RoundedRect +{ + public Rect Rect; + public float RadiusX; + public float RadiusY; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1SweepDirection.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1SweepDirection.cs new file mode 100644 index 000000000000..28cd22e480b7 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1SweepDirection.cs @@ -0,0 +1,7 @@ +namespace Windows.Graphics.Interop.Direct2D; + +internal enum D2D1SweepDirection +{ + CounterClockwise = 0, + Clockwise = 1 +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1EllipseGeometry.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1EllipseGeometry.cs new file mode 100644 index 000000000000..db06a8dec57a --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1EllipseGeometry.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A2-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1EllipseGeometry : ID2D1Geometry +{ + D2D1Ellipse GetEllipse(); +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1Geometry.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1Geometry.cs new file mode 100644 index 000000000000..744a7deba0b9 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1Geometry.cs @@ -0,0 +1,8 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A1-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1Geometry { } diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1GeometrySink.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1GeometrySink.cs new file mode 100644 index 000000000000..c54e6fbb3783 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1GeometrySink.cs @@ -0,0 +1,18 @@ +using Windows.Foundation; +using System.Runtime.InteropServices; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A2-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1GeometrySink : ID2D1SimplifiedGeometrySink +{ + void AddLine(Point point); + + void AddBezier(D2D1BezierSegment bezier); + + void AddQuadraticBezier(D2D1QuadraticBezierSegment bezier); + + void AddQuadraticBeziers(D2D1QuadraticBezierSegment[] beziers); + + void AddArc(D2D1ArcSegment arc); +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1PathGeometry.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1PathGeometry.cs new file mode 100644 index 000000000000..414dd7349d6c --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1PathGeometry.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A2-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1PathGeometry : ID2D1Geometry +{ + ID2D1GeometrySink Open(); + + void Stream(ID2D1GeometrySink geometrySink); + + uint GetSegmentCount(); + + uint GetFigureCount(); +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RectangleGeometry.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RectangleGeometry.cs new file mode 100644 index 000000000000..d9959fc905de --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RectangleGeometry.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A2-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1RectangleGeometry : ID2D1Geometry +{ + Rect GetRect(); +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RoundedRectangleGeometry.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RoundedRectangleGeometry.cs new file mode 100644 index 000000000000..5f7311d22f88 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1RoundedRectangleGeometry.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A3-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1RoundedRectangleGeometry : ID2D1Geometry +{ + D2D1RoundedRect GetRoundedRect(); +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1SimplifiedGeometrySink.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1SimplifiedGeometrySink.cs new file mode 100644 index 000000000000..eec0170814e4 --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/ID2D1SimplifiedGeometrySink.cs @@ -0,0 +1,22 @@ +using Windows.Foundation; +using System.Runtime.InteropServices; + +namespace Windows.Graphics.Interop.Direct2D; + +[Guid("2CD906A2-12E2-11DC-9FED-001143A055F9")] +internal interface ID2D1SimplifiedGeometrySink +{ + void SetFillMode(D2D1FillMode fillMode); + + void SetSegmentFlags(D2D1PathSegment vertexFlags); + + void BeginFigure(Point startPoint, D2D1FigureBegin figureBegin); + + void AddLines(Point[] points); + + void AddBeziers(D2D1BezierSegment[] beziers); + + void EndFigure(D2D1FigureEnd figureEnd); + + void Close(); +} diff --git a/src/Uno.UWP/Graphics/Interop/IGeometrySource2DInterop.cs b/src/Uno.UWP/Graphics/Interop/IGeometrySource2DInterop.cs new file mode 100644 index 000000000000..9bc1bbd6310f --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/IGeometrySource2DInterop.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; +using Windows.Graphics.Interop.Direct2D; +using Windows.Foundation; + +namespace Windows.Graphics.Interop; + +[Guid("0657AF73-53FD-47CF-84FF-C8492D2A80A3")] +internal interface IGeometrySource2DInterop +{ + ID2D1Geometry GetGeometry(); + + ID2D1Geometry TryGetGeometryUsingFactory(/*ID2D1Factory*/ object factory); +} From 0b3d8ee6b687f3a6340a1d2ebe49698404361c55 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Tue, 30 Jan 2024 03:21:38 +0200 Subject: [PATCH 02/31] feat(composition): Partially implement CompositionPathGeometry --- .../Composition/CompositionPathGeometry.cs | 2 - .../CompositionPathGeometry.skia.cs | 212 ++++++++++++++++++ .../Composition/SkiaGeometrySource2D.skia.cs | 4 +- .../Composition/Uno/CompositionPathCommand.cs | 48 ++++ .../Uno/CompositionPathCommandType.cs | 23 ++ .../Interop/Direct2D/D2D1Helpers.skia.cs | 21 ++ .../Interop/Direct2D/D2D1PathSegment.cs | 5 +- 7 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs create mode 100644 src/Uno.UI.Composition/Composition/Uno/CompositionPathCommand.cs create mode 100644 src/Uno.UI.Composition/Composition/Uno/CompositionPathCommandType.cs create mode 100644 src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.cs index 8cf6c0354265..bb422a8e6892 100644 --- a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.cs +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.cs @@ -18,7 +18,5 @@ public CompositionPath? Path get => _path; set => SetObjectProperty(ref _path, value); } - - internal override IGeometrySource2D? BuildGeometry() => Path?.GeometrySource; } } diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs new file mode 100644 index 000000000000..4f659b341525 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs @@ -0,0 +1,212 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Numerics; +using SkiaSharp; +using Uno.UI.Composition; +using Windows.Foundation; +using Windows.Graphics; +using Windows.Graphics.Interop.Direct2D; +using static Uno.FoundationFeatureConfiguration; + +namespace Microsoft.UI.Composition; + +public partial class CompositionPathGeometry : CompositionGeometry, ID2D1GeometrySink +{ + private SkiaGeometrySource2D? _geometrySource2D; + private List _commands = new(); + + internal override IGeometrySource2D? BuildGeometry() => _geometrySource2D; + + private void InternalBuildGeometry() + { + SkiaGeometrySource2D? geometrySource = null; + + switch (Path?.GeometrySource.GetType()) + { + case ID2D1RectangleGeometry rectangleGeometry: + { + var rect = rectangleGeometry.GetRect(); + geometrySource = new(BuildRectangleGeometry(rect.Location.ToVector2(), rect.Size.ToVector2())); + break; + } + case ID2D1RoundedRectangleGeometry roundedRectangleGeometry: + { + var rect = roundedRectangleGeometry.GetRoundedRect(); + geometrySource = new(BuildRoundedRectangleGeometry(rect.Rect.Location.ToVector2(), rect.Rect.Size.ToVector2(), new(rect.RadiusX, rect.RadiusY))); + break; + } + case ID2D1EllipseGeometry ellipseGeometry: + { + var ellipse = ellipseGeometry.GetEllipse(); + geometrySource = new(BuildEllipseGeometry(ellipse.Point.ToVector2(), new(ellipse.RadiusX, ellipse.RadiusY))); + break; + } + case ID2D1PathGeometry pathGeometry: + { + geometrySource = InternalBuildPathGeometry(pathGeometry); + break; + } + default: + throw new InvalidOperationException($"Path geometry source type {Path?.GeometrySource.GetType()} is no supported"); + } + + _geometrySource2D?.Dispose(); + _geometrySource2D = geometrySource; + _commands.Clear(); + } + + private SkiaGeometrySource2D? InternalBuildPathGeometry(ID2D1PathGeometry pathGeometry) + { + pathGeometry.Stream(this); + + SKPath path = new(); + foreach (var command in _commands) + { + switch (command.Type) + { + case CompositionPathCommandType.SetFillMode: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + path.FillType = ((D2D1FillMode)parameters[0]).ToSkia(); + break; + } + case CompositionPathCommandType.SetSegmentFlags: + break; // TODO + case CompositionPathCommandType.BeginFigure: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 2); + var point = (Point)parameters[0]; + path.MoveTo(point.ToSkia()); + break; + } + case CompositionPathCommandType.AddLine: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var point = (Point)parameters[0]; + path.LineTo(point.ToSkia()); + break; + } + case CompositionPathCommandType.AddLines: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var points = (Point[])parameters[0]; + + foreach (var point in points) + { + path.LineTo(point.ToSkia()); + } + + break; + } + case CompositionPathCommandType.AddBezier: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var bezier = (D2D1BezierSegment)parameters[0]; + path.CubicTo(bezier.Point1.ToSkia(), bezier.Point2.ToSkia(), bezier.Point3.ToSkia()); + break; + } + case CompositionPathCommandType.AddBeziers: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var beziers = (D2D1BezierSegment[])parameters[0]; + + foreach (var bezier in beziers) + { + path.CubicTo(bezier.Point1.ToSkia(), bezier.Point2.ToSkia(), bezier.Point3.ToSkia()); + } + + break; + } + case CompositionPathCommandType.AddQuadraticBezier: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var bezier = (D2D1QuadraticBezierSegment)parameters[0]; + path.QuadTo(bezier.Point1.ToSkia(), bezier.Point2.ToSkia()); + break; + } + case CompositionPathCommandType.AddQuadraticBeziers: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var beziers = (D2D1QuadraticBezierSegment[])parameters[0]; + + foreach (var bezier in beziers) + { + path.QuadTo(bezier.Point1.ToSkia(), bezier.Point2.ToSkia()); + } + + break; + } + case CompositionPathCommandType.AddArc: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var arc = (D2D1ArcSegment)parameters[0]; + path.ArcTo(new((float)arc.Size.Width, (float)arc.Size.Height), arc.RotationAngle, arc.ArcSize.ToSkia(), arc.SweepDirection.ToSkia(), arc.Point.ToSkia()); + break; + } + case CompositionPathCommandType.EndFigure: + { + var parameters = ValidateCommandParameters(command, expectedParameterCount: 1); + var end = (D2D1FigureEnd)parameters[0]; + + if (end is D2D1FigureEnd.Closed) + { + path.Close(); + } + + break; + } + case CompositionPathCommandType.Close: + break; // We don't actually have a sink to close, so we can ignore this + } + } + + return new SkiaGeometrySource2D(path); + } + + private static object[] ValidateCommandParameters(CompositionPathCommand command, int expectedParameterCount) + { + if (command.Parameters is null || command.Parameters.Length != expectedParameterCount) + { + throw new InvalidOperationException("Unexpected path command parameters value"); + } + + return command.Parameters; + } + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + if (propertyName is nameof(Path)) + { + _commands.Clear(); + InternalBuildGeometry(); + } + + base.OnPropertyChangedCore(propertyName, isSubPropertyChange); + } + + void ID2D1GeometrySink.AddLine(Point point) => _commands.Add(CompositionPathCommand.Create(point)); + + void ID2D1GeometrySink.AddBezier(D2D1BezierSegment bezier) => _commands.Add(CompositionPathCommand.Create(bezier)); + + void ID2D1GeometrySink.AddQuadraticBezier(D2D1QuadraticBezierSegment bezier) => _commands.Add(CompositionPathCommand.Create(bezier)); + + void ID2D1GeometrySink.AddQuadraticBeziers(D2D1QuadraticBezierSegment[] beziers) => _commands.Add(CompositionPathCommand.Create(beziers)); + + void ID2D1GeometrySink.AddArc(D2D1ArcSegment arc) => _commands.Add(CompositionPathCommand.Create(arc)); + + void ID2D1SimplifiedGeometrySink.SetFillMode(D2D1FillMode fillMode) => _commands.Add(CompositionPathCommand.Create(fillMode)); + + void ID2D1SimplifiedGeometrySink.SetSegmentFlags(D2D1PathSegment vertexFlags) => _commands.Add(CompositionPathCommand.Create(vertexFlags)); + + void ID2D1SimplifiedGeometrySink.BeginFigure(Point startPoint, D2D1FigureBegin figureBegin) => _commands.Add(CompositionPathCommand.Create(startPoint, figureBegin)); + + void ID2D1SimplifiedGeometrySink.AddLines(Point[] points) => _commands.Add(CompositionPathCommand.Create(points)); + + void ID2D1SimplifiedGeometrySink.AddBeziers(D2D1BezierSegment[] beziers) => _commands.Add(CompositionPathCommand.Create(beziers)); + + void ID2D1SimplifiedGeometrySink.EndFigure(D2D1FigureEnd figureEnd) => _commands.Add(CompositionPathCommand.Create(figureEnd)); + + void ID2D1SimplifiedGeometrySink.Close() => _commands.Add(CompositionPathCommand.Create()); +} diff --git a/src/Uno.UI.Composition/Composition/SkiaGeometrySource2D.skia.cs b/src/Uno.UI.Composition/Composition/SkiaGeometrySource2D.skia.cs index e1872a244684..1f324d2c535e 100644 --- a/src/Uno.UI.Composition/Composition/SkiaGeometrySource2D.skia.cs +++ b/src/Uno.UI.Composition/Composition/SkiaGeometrySource2D.skia.cs @@ -6,7 +6,7 @@ namespace Microsoft.UI.Composition { - internal class SkiaGeometrySource2D : IGeometrySource2D + public class SkiaGeometrySource2D : IGeometrySource2D, IDisposable { public SkiaGeometrySource2D(SKPath source) { @@ -18,5 +18,7 @@ public SkiaGeometrySource2D(SKPath source) /// This can lead to nasty invalidation bugs where the SKPath changes without notifying anyone. /// public SKPath Geometry { get; } + + public void Dispose() => Geometry.Dispose(); } } diff --git a/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommand.cs b/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommand.cs new file mode 100644 index 000000000000..c651c0e91b03 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommand.cs @@ -0,0 +1,48 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Graphics.Interop.Direct2D; + +namespace Uno.UI.Composition; + +internal class CompositionPathCommand +{ + public CompositionPathCommandType Type { get; private set; } + + public object[]? Parameters { get; private set; } + + private CompositionPathCommand(CompositionPathCommandType type, object[]? parameters = null) + { + Type = type; + Parameters = parameters; + } + + public static CompositionPathCommand Create(D2D1FillMode fillMode) => new(CompositionPathCommandType.SetFillMode, [fillMode]); + + public static CompositionPathCommand Create(D2D1PathSegment vertexFlags) => new(CompositionPathCommandType.SetSegmentFlags, [vertexFlags]); + + public static CompositionPathCommand Create(Point startPoint, D2D1FigureBegin figureBegin) => new(CompositionPathCommandType.BeginFigure, [startPoint, figureBegin]); + + public static CompositionPathCommand Create(Point point) => new(CompositionPathCommandType.AddLine, [point]); + + public static CompositionPathCommand Create(Point[] points) => new(CompositionPathCommandType.AddLines, [points]); + + public static CompositionPathCommand Create(D2D1BezierSegment bezier) => new(CompositionPathCommandType.AddBezier, [bezier]); + + public static CompositionPathCommand Create(D2D1BezierSegment[] beziers) => new(CompositionPathCommandType.AddBeziers, [beziers]); + + public static CompositionPathCommand Create(D2D1QuadraticBezierSegment bezier) => new(CompositionPathCommandType.AddQuadraticBezier, [bezier]); + + public static CompositionPathCommand Create(D2D1QuadraticBezierSegment[] beziers) => new(CompositionPathCommandType.AddQuadraticBeziers, [beziers]); + + public static CompositionPathCommand Create(D2D1ArcSegment arc) => new(CompositionPathCommandType.AddArc, [arc]); + + public static CompositionPathCommand Create(D2D1FigureEnd figureEnd) => new(CompositionPathCommandType.EndFigure, [figureEnd]); + + public static CompositionPathCommand Create() => new(CompositionPathCommandType.Close); +} diff --git a/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommandType.cs b/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommandType.cs new file mode 100644 index 000000000000..30f601b02681 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/Uno/CompositionPathCommandType.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Uno.UI.Composition; + +internal enum CompositionPathCommandType +{ + SetFillMode, + SetSegmentFlags, + BeginFigure, + AddLine, + AddLines, + AddBezier, + AddBeziers, + AddQuadraticBezier, + AddQuadraticBeziers, + AddArc, + EndFigure, + Close +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs new file mode 100644 index 000000000000..c98cdcf7206c --- /dev/null +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SkiaSharp; +using Windows.Foundation; + +namespace Windows.Graphics.Interop.Direct2D; + +internal static partial class D2D1Helpers +{ + public static SKPathFillType ToSkia(this D2D1FillMode mode) => mode is D2D1FillMode.Alternate ? SKPathFillType.EvenOdd : SKPathFillType.Winding; + + public static SKPoint ToSkia(this Point point) => new((float)point.X, (float)point.Y); + + // This is not a mistake, enum members are in the reverse order + public static SKPathDirection ToSkia(this D2D1SweepDirection direction) => direction is D2D1SweepDirection.CounterClockwise ? SKPathDirection.Clockwise : SKPathDirection.CounterClockwise; + + public static SKPathArcSize ToSkia(this D2D1ArcSize size) => (SKPathArcSize)size; +} diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs index 1548cbb663bf..af8d6b63afab 100644 --- a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1PathSegment.cs @@ -1,5 +1,8 @@ -namespace Windows.Graphics.Interop.Direct2D; +using System; +namespace Windows.Graphics.Interop.Direct2D; + +[Flags] internal enum D2D1PathSegment { None = 0, From 0c16861aad5135ff18fa589ae80b5f5cba53db3c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Wed, 31 Jan 2024 03:25:55 +0200 Subject: [PATCH 03/31] feat(composition): Implemented CanvasPathBuilder + few fixes for CompositionPathGeometry --- .../CompositionPathGeometry.skia.cs | 73 ++++---- .../Uno/ICompositionPathCommandsProvider.cs | 14 ++ .../Microsoft/Graphics/Canvas/CanvasDevice.cs | 20 +++ .../Graphics/Canvas/Geometry/CanvasArcSize.cs | 13 ++ .../Canvas/Geometry/CanvasFigureFill.cs | 7 + .../Canvas/Geometry/CanvasFigureLoop.cs | 7 + .../Geometry/CanvasFigureSegmentOptions.cs | 11 ++ .../CanvasFilledRegionDetermination.cs | 7 + .../Canvas/Geometry/CanvasPathBuilder.cs | 156 ++++++++++++++++++ .../Canvas/Geometry/CanvasSweepDirection.cs | 13 ++ .../Graphics/Canvas/ICanvasResourceCreator.cs | 12 ++ src/Uno.UWP/Extensions/MathEx.cs | 12 ++ 12 files changed, 316 insertions(+), 29 deletions(-) create mode 100644 src/Uno.UI.Composition/Composition/Uno/ICompositionPathCommandsProvider.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/CanvasDevice.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasArcSize.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureFill.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureLoop.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureSegmentOptions.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFilledRegionDetermination.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasSweepDirection.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/ICanvasResourceCreator.cs diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs index 4f659b341525..5cfddf664a7d 100644 --- a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs @@ -7,6 +7,7 @@ using Uno.UI.Composition; using Windows.Foundation; using Windows.Graphics; +using Windows.Graphics.Interop; using Windows.Graphics.Interop.Direct2D; using static Uno.FoundationFeatureConfiguration; @@ -23,33 +24,49 @@ private void InternalBuildGeometry() { SkiaGeometrySource2D? geometrySource = null; - switch (Path?.GeometrySource.GetType()) + if (Path?.GeometrySource is IGeometrySource2DInterop geometrySourceInterop) { - case ID2D1RectangleGeometry rectangleGeometry: - { - var rect = rectangleGeometry.GetRect(); - geometrySource = new(BuildRectangleGeometry(rect.Location.ToVector2(), rect.Size.ToVector2())); - break; - } - case ID2D1RoundedRectangleGeometry roundedRectangleGeometry: - { - var rect = roundedRectangleGeometry.GetRoundedRect(); - geometrySource = new(BuildRoundedRectangleGeometry(rect.Rect.Location.ToVector2(), rect.Rect.Size.ToVector2(), new(rect.RadiusX, rect.RadiusY))); - break; - } - case ID2D1EllipseGeometry ellipseGeometry: - { - var ellipse = ellipseGeometry.GetEllipse(); - geometrySource = new(BuildEllipseGeometry(ellipse.Point.ToVector2(), new(ellipse.RadiusX, ellipse.RadiusY))); - break; - } - case ID2D1PathGeometry pathGeometry: - { - geometrySource = InternalBuildPathGeometry(pathGeometry); - break; - } - default: - throw new InvalidOperationException($"Path geometry source type {Path?.GeometrySource.GetType()} is no supported"); + switch (geometrySourceInterop) + { + case ID2D1RectangleGeometry rectangleGeometry: + { + var rect = rectangleGeometry.GetRect(); + geometrySource = new(BuildRectangleGeometry(rect.Location.ToVector2(), rect.Size.ToVector2())); + break; + } + case ID2D1RoundedRectangleGeometry roundedRectangleGeometry: + { + var rect = roundedRectangleGeometry.GetRoundedRect(); + geometrySource = new(BuildRoundedRectangleGeometry(rect.Rect.Location.ToVector2(), rect.Rect.Size.ToVector2(), new(rect.RadiusX, rect.RadiusY))); + break; + } + case ID2D1EllipseGeometry ellipseGeometry: + { + var ellipse = ellipseGeometry.GetEllipse(); + geometrySource = new(BuildEllipseGeometry(ellipse.Point.ToVector2(), new(ellipse.RadiusX, ellipse.RadiusY))); + break; + } + case ID2D1PathGeometry pathGeometry: + { + if (pathGeometry is ICompositionPathCommandsProvider { Commands: List commands }) + { + _commands.AddRange(commands); + } + else + { + pathGeometry.Stream(this); + } + + geometrySource = InternalBuildPathGeometry(); + break; + } + default: + throw new InvalidOperationException($"Path geometry source type {geometrySourceInterop.GetType().Name} is no supported"); + } + } + else + { + throw new InvalidOperationException($"Path geometry source type doesn't implement IGeometrySource2DInterop"); } _geometrySource2D?.Dispose(); @@ -57,10 +74,8 @@ private void InternalBuildGeometry() _commands.Clear(); } - private SkiaGeometrySource2D? InternalBuildPathGeometry(ID2D1PathGeometry pathGeometry) + private SkiaGeometrySource2D? InternalBuildPathGeometry() { - pathGeometry.Stream(this); - SKPath path = new(); foreach (var command in _commands) { diff --git a/src/Uno.UI.Composition/Composition/Uno/ICompositionPathCommandsProvider.cs b/src/Uno.UI.Composition/Composition/Uno/ICompositionPathCommandsProvider.cs new file mode 100644 index 000000000000..ab4fe78d9db0 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/Uno/ICompositionPathCommandsProvider.cs @@ -0,0 +1,14 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Uno.UI.Composition; + +internal interface ICompositionPathCommandsProvider +{ + List Commands { get; } +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/CanvasDevice.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/CanvasDevice.cs new file mode 100644 index 000000000000..d61965dcd310 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/CanvasDevice.cs @@ -0,0 +1,20 @@ +using System; + +namespace Microsoft.Graphics.Canvas; + +internal class CanvasDevice : ICanvasResourceCreator, IDisposable +{ + private static Lazy _sharedDeviceLazy = new(() => new()); + + public CanvasDevice() { } + + public CanvasDevice(bool forceSoftwareRenderer) { } + + public CanvasDevice Device => this; + + public static CanvasDevice GetSharedDevice() => _sharedDeviceLazy.Value; + + public static CanvasDevice GetSharedDevice(bool forceSoftwareRenderer) => _sharedDeviceLazy.Value; + + public void Dispose() { } +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasArcSize.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasArcSize.cs new file mode 100644 index 000000000000..45463a8ebe07 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasArcSize.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Graphics.Canvas.Geometry; + +internal enum CanvasArcSize +{ + Small = 0, + Large = 1 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureFill.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureFill.cs new file mode 100644 index 000000000000..5d69c0acab19 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureFill.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Graphics.Canvas.Geometry; + +internal enum CanvasFigureFill +{ + Default = 0, + DoesNotAffectFills = 1 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureLoop.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureLoop.cs new file mode 100644 index 000000000000..79efb0f83632 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureLoop.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Graphics.Canvas.Geometry; + +internal enum CanvasFigureLoop +{ + Open = 0, + Closed = 1 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureSegmentOptions.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureSegmentOptions.cs new file mode 100644 index 000000000000..58d18723d837 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFigureSegmentOptions.cs @@ -0,0 +1,11 @@ +using System; + +namespace Microsoft.Graphics.Canvas.Geometry; + +[Flags] +internal enum CanvasFigureSegmentOptions +{ + None = 0, + ForceUnstroked = 1, + ForceRoundLineJoin = 2 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFilledRegionDetermination.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFilledRegionDetermination.cs new file mode 100644 index 000000000000..6e618953be53 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasFilledRegionDetermination.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Graphics.Canvas.Geometry; + +internal enum CanvasFilledRegionDetermination +{ + Alternate = 0, + Winding = 1 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs new file mode 100644 index 000000000000..0e597fe76f4d --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs @@ -0,0 +1,156 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; +using Windows.Foundation; +using Windows.Graphics.Interop.Direct2D; + +namespace Microsoft.Graphics.Canvas.Geometry; + +internal class CanvasPathBuilder : IDisposable +{ + private List _commands = new List(); + + public CanvasPathBuilder(ICanvasResourceCreator? resourceCreator) { } + + internal List Commands => _commands; + + public void AddArc(Vector2 centerPoint, float radiusX, float radiusY, float startAngle, float sweepAngle) + { + bool isFullCircle = MathF.Abs(sweepAngle) >= (MathF.PI * 2.0f) - float.Epsilon; + + if (isFullCircle) + { + sweepAngle = (sweepAngle < 0) ? -MathF.PI : MathF.PI; + } + + Point startPoint = new() + { + X = centerPoint.X + MathF.Cos(startAngle) * radiusX, + Y = centerPoint.Y + MathF.Sin(startAngle) * radiusY + }; + + Point endPoint = new() + { + X = centerPoint.X + Math.Cos(startAngle + sweepAngle) * radiusX, + Y = centerPoint.Y + MathF.Sin(startAngle + sweepAngle) * radiusY + }; + + D2D1ArcSegment segment = new D2D1ArcSegment() + { + Point = endPoint, + Size = new(radiusX, radiusY), + RotationAngle = 0, + SweepDirection = (sweepAngle >= 0) ? D2D1SweepDirection.Clockwise : D2D1SweepDirection.CounterClockwise, + ArcSize = (MathF.Abs(sweepAngle) > MathF.PI) ? D2D1ArcSize.Large : D2D1ArcSize.Small + }; + + _commands.Add(CompositionPathCommand.Create(startPoint)); + _commands.Add(CompositionPathCommand.Create(segment)); + + if (isFullCircle) + { + segment.Point = startPoint; + _commands.Add(CompositionPathCommand.Create(segment)); + } + } + + public void AddArc(Vector2 endPoint, float radiusX, float radiusY, float rotationAngle, CanvasSweepDirection sweepDirection, CanvasArcSize arcSize) + { + D2D1ArcSegment segment = new D2D1ArcSegment() + { + Point = endPoint.ToPoint(), + Size = new(radiusX, radiusY), + RotationAngle = Uno.Extensions.MathEx.ToDegree(rotationAngle), + SweepDirection = (D2D1SweepDirection)sweepDirection, + ArcSize = (D2D1ArcSize)arcSize + }; + + _commands.Add(CompositionPathCommand.Create(segment)); + } + + public void AddCubicBezier(Vector2 controlPoint1, Vector2 controlPoint2, Vector2 endPoint) + { + D2D1BezierSegment segment = new D2D1BezierSegment() + { + Point1 = controlPoint1.ToPoint(), + Point2 = controlPoint2.ToPoint(), + Point3 = endPoint.ToPoint() + }; + + _commands.Add(CompositionPathCommand.Create(segment)); + } + + // TODO + /*public void AddGeometry(CanvasGeometry geometry) + { + + }*/ + + public void AddLine(Vector2 endPoint) + { + _commands.Add(CompositionPathCommand.Create(endPoint.ToPoint())); + } + + public void AddLine(float x, float y) + { + _commands.Add(CompositionPathCommand.Create(new Point(x, y))); + } + + public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint) + { + D2D1QuadraticBezierSegment segment = new D2D1QuadraticBezierSegment() + { + Point1 = controlPoint.ToPoint(), + Point2 = endPoint.ToPoint() + }; + + _commands.Add(CompositionPathCommand.Create(segment)); + } + + public void BeginFigure(Vector2 startPoint) + { + _commands.Add(CompositionPathCommand.Create(startPoint.ToPoint(), D2D1FigureBegin.Hollow)); + } + + public void BeginFigure(float startX, float startY) + { + _commands.Add(CompositionPathCommand.Create(new(startX, startY), D2D1FigureBegin.Hollow)); + } + + public void BeginFigure(Vector2 startPoint, CanvasFigureFill figureFill) + { + _commands.Add(CompositionPathCommand.Create(startPoint.ToPoint(), (D2D1FigureBegin)figureFill)); + } + + public void BeginFigure(float startX, float startY, CanvasFigureFill figureFill) + { + _commands.Add(CompositionPathCommand.Create(new(startX, startY), (D2D1FigureBegin)figureFill)); + } + + public void EndFigure(CanvasFigureLoop figureLoop) + { + _commands.Add(CompositionPathCommand.Create((D2D1FigureEnd)figureLoop)); + } + + public void SetFilledRegionDetermination(CanvasFilledRegionDetermination filledRegionDetermination) + { + _commands.Add(CompositionPathCommand.Create((D2D1FillMode)filledRegionDetermination)); + } + + public void SetSegmentOptions(CanvasFigureSegmentOptions figureSegmentOptions) + { + _commands.Add(CompositionPathCommand.Create((D2D1PathSegment)figureSegmentOptions)); + } + + public void Dispose() + { + _commands.Clear(); + } +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasSweepDirection.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasSweepDirection.cs new file mode 100644 index 000000000000..6969b375ffe9 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasSweepDirection.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Graphics.Canvas.Geometry; + +internal enum CanvasSweepDirection +{ + CounterClockwise = 0, + Clockwise = 1 +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/ICanvasResourceCreator.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/ICanvasResourceCreator.cs new file mode 100644 index 000000000000..bfa7b6d52aa7 --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/ICanvasResourceCreator.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Graphics.Canvas; + +internal interface ICanvasResourceCreator +{ + CanvasDevice Device { get; } +} diff --git a/src/Uno.UWP/Extensions/MathEx.cs b/src/Uno.UWP/Extensions/MathEx.cs index aa8476888880..95133b11274e 100644 --- a/src/Uno.UWP/Extensions/MathEx.cs +++ b/src/Uno.UWP/Extensions/MathEx.cs @@ -10,12 +10,24 @@ internal static class MathEx public static double ToRadians(double angleDegree) => angleDegree * Math.PI / 180.0; + /// + /// Converts an angle in degree into radians + /// + public static float ToRadians(float angleDegree) + => angleDegree * MathF.PI / 180.0f; + /// /// Converts an angle in radians into degrees /// public static double ToDegree(double angleRadian) => angleRadian * 180.0 / Math.PI; + /// + /// Converts an angle in radians into degrees + /// + public static float ToDegree(float angleRadian) + => angleRadian * 180.0f / MathF.PI; + /// /// Converts an angle in radians into degrees normalized in the [0, 360[ range. /// From 0a5cee0a3445acd6a13377538a3b664d6e787730 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Thu, 1 Feb 2024 01:04:02 +0200 Subject: [PATCH 04/31] feat(composition): Partially implemented CanvasGeometry + UI samples --- .../UITests.Shared/UITests.Shared.projitems | 7 + .../CompositionPathTests.xaml | 15 +++ .../CompositionPathTests.xaml.cs | 121 ++++++++++++++++++ .../CompositionPathGeometry.skia.cs | 12 +- .../Canvas/Geometry/CanvasGeometry.cs | 50 ++++++++ .../Canvas/Geometry/CanvasPathBuilder.cs | 5 +- .../Interop/Direct2D/D2D1Helpers.skia.cs | 4 +- 7 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs create mode 100644 src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 9ae759592ea3..162d690db72d 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -766,6 +766,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -6049,6 +6053,9 @@ + + CompositionPathTests.xaml + CloseRequestedTests.xaml diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml new file mode 100644 index 000000000000..2c1ac8f4d132 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs new file mode 100644 index 000000000000..5681171770a2 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Uno.UI.Samples.Controls; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using Microsoft.UI.Composition; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Graphics.Canvas; +using System.Numerics; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace UITests.Shared.Windows_UI_Composition +{ + [Sample("Microsoft.UI.Composition", Name = "CompositionPath", Description = "Represents a series of connected lines and curves.", IsManualTest = true)] + public sealed partial class CompositionPathTests : UserControl + { + public CompositionPathTests() + { + this.InitializeComponent(); + this.Loaded += CompositionPathTests_Loaded; + } + + private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) + { + var compositor = Microsoft.UI.Xaml.Window.Current.Compositor; + var device = CanvasDevice.GetSharedDevice(); + + using (var builder = new CanvasPathBuilder(device)) + { + var visual = compositor.CreateShapeVisual(); + + builder.BeginFigure(1, 1); + builder.AddLine(200, 200); + builder.AddLine(1, 200); + builder.EndFigure(CanvasFigureLoop.Closed); + + var path = new CompositionPath(CanvasGeometry.CreatePath(builder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightGreen); + + visual.Shapes.Add(shape); + visual.Size = new(200); + + ElementCompositionPreview.SetElementChildVisual(compPresenter1, visual); + } + + using (var builder = new CanvasPathBuilder(device)) + { + var visual = compositor.CreateShapeVisual(); + + builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + builder.BeginFigure(new Vector2(656.5f, 400.5f)); + builder.AddCubicBezier(new Vector2(656.5f, 350.637f), new Vector2(598.572f, 307.493f), new Vector2(514.292f, 286.708f)); + builder.AddCubicBezier(new Vector2(493.507f, 202.428f), new Vector2(450.363f, 144.5f), new Vector2(400.5f, 144.5f)); + builder.AddCubicBezier(new Vector2(350.637f, 144.5f), new Vector2(307.493f, 202.428f), new Vector2(286.708f, 286.708f)); + builder.AddCubicBezier(new Vector2(202.428f, 307.493f), new Vector2(144.5f, 350.637f), new Vector2(144.5f, 400.5f)); + builder.AddCubicBezier(new Vector2(144.5f, 450.363f), new Vector2(202.428f, 493.507f), new Vector2(286.708f, 514.292f)); + builder.AddCubicBezier(new Vector2(307.493f, 598.572f), new Vector2(350.637f, 656.5f), new Vector2(400.5f, 656.5f)); + builder.AddCubicBezier(new Vector2(450.363f, 656.5f), new Vector2(493.507f, 598.572f), new Vector2(514.292f, 514.292f)); + builder.AddCubicBezier(new Vector2(598.572f, 493.507f), new Vector2(656.5f, 450.363f), new Vector2(656.5f, 400.5f)); + builder.EndFigure(CanvasFigureLoop.Closed); + + builder.BeginFigure(new Vector2(581.519f, 219.481f)); + builder.AddCubicBezier(new Vector2(546.261f, 184.222f), new Vector2(474.793f, 194.676f), new Vector2(400.5f, 239.574f)); + builder.AddCubicBezier(new Vector2(326.207f, 194.676f), new Vector2(254.739f, 184.222f), new Vector2(219.481f, 219.481f)); + builder.AddCubicBezier(new Vector2(184.222f, 254.739f), new Vector2(194.676f, 326.207f), new Vector2(239.574f, 400.5f)); + builder.AddCubicBezier(new Vector2(194.676f, 474.792f), new Vector2(184.222f, 546.261f), new Vector2(219.481f, 581.519f)); + builder.AddCubicBezier(new Vector2(254.739f, 616.778f), new Vector2(326.207f, 606.324f), new Vector2(400.5f, 561.426f)); + builder.AddCubicBezier(new Vector2(474.793f, 606.324f), new Vector2(546.261f, 616.778f), new Vector2(581.519f, 581.519f)); + builder.AddCubicBezier(new Vector2(616.778f, 546.261f), new Vector2(606.324f, 474.792f), new Vector2(561.426f, 400.5f)); + builder.AddCubicBezier(new Vector2(606.324f, 326.207f), new Vector2(616.778f, 254.739f), new Vector2(581.519f, 219.481f)); + builder.EndFigure(CanvasFigureLoop.Closed); + + builder.BeginFigure(new Vector2(148.5f, 112.5f)); + builder.AddLine(new Vector2(646.5f, 112.5f)); + builder.AddLine(new Vector2(646.5f, 112.5f)); + builder.AddArc(new Vector2(682.5f, 148.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(682.5f, 646.5f)); + builder.AddLine(new Vector2(682.5f, 646.5f)); + builder.AddArc(new Vector2(646.5f, 682.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(148.5f, 682.5f)); + builder.AddLine(new Vector2(148.5f, 682.5f)); + builder.AddArc(new Vector2(112.5f, 646.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(112.5f, 148.5f)); + builder.AddLine(new Vector2(112.5f, 148.5f)); + builder.AddArc(new Vector2(148.5f, 112.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.EndFigure(CanvasFigureLoop.Closed); + + var path = new CompositionPath(CanvasGeometry.CreatePath(builder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.Transparent); + shape.StrokeBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightGreen); + shape.StrokeThickness = 0.8f; + shape.Scale = new(200f / 570f); + shape.Offset = -112.5f * shape.Scale; + + visual.Shapes.Add(shape); + visual.Size = new(200); + + ElementCompositionPreview.SetElementChildVisual(compPresenter2, visual); + } + } + } +} diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs index 5cfddf664a7d..31710c62be4d 100644 --- a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs @@ -26,7 +26,7 @@ private void InternalBuildGeometry() if (Path?.GeometrySource is IGeometrySource2DInterop geometrySourceInterop) { - switch (geometrySourceInterop) + switch (geometrySourceInterop.GetGeometry()) { case ID2D1RectangleGeometry rectangleGeometry: { @@ -58,20 +58,26 @@ private void InternalBuildGeometry() } geometrySource = InternalBuildPathGeometry(); + _commands.Clear(); + break; } default: throw new InvalidOperationException($"Path geometry source type {geometrySourceInterop.GetType().Name} is no supported"); } + + _geometrySource2D?.Dispose(); + } + else if (Path?.GeometrySource is SkiaGeometrySource2D skiaGeometry) + { + geometrySource = skiaGeometry; } else { throw new InvalidOperationException($"Path geometry source type doesn't implement IGeometrySource2DInterop"); } - _geometrySource2D?.Dispose(); _geometrySource2D = geometrySource; - _commands.Clear(); } private SkiaGeometrySource2D? InternalBuildPathGeometry() diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs new file mode 100644 index 000000000000..365b5c2ec69e --- /dev/null +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs @@ -0,0 +1,50 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using Uno.UI.Composition; +using Windows.Graphics; +using Windows.Graphics.Interop; +using Windows.Graphics.Interop.Direct2D; + +namespace Microsoft.Graphics.Canvas.Geometry; + +internal class CanvasGeometry : IDisposable, IGeometrySource2D, IGeometrySource2DInterop +{ + private ID2D1Geometry _geometry; + + private CanvasGeometry(ID2D1Geometry geometry) => _geometry = geometry ?? throw new ArgumentNullException("geometry"); + + public ID2D1Geometry GetGeometry() => _geometry; + + public ID2D1Geometry? TryGetGeometryUsingFactory(object factory) => null; + + public void Dispose() { } + + + public static CanvasGeometry CreatePath(CanvasPathBuilder pathBuilder) => new(new CanvasPathGeometry(pathBuilder.Commands)); + + private class CanvasPathGeometry : ID2D1PathGeometry, ICompositionPathCommandsProvider + { + private List _commands; + + public CanvasPathGeometry(List commands) => _commands = commands ?? throw new ArgumentNullException("commands"); + + public List Commands => _commands; + + public uint GetFigureCount() => (uint)_commands.Count(x => x.Type is CompositionPathCommandType.EndFigure); + + public uint GetSegmentCount() => (uint)_commands.Count(x => + x.Type is not + (CompositionPathCommandType.Close or + CompositionPathCommandType.SetSegmentFlags or + CompositionPathCommandType.SetFillMode or + CompositionPathCommandType.BeginFigure or + CompositionPathCommandType.EndFigure)); + + public ID2D1GeometrySink Open() => throw new NotImplementedException(); + + public void Stream(ID2D1GeometrySink geometrySink) => throw new NotImplementedException(); // TODO + } +} diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs index 0e597fe76f4d..5810c235656d 100644 --- a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs @@ -149,8 +149,5 @@ public void SetSegmentOptions(CanvasFigureSegmentOptions figureSegmentOptions) _commands.Add(CompositionPathCommand.Create((D2D1PathSegment)figureSegmentOptions)); } - public void Dispose() - { - _commands.Clear(); - } + public void Dispose() { } } diff --git a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs index c98cdcf7206c..c311db8b6fa2 100644 --- a/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs +++ b/src/Uno.UWP/Graphics/Interop/Direct2D/D2D1Helpers.skia.cs @@ -14,8 +14,8 @@ internal static partial class D2D1Helpers public static SKPoint ToSkia(this Point point) => new((float)point.X, (float)point.Y); - // This is not a mistake, enum members are in the reverse order - public static SKPathDirection ToSkia(this D2D1SweepDirection direction) => direction is D2D1SweepDirection.CounterClockwise ? SKPathDirection.Clockwise : SKPathDirection.CounterClockwise; + // Enum members are in the reverse order so we can't cast directly + public static SKPathDirection ToSkia(this D2D1SweepDirection direction) => direction is D2D1SweepDirection.CounterClockwise ? SKPathDirection.CounterClockwise : SKPathDirection.Clockwise; public static SKPathArcSize ToSkia(this D2D1ArcSize size) => (SKPathArcSize)size; } From 62ee2b1a93566cdd7c4e817bc1c553c9f2e4b70c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 3 Feb 2024 04:16:08 +0200 Subject: [PATCH 05/31] feat(composition): Implement more CanvasGeometry methods + more tests --- .../CompositionPathTests.xaml | 1 + .../CompositionPathTests.xaml.cs | 623 ++++++++++++++++++ .../Canvas/Geometry/CanvasGeometry.cs | 64 ++ 3 files changed, 688 insertions(+) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml index 2c1ac8f4d132..6a0e3ff7275e 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml @@ -11,5 +11,6 @@ + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index 5681171770a2..ca64d4243a8f 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -37,6 +37,7 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) var compositor = Microsoft.UI.Xaml.Window.Current.Compositor; var device = CanvasDevice.GetSharedDevice(); + // Simple shape using (var builder = new CanvasPathBuilder(device)) { var visual = compositor.CreateShapeVisual(); @@ -58,6 +59,7 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) ElementCompositionPreview.SetElementChildVisual(compPresenter1, visual); } + // Complex shape using (var builder = new CanvasPathBuilder(device)) { var visual = compositor.CreateShapeVisual(); @@ -116,6 +118,627 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) ElementCompositionPreview.SetElementChildVisual(compPresenter2, visual); } + + // Multiple shapes, source: https://www.svgrepo.com + var multiShapeVisual = compositor.CreateShapeVisual(); + + // 1 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(41.84375f, 26f)); + pathBuilder.AddLine(new Vector2(159.0313f, 26f)); + pathBuilder.AddLine(new Vector2(159.0313f, 180.2969f)); + pathBuilder.AddCubicBezier(new Vector2(159.0313f, 183.5313f), new Vector2(156.4102f, 186.1563f), new Vector2(153.1719f, 186.1563f)); + pathBuilder.AddLine(new Vector2(47.70313f, 186.1563f)); + pathBuilder.AddCubicBezier(new Vector2(44.46875f, 186.1563f), new Vector2(41.84375f, 183.5313f), new Vector2(41.84375f, 180.2969f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(41.84375f, 26f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 2 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(153.1719f, 187.6211f)); + pathBuilder.AddLine(new Vector2(47.70313f, 187.6211f)); + pathBuilder.AddCubicBezier(new Vector2(43.66016f, 187.6172f), new Vector2(40.38672f, 184.3398f), new Vector2(40.38281f, 180.2969f)); + pathBuilder.AddLine(new Vector2(40.38281f, 26f)); + pathBuilder.AddCubicBezier(new Vector2(40.38281f, 25.19141f), new Vector2(41.03516f, 24.53516f), new Vector2(41.84375f, 24.53516f)); + pathBuilder.AddLine(new Vector2(159.0313f, 24.53516f)); + pathBuilder.AddCubicBezier(new Vector2(159.8438f, 24.53516f), new Vector2(160.5f, 25.19141f), new Vector2(160.5f, 26f)); + pathBuilder.AddLine(new Vector2(160.5f, 180.2969f)); + pathBuilder.AddCubicBezier(new Vector2(160.4922f, 184.3398f), new Vector2(157.2188f, 187.6172f), new Vector2(153.1758f, 187.6211f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(43.3125f, 27.46484f)); + pathBuilder.AddLine(new Vector2(43.3125f, 180.2969f)); + pathBuilder.AddCubicBezier(new Vector2(43.3125f, 182.7227f), new Vector2(45.28125f, 184.6875f), new Vector2(47.70313f, 184.6914f)); + pathBuilder.AddLine(new Vector2(153.1719f, 184.6914f)); + pathBuilder.AddCubicBezier(new Vector2(155.5977f, 184.6875f), new Vector2(157.5664f, 182.7227f), new Vector2(157.5703f, 180.2969f)); + pathBuilder.AddLine(new Vector2(157.5703f, 27.46484f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(43.3125f, 27.46484f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 3 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(47.70313f, 14.16016f)); + pathBuilder.AddLine(new Vector2(153.1719f, 14.16016f)); + pathBuilder.AddCubicBezier(new Vector2(156.4102f, 14.16016f), new Vector2(159.0313f, 16.78516f), new Vector2(159.0313f, 20.01953f)); + pathBuilder.AddLine(new Vector2(159.0313f, 53.22266f)); + pathBuilder.AddLine(new Vector2(41.84375f, 53.22266f)); + pathBuilder.AddLine(new Vector2(41.84375f, 20.01953f)); + pathBuilder.AddCubicBezier(new Vector2(41.84375f, 16.78516f), new Vector2(44.46875f, 14.16016f), new Vector2(47.70313f, 14.16016f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(47.70313f, 14.16016f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 255, 255, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 4 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(159.0313f, 54.6875f)); + pathBuilder.AddLine(new Vector2(41.84375f, 54.6875f)); + pathBuilder.AddCubicBezier(new Vector2(41.03516f, 54.6875f), new Vector2(40.38281f, 54.03125f), new Vector2(40.38281f, 53.22266f)); + pathBuilder.AddLine(new Vector2(40.38281f, 20.01953f)); + pathBuilder.AddCubicBezier(new Vector2(40.38672f, 15.97656f), new Vector2(43.66016f, 12.69922f), new Vector2(47.70313f, 12.69531f)); + pathBuilder.AddLine(new Vector2(153.1719f, 12.69531f)); + pathBuilder.AddCubicBezier(new Vector2(157.2188f, 12.69922f), new Vector2(160.4922f, 15.97656f), new Vector2(160.5f, 20.01953f)); + pathBuilder.AddLine(new Vector2(160.5f, 53.22266f)); + pathBuilder.AddCubicBezier(new Vector2(160.5f, 54.03125f), new Vector2(159.8438f, 54.6875f), new Vector2(159.0313f, 54.6875f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(43.3125f, 51.75781f)); + pathBuilder.AddLine(new Vector2(157.5703f, 51.75781f)); + pathBuilder.AddLine(new Vector2(157.5703f, 20.01953f)); + pathBuilder.AddCubicBezier(new Vector2(157.5664f, 17.59375f), new Vector2(155.5977f, 15.62891f), new Vector2(153.1758f, 15.625f)); + pathBuilder.AddLine(new Vector2(47.70313f, 15.625f)); + pathBuilder.AddCubicBezier(new Vector2(45.28125f, 15.62891f), new Vector2(43.3125f, 17.59375f), new Vector2(43.3125f, 20.01953f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(43.3125f, 51.75781f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 5 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(61.375f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(61.375f, 132.7422f), new Vector2(78.86719f, 150.2344f), new Vector2(100.4375f, 150.2344f)); + pathBuilder.AddCubicBezier(new Vector2(122.0117f, 150.2344f), new Vector2(139.5f, 132.7422f), new Vector2(139.5f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(139.5f, 89.59766f), new Vector2(122.0117f, 72.10938f), new Vector2(100.4375f, 72.10938f)); + pathBuilder.AddCubicBezier(new Vector2(78.86719f, 72.10938f), new Vector2(61.375f, 89.59766f), new Vector2(61.375f, 111.1719f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(61.375f, 111.1719f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 255, 255, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 6 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(100.4375f, 151.6953f)); + pathBuilder.AddCubicBezier(new Vector2(78.09375f, 151.6953f), new Vector2(59.91406f, 133.5156f), new Vector2(59.91406f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(59.91406f, 88.82422f), new Vector2(78.09375f, 70.64453f), new Vector2(100.4375f, 70.64453f)); + pathBuilder.AddCubicBezier(new Vector2(122.7891f, 70.64453f), new Vector2(140.9688f, 88.82422f), new Vector2(140.9688f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(140.9688f, 133.5195f), new Vector2(122.7852f, 151.6953f), new Vector2(100.4375f, 151.6953f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(100.4375f, 73.57422f)); + pathBuilder.AddCubicBezier(new Vector2(79.70703f, 73.57422f), new Vector2(62.84375f, 90.44141f), new Vector2(62.84375f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(62.84375f, 131.8984f), new Vector2(79.70703f, 148.7656f), new Vector2(100.4375f, 148.7656f)); + pathBuilder.AddCubicBezier(new Vector2(121.1719f, 148.7656f), new Vector2(138.0391f, 131.8984f), new Vector2(138.0391f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(138.0391f, 90.44141f), new Vector2(121.1719f, 73.57422f), new Vector2(100.4375f, 73.57422f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(100.4375f, 73.57422f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 7 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(71.14063f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(71.14063f, 127.3516f), new Vector2(84.25781f, 140.4688f), new Vector2(100.4375f, 140.4688f)); + pathBuilder.AddCubicBezier(new Vector2(116.6211f, 140.4688f), new Vector2(129.7344f, 127.3516f), new Vector2(129.7344f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(129.7344f, 94.98828f), new Vector2(116.6211f, 81.875f), new Vector2(100.4375f, 81.875f)); + pathBuilder.AddCubicBezier(new Vector2(84.25781f, 81.875f), new Vector2(71.14063f, 94.98828f), new Vector2(71.14063f, 111.1719f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(71.14063f, 111.1719f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 8 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(100.457f, 140.9531f)); + pathBuilder.AddLine(new Vector2(100.4375f, 140.9531f)); + pathBuilder.AddCubicBezier(new Vector2(98.49609f, 140.957f), new Vector2(96.55469f, 140.7656f), new Vector2(94.64844f, 140.3906f)); + pathBuilder.AddCubicBezier(new Vector2(94.38281f, 140.3398f), new Vector2(94.21094f, 140.082f), new Vector2(94.26563f, 139.8164f)); + pathBuilder.AddCubicBezier(new Vector2(94.32031f, 139.5508f), new Vector2(94.57813f, 139.3789f), new Vector2(94.84375f, 139.4336f)); + pathBuilder.AddCubicBezier(new Vector2(96.69141f, 139.7969f), new Vector2(98.56641f, 139.9805f), new Vector2(100.4453f, 139.9766f)); + pathBuilder.AddCubicBezier(new Vector2(100.7188f, 139.9766f), new Vector2(100.9375f, 140.1953f), new Vector2(100.9414f, 140.4688f)); + pathBuilder.AddCubicBezier(new Vector2(100.9453f, 140.5977f), new Vector2(100.8945f, 140.7227f), new Vector2(100.8047f, 140.8125f)); + pathBuilder.AddCubicBezier(new Vector2(100.7109f, 140.9063f), new Vector2(100.5859f, 140.957f), new Vector2(100.457f, 140.9531f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(106.168f, 140.3945f)); + pathBuilder.AddCubicBezier(new Vector2(105.8984f, 140.4219f), new Vector2(105.6602f, 140.2227f), new Vector2(105.6367f, 139.9531f)); + pathBuilder.AddCubicBezier(new Vector2(105.6094f, 139.6836f), new Vector2(105.8047f, 139.4453f), new Vector2(106.0781f, 139.418f)); + pathBuilder.AddCubicBezier(new Vector2(107.9258f, 139.0508f), new Vector2(109.7344f, 138.5f), new Vector2(111.4766f, 137.7813f)); + pathBuilder.AddCubicBezier(new Vector2(111.7266f, 137.6758f), new Vector2(112.0117f, 137.793f), new Vector2(112.1172f, 138.043f)); + pathBuilder.AddCubicBezier(new Vector2(112.2227f, 138.293f), new Vector2(112.1016f, 138.5781f), new Vector2(111.8555f, 138.6797f)); + pathBuilder.AddCubicBezier(new Vector2(110.0508f, 139.4297f), new Vector2(108.1797f, 139.9961f), new Vector2(106.2656f, 140.375f)); + pathBuilder.AddCubicBezier(new Vector2(106.2344f, 140.3867f), new Vector2(106.2031f, 140.3906f), new Vector2(106.168f, 140.3945f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(89.24609f, 138.7422f)); + pathBuilder.AddCubicBezier(new Vector2(89.17969f, 138.7422f), new Vector2(89.11328f, 138.7305f), new Vector2(89.05078f, 138.7031f)); + pathBuilder.AddCubicBezier(new Vector2(87.25f, 137.957f), new Vector2(85.52344f, 137.0352f), new Vector2(83.90234f, 135.9531f)); + pathBuilder.AddCubicBezier(new Vector2(83.67969f, 135.8008f), new Vector2(83.61719f, 135.4961f), new Vector2(83.76563f, 135.2734f)); + pathBuilder.AddCubicBezier(new Vector2(83.91797f, 135.0508f), new Vector2(84.22266f, 134.9883f), new Vector2(84.44531f, 135.1367f)); + pathBuilder.AddCubicBezier(new Vector2(86.01563f, 136.1875f), new Vector2(87.68359f, 137.0781f), new Vector2(89.42578f, 137.8008f)); + pathBuilder.AddCubicBezier(new Vector2(89.64453f, 137.8906f), new Vector2(89.76563f, 138.1211f), new Vector2(89.71875f, 138.3516f)); + pathBuilder.AddCubicBezier(new Vector2(89.66797f, 138.582f), new Vector2(89.46484f, 138.7461f), new Vector2(89.23047f, 138.7383f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(116.7266f, 136.0078f)); + pathBuilder.AddCubicBezier(new Vector2(116.5117f, 136.0078f), new Vector2(116.3203f, 135.8672f), new Vector2(116.2578f, 135.6602f)); + pathBuilder.AddCubicBezier(new Vector2(116.1953f, 135.4531f), new Vector2(116.2773f, 135.2305f), new Vector2(116.4531f, 135.1094f)); + pathBuilder.AddCubicBezier(new Vector2(118.0234f, 134.0625f), new Vector2(119.4844f, 132.8594f), new Vector2(120.8164f, 131.5234f)); + pathBuilder.AddCubicBezier(new Vector2(121.0078f, 131.3359f), new Vector2(121.3164f, 131.3359f), new Vector2(121.5078f, 131.5234f)); + pathBuilder.AddCubicBezier(new Vector2(121.6992f, 131.7148f), new Vector2(121.6992f, 132.0273f), new Vector2(121.5078f, 132.2188f)); + pathBuilder.AddCubicBezier(new Vector2(120.1289f, 133.6016f), new Vector2(118.6133f, 134.8477f), new Vector2(116.9922f, 135.9375f)); + pathBuilder.AddCubicBezier(new Vector2(116.9141f, 135.9883f), new Vector2(116.8203f, 136.0156f), new Vector2(116.7266f, 136.0156f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(79.74219f, 132.3906f)); + pathBuilder.AddCubicBezier(new Vector2(79.61328f, 132.3906f), new Vector2(79.48828f, 132.3398f), new Vector2(79.39453f, 132.25f)); + pathBuilder.AddCubicBezier(new Vector2(78.01563f, 130.8711f), new Vector2(76.77344f, 129.3594f), new Vector2(75.68359f, 127.7422f)); + pathBuilder.AddCubicBezier(new Vector2(75.53516f, 127.5156f), new Vector2(75.59375f, 127.2148f), new Vector2(75.82031f, 127.0625f)); + pathBuilder.AddCubicBezier(new Vector2(76.04297f, 126.9102f), new Vector2(76.34375f, 126.9727f), new Vector2(76.49609f, 127.1953f)); + pathBuilder.AddCubicBezier(new Vector2(77.54688f, 128.7617f), new Vector2(78.74609f, 130.2227f), new Vector2(80.07813f, 131.5547f)); + pathBuilder.AddCubicBezier(new Vector2(80.21875f, 131.6953f), new Vector2(80.26172f, 131.9063f), new Vector2(80.18359f, 132.0898f)); + pathBuilder.AddCubicBezier(new Vector2(80.10938f, 132.2734f), new Vector2(79.92969f, 132.3906f), new Vector2(79.73438f, 132.3906f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(124.8047f, 127.9297f)); + pathBuilder.AddCubicBezier(new Vector2(124.625f, 127.9297f), new Vector2(124.457f, 127.832f), new Vector2(124.375f, 127.6719f)); + pathBuilder.AddCubicBezier(new Vector2(124.2891f, 127.5117f), new Vector2(124.2969f, 127.3203f), new Vector2(124.3984f, 127.1719f)); + pathBuilder.AddCubicBezier(new Vector2(125.4492f, 125.6016f), new Vector2(126.3398f, 123.9336f), new Vector2(127.0625f, 122.1914f)); + pathBuilder.AddCubicBezier(new Vector2(127.1289f, 122.0273f), new Vector2(127.2734f, 121.9102f), new Vector2(127.4492f, 121.8867f)); + pathBuilder.AddCubicBezier(new Vector2(127.625f, 121.8633f), new Vector2(127.8008f, 121.9336f), new Vector2(127.9063f, 122.0742f)); + pathBuilder.AddCubicBezier(new Vector2(128.0117f, 122.2148f), new Vector2(128.0352f, 122.4023f), new Vector2(127.9648f, 122.5664f)); + pathBuilder.AddCubicBezier(new Vector2(127.2188f, 124.3672f), new Vector2(126.293f, 126.0898f), new Vector2(125.2109f, 127.7109f)); + pathBuilder.AddCubicBezier(new Vector2(125.1172f, 127.8477f), new Vector2(124.9688f, 127.9297f), new Vector2(124.8047f, 127.9297f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(73.375f, 122.8984f)); + pathBuilder.AddCubicBezier(new Vector2(73.17969f, 122.8984f), new Vector2(73f, 122.7813f), new Vector2(72.92578f, 122.5977f)); + pathBuilder.AddCubicBezier(new Vector2(72.17578f, 120.7969f), new Vector2(71.60938f, 118.9258f), new Vector2(71.22656f, 117.0117f)); + pathBuilder.AddCubicBezier(new Vector2(71.17188f, 116.7461f), new Vector2(71.34375f, 116.4883f), new Vector2(71.60547f, 116.4336f)); + pathBuilder.AddCubicBezier(new Vector2(71.87109f, 116.3828f), new Vector2(72.12891f, 116.5508f), new Vector2(72.18359f, 116.8164f)); + pathBuilder.AddCubicBezier(new Vector2(72.55078f, 118.668f), new Vector2(73.10156f, 120.4805f), new Vector2(73.82813f, 122.2227f)); + pathBuilder.AddCubicBezier(new Vector2(73.89063f, 122.375f), new Vector2(73.875f, 122.5469f), new Vector2(73.78516f, 122.6797f)); + pathBuilder.AddCubicBezier(new Vector2(73.69141f, 122.8164f), new Vector2(73.53906f, 122.8984f), new Vector2(73.375f, 122.8984f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(129.1797f, 117.3711f)); + pathBuilder.AddCubicBezier(new Vector2(129.1484f, 117.375f), new Vector2(129.1172f, 117.375f), new Vector2(129.0859f, 117.3711f)); + pathBuilder.AddCubicBezier(new Vector2(128.957f, 117.3477f), new Vector2(128.8438f, 117.2734f), new Vector2(128.7695f, 117.1641f)); + pathBuilder.AddCubicBezier(new Vector2(128.6953f, 117.0586f), new Vector2(128.668f, 116.9258f), new Vector2(128.6953f, 116.7969f)); + pathBuilder.AddCubicBezier(new Vector2(129.0586f, 114.9453f), new Vector2(129.2422f, 113.0625f), new Vector2(129.2383f, 111.1797f)); + pathBuilder.AddLine(new Vector2(129.2383f, 111.1094f)); + pathBuilder.AddCubicBezier(new Vector2(129.2383f, 110.8398f), new Vector2(129.457f, 110.6211f), new Vector2(129.7266f, 110.6211f)); + pathBuilder.AddCubicBezier(new Vector2(129.9961f, 110.6211f), new Vector2(130.2148f, 110.8398f), new Vector2(130.2148f, 111.1094f)); + pathBuilder.AddLine(new Vector2(130.2148f, 111.1797f)); + pathBuilder.AddCubicBezier(new Vector2(130.2148f, 113.1289f), new Vector2(130.0273f, 115.0742f), new Vector2(129.6484f, 116.9883f)); + pathBuilder.AddCubicBezier(new Vector2(129.6016f, 117.207f), new Vector2(129.4063f, 117.3672f), new Vector2(129.1797f, 117.3711f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(71.14063f, 111.6914f)); + pathBuilder.AddCubicBezier(new Vector2(70.87109f, 111.6914f), new Vector2(70.65234f, 111.4727f), new Vector2(70.65234f, 111.2031f)); + pathBuilder.AddLine(new Vector2(70.65234f, 111.1719f)); + pathBuilder.AddCubicBezier(new Vector2(70.65234f, 109.2305f), new Vector2(70.83984f, 107.2969f), new Vector2(71.21484f, 105.3945f)); + pathBuilder.AddCubicBezier(new Vector2(71.26563f, 105.1289f), new Vector2(71.52344f, 104.957f), new Vector2(71.78906f, 105.0117f)); + pathBuilder.AddCubicBezier(new Vector2(72.05469f, 105.0664f), new Vector2(72.22656f, 105.3242f), new Vector2(72.17188f, 105.5898f)); + pathBuilder.AddCubicBezier(new Vector2(71.8125f, 107.4297f), new Vector2(71.62891f, 109.3008f), new Vector2(71.63281f, 111.1797f)); + pathBuilder.AddLine(new Vector2(71.63281f, 111.2109f)); + pathBuilder.AddCubicBezier(new Vector2(71.625f, 111.4766f), new Vector2(71.41016f, 111.6914f), new Vector2(71.14063f, 111.6914f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(129.1641f, 105.875f)); + pathBuilder.AddCubicBezier(new Vector2(128.9336f, 105.8789f), new Vector2(128.7344f, 105.7148f), new Vector2(128.6875f, 105.4844f)); + pathBuilder.AddCubicBezier(new Vector2(128.3164f, 103.6367f), new Vector2(127.7656f, 101.8281f), new Vector2(127.0391f, 100.0898f)); + pathBuilder.AddCubicBezier(new Vector2(126.9648f, 99.92578f), new Vector2(126.9844f, 99.73828f), new Vector2(127.0938f, 99.59375f)); + pathBuilder.AddCubicBezier(new Vector2(127.1992f, 99.45313f), new Vector2(127.375f, 99.37891f), new Vector2(127.5508f, 99.40234f)); + pathBuilder.AddCubicBezier(new Vector2(127.7266f, 99.42578f), new Vector2(127.875f, 99.54297f), new Vector2(127.9375f, 99.71094f)); + pathBuilder.AddCubicBezier(new Vector2(128.6914f, 101.5117f), new Vector2(129.2617f, 103.3828f), new Vector2(129.6445f, 105.293f)); + pathBuilder.AddCubicBezier(new Vector2(129.6758f, 105.4375f), new Vector2(129.6367f, 105.5859f), new Vector2(129.543f, 105.7031f)); + pathBuilder.AddCubicBezier(new Vector2(129.4531f, 105.8164f), new Vector2(129.3125f, 105.8828f), new Vector2(129.1641f, 105.8828f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(73.35156f, 100.4805f)); + pathBuilder.AddCubicBezier(new Vector2(73.1875f, 100.4805f), new Vector2(73.03516f, 100.3984f), new Vector2(72.94531f, 100.2617f)); + pathBuilder.AddCubicBezier(new Vector2(72.85547f, 100.1289f), new Vector2(72.83594f, 99.95703f), new Vector2(72.89844f, 99.80469f)); + pathBuilder.AddCubicBezier(new Vector2(73.64453f, 98f), new Vector2(74.56641f, 96.27734f), new Vector2(75.64844f, 94.65625f)); + pathBuilder.AddCubicBezier(new Vector2(75.80078f, 94.42969f), new Vector2(76.10547f, 94.37109f), new Vector2(76.32813f, 94.51953f)); + pathBuilder.AddCubicBezier(new Vector2(76.55078f, 94.66797f), new Vector2(76.61328f, 94.97266f), new Vector2(76.46094f, 95.19922f)); + pathBuilder.AddCubicBezier(new Vector2(75.41406f, 96.76563f), new Vector2(74.52734f, 98.43359f), new Vector2(73.80469f, 100.1797f)); + pathBuilder.AddCubicBezier(new Vector2(73.73047f, 100.3633f), new Vector2(73.55078f, 100.4805f), new Vector2(73.35156f, 100.4805f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(124.7695f, 95.32813f)); + pathBuilder.AddCubicBezier(new Vector2(124.6055f, 95.33203f), new Vector2(124.4531f, 95.25f), new Vector2(124.3633f, 95.11719f)); + pathBuilder.AddCubicBezier(new Vector2(123.3086f, 93.55078f), new Vector2(122.1055f, 92.08984f), new Vector2(120.7695f, 90.75781f)); + pathBuilder.AddCubicBezier(new Vector2(120.6406f, 90.63672f), new Vector2(120.5898f, 90.45703f), new Vector2(120.6367f, 90.28516f)); + pathBuilder.AddCubicBezier(new Vector2(120.6797f, 90.11328f), new Vector2(120.8125f, 89.98047f), new Vector2(120.9844f, 89.9375f)); + pathBuilder.AddCubicBezier(new Vector2(121.1563f, 89.89063f), new Vector2(121.3359f, 89.94141f), new Vector2(121.457f, 90.07031f)); + pathBuilder.AddCubicBezier(new Vector2(122.8398f, 91.44531f), new Vector2(124.082f, 92.95313f), new Vector2(125.168f, 94.57031f)); + pathBuilder.AddCubicBezier(new Vector2(125.2695f, 94.71875f), new Vector2(125.2813f, 94.91406f), new Vector2(125.1953f, 95.07422f)); + pathBuilder.AddCubicBezier(new Vector2(125.1133f, 95.23047f), new Vector2(124.9453f, 95.33203f), new Vector2(124.7656f, 95.33203f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(79.6875f, 90.96875f)); + pathBuilder.AddCubicBezier(new Vector2(79.48828f, 90.97266f), new Vector2(79.3125f, 90.85156f), new Vector2(79.23438f, 90.66797f)); + pathBuilder.AddCubicBezier(new Vector2(79.16016f, 90.48828f), new Vector2(79.20313f, 90.27734f), new Vector2(79.34375f, 90.13672f)); + pathBuilder.AddCubicBezier(new Vector2(80.71875f, 88.75781f), new Vector2(82.23047f, 87.51172f), new Vector2(83.84766f, 86.42578f)); + pathBuilder.AddCubicBezier(new Vector2(84.07031f, 86.27344f), new Vector2(84.375f, 86.33594f), new Vector2(84.52344f, 86.55859f)); + pathBuilder.AddCubicBezier(new Vector2(84.67578f, 86.78125f), new Vector2(84.61719f, 87.08594f), new Vector2(84.39063f, 87.23438f)); + pathBuilder.AddCubicBezier(new Vector2(82.82813f, 88.28516f), new Vector2(81.37109f, 89.48828f), new Vector2(80.03906f, 90.82031f)); + pathBuilder.AddCubicBezier(new Vector2(79.94922f, 90.91797f), new Vector2(79.82031f, 90.96875f), new Vector2(79.6875f, 90.96875f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(116.6602f, 87.25781f)); + pathBuilder.AddCubicBezier(new Vector2(116.5664f, 87.25781f), new Vector2(116.4727f, 87.23047f), new Vector2(116.3906f, 87.17969f)); + pathBuilder.AddCubicBezier(new Vector2(114.8242f, 86.13281f), new Vector2(113.1523f, 85.24219f), new Vector2(111.4102f, 84.52344f)); + pathBuilder.AddCubicBezier(new Vector2(111.1641f, 84.41406f), new Vector2(111.0508f, 84.13281f), new Vector2(111.1523f, 83.88672f)); + pathBuilder.AddCubicBezier(new Vector2(111.2539f, 83.64063f), new Vector2(111.5352f, 83.52344f), new Vector2(111.7813f, 83.61719f)); + pathBuilder.AddCubicBezier(new Vector2(113.5859f, 84.36328f), new Vector2(115.3125f, 85.28516f), new Vector2(116.9375f, 86.36719f)); + pathBuilder.AddCubicBezier(new Vector2(117.1133f, 86.48438f), new Vector2(117.1953f, 86.70703f), new Vector2(117.1328f, 86.91406f)); + pathBuilder.AddCubicBezier(new Vector2(117.0703f, 87.12109f), new Vector2(116.8789f, 87.26172f), new Vector2(116.6641f, 87.25781f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(89.18359f, 84.60156f)); + pathBuilder.AddCubicBezier(new Vector2(88.94922f, 84.60547f), new Vector2(88.74609f, 84.44141f), new Vector2(88.69531f, 84.21094f)); + pathBuilder.AddCubicBezier(new Vector2(88.64844f, 83.98438f), new Vector2(88.76953f, 83.75f), new Vector2(88.98828f, 83.66406f)); + pathBuilder.AddCubicBezier(new Vector2(90.78906f, 82.91406f), new Vector2(92.66016f, 82.34375f), new Vector2(94.57031f, 81.96094f)); + pathBuilder.AddCubicBezier(new Vector2(94.83594f, 81.90625f), new Vector2(95.09375f, 82.07813f), new Vector2(95.14844f, 82.33984f)); + pathBuilder.AddCubicBezier(new Vector2(95.20313f, 82.60547f), new Vector2(95.03125f, 82.86328f), new Vector2(94.76953f, 82.91797f)); + pathBuilder.AddCubicBezier(new Vector2(92.91797f, 83.28906f), new Vector2(91.10938f, 83.84375f), new Vector2(89.37109f, 84.57031f)); + pathBuilder.AddCubicBezier(new Vector2(89.3125f, 84.59375f), new Vector2(89.24609f, 84.60547f), new Vector2(89.18359f, 84.60156f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(106.1055f, 82.90625f)); + pathBuilder.AddCubicBezier(new Vector2(106.0742f, 82.91016f), new Vector2(106.043f, 82.91016f), new Vector2(106.0117f, 82.90625f)); + pathBuilder.AddCubicBezier(new Vector2(104.1758f, 82.55078f), new Vector2(102.3125f, 82.37109f), new Vector2(100.4414f, 82.37109f)); + pathBuilder.AddLine(new Vector2(100.3906f, 82.37109f)); + pathBuilder.AddCubicBezier(new Vector2(100.1211f, 82.37109f), new Vector2(99.90234f, 82.15234f), new Vector2(99.90234f, 81.88281f)); + pathBuilder.AddCubicBezier(new Vector2(99.90234f, 81.61328f), new Vector2(100.1211f, 81.39453f), new Vector2(100.3906f, 81.39453f)); + pathBuilder.AddLine(new Vector2(100.4375f, 81.39453f)); + pathBuilder.AddCubicBezier(new Vector2(102.3711f, 81.39453f), new Vector2(104.3008f, 81.58203f), new Vector2(106.1992f, 81.94922f)); + pathBuilder.AddCubicBezier(new Vector2(106.4688f, 81.97656f), new Vector2(106.668f, 82.21484f), new Vector2(106.6406f, 82.48828f)); + pathBuilder.AddCubicBezier(new Vector2(106.6133f, 82.75781f), new Vector2(106.375f, 82.95313f), new Vector2(106.1055f, 82.92578f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(106.1055f, 82.90625f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 9 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(55.51563f, 28.80859f)); + pathBuilder.AddLine(new Vector2(65.28125f, 28.80859f)); + pathBuilder.AddLine(new Vector2(65.28125f, 38.57422f)); + pathBuilder.AddLine(new Vector2(55.51563f, 38.57422f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(55.51563f, 28.80859f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 10 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(65.28125f, 40.03906f)); + pathBuilder.AddLine(new Vector2(55.51563f, 40.03906f)); + pathBuilder.AddCubicBezier(new Vector2(54.70703f, 40.03906f), new Vector2(54.05469f, 39.38281f), new Vector2(54.05469f, 38.57422f)); + pathBuilder.AddLine(new Vector2(54.05469f, 28.80859f)); + pathBuilder.AddCubicBezier(new Vector2(54.05469f, 28f), new Vector2(54.70703f, 27.34375f), new Vector2(55.51563f, 27.34375f)); + pathBuilder.AddLine(new Vector2(65.28125f, 27.34375f)); + pathBuilder.AddCubicBezier(new Vector2(66.09375f, 27.34375f), new Vector2(66.75f, 28f), new Vector2(66.75f, 28.80859f)); + pathBuilder.AddLine(new Vector2(66.75f, 38.57422f)); + pathBuilder.AddCubicBezier(new Vector2(66.75f, 39.38281f), new Vector2(66.09375f, 40.03906f), new Vector2(65.28125f, 40.03906f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(56.98438f, 37.10938f)); + pathBuilder.AddLine(new Vector2(63.82031f, 37.10938f)); + pathBuilder.AddLine(new Vector2(63.82031f, 30.27344f)); + pathBuilder.AddLine(new Vector2(56.98438f, 30.27344f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(56.98438f, 37.10938f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 11 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(71.72656f, 28.80859f)); + pathBuilder.AddLine(new Vector2(81.49219f, 28.80859f)); + pathBuilder.AddLine(new Vector2(81.49219f, 38.57422f)); + pathBuilder.AddLine(new Vector2(71.72656f, 38.57422f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(71.72656f, 28.80859f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 12 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(81.49219f, 40.03906f)); + pathBuilder.AddLine(new Vector2(71.72656f, 40.03906f)); + pathBuilder.AddCubicBezier(new Vector2(70.91797f, 40.03906f), new Vector2(70.26172f, 39.38281f), new Vector2(70.26172f, 38.57422f)); + pathBuilder.AddLine(new Vector2(70.26172f, 28.80859f)); + pathBuilder.AddCubicBezier(new Vector2(70.26172f, 28f), new Vector2(70.91797f, 27.34375f), new Vector2(71.72656f, 27.34375f)); + pathBuilder.AddLine(new Vector2(81.49219f, 27.34375f)); + pathBuilder.AddCubicBezier(new Vector2(82.30078f, 27.34375f), new Vector2(82.95703f, 28f), new Vector2(82.95703f, 28.80859f)); + pathBuilder.AddLine(new Vector2(82.95703f, 38.57422f)); + pathBuilder.AddCubicBezier(new Vector2(82.95703f, 39.38281f), new Vector2(82.30078f, 40.03906f), new Vector2(81.49219f, 40.03906f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(73.19141f, 37.10938f)); + pathBuilder.AddLine(new Vector2(80.02734f, 37.10938f)); + pathBuilder.AddLine(new Vector2(80.02734f, 30.27344f)); + pathBuilder.AddLine(new Vector2(73.19141f, 30.27344f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(73.19141f, 37.10938f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 13 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(87.93359f, 28.80859f)); + pathBuilder.AddLine(new Vector2(97.69922f, 28.80859f)); + pathBuilder.AddLine(new Vector2(97.69922f, 38.57422f)); + pathBuilder.AddLine(new Vector2(87.93359f, 38.57422f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(87.93359f, 28.80859f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 14 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(97.69922f, 40.03906f)); + pathBuilder.AddLine(new Vector2(87.93359f, 40.03906f)); + pathBuilder.AddCubicBezier(new Vector2(87.125f, 40.03906f), new Vector2(86.46875f, 39.38281f), new Vector2(86.46875f, 38.57422f)); + pathBuilder.AddLine(new Vector2(86.46875f, 28.80859f)); + pathBuilder.AddCubicBezier(new Vector2(86.46875f, 28f), new Vector2(87.125f, 27.34375f), new Vector2(87.93359f, 27.34375f)); + pathBuilder.AddLine(new Vector2(97.69922f, 27.34375f)); + pathBuilder.AddCubicBezier(new Vector2(98.50781f, 27.34375f), new Vector2(99.16406f, 28f), new Vector2(99.16406f, 28.80859f)); + pathBuilder.AddLine(new Vector2(99.16406f, 38.57422f)); + pathBuilder.AddCubicBezier(new Vector2(99.16406f, 39.38281f), new Vector2(98.50781f, 40.03906f), new Vector2(97.69922f, 40.03906f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(89.39844f, 37.10938f)); + pathBuilder.AddLine(new Vector2(96.23438f, 37.10938f)); + pathBuilder.AddLine(new Vector2(96.23438f, 30.27344f)); + pathBuilder.AddLine(new Vector2(89.39844f, 30.27344f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(89.39844f, 37.10938f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 15 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(132.5078f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(132.5078f, 36.92578f), new Vector2(135.1328f, 39.55078f), new Vector2(138.3672f, 39.55078f)); + pathBuilder.AddCubicBezier(new Vector2(141.6016f, 39.55078f), new Vector2(144.2266f, 36.92578f), new Vector2(144.2266f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(144.2266f, 30.45703f), new Vector2(141.6016f, 27.83203f), new Vector2(138.3672f, 27.83203f)); + pathBuilder.AddCubicBezier(new Vector2(135.1328f, 27.83203f), new Vector2(132.5078f, 30.45703f), new Vector2(132.5078f, 33.69141f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(132.5078f, 33.69141f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 223, 237, 255)); + + multiShapeVisual.Shapes.Add(shape); + } + + // 16 + using (var pathBuilder = new CanvasPathBuilder(device)) + { + pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + pathBuilder.BeginFigure(new Vector2(138.3672f, 41.01563f)); + pathBuilder.AddCubicBezier(new Vector2(134.3203f, 41.01563f), new Vector2(131.043f, 37.73828f), new Vector2(131.043f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(131.043f, 29.64453f), new Vector2(134.3203f, 26.36719f), new Vector2(138.3672f, 26.36719f)); + pathBuilder.AddCubicBezier(new Vector2(142.4141f, 26.36719f), new Vector2(145.6914f, 29.64453f), new Vector2(145.6914f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(145.6875f, 37.73438f), new Vector2(142.4102f, 41.01172f), new Vector2(138.3672f, 41.01563f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(138.3672f, 29.29688f)); + pathBuilder.AddCubicBezier(new Vector2(135.9414f, 29.29688f), new Vector2(133.9727f, 31.26563f), new Vector2(133.9727f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(133.9727f, 36.11719f), new Vector2(135.9414f, 38.08594f), new Vector2(138.3672f, 38.08594f)); + pathBuilder.AddCubicBezier(new Vector2(140.793f, 38.08594f), new Vector2(142.7617f, 36.11719f), new Vector2(142.7617f, 33.69141f)); + pathBuilder.AddCubicBezier(new Vector2(142.7578f, 31.26563f), new Vector2(140.793f, 29.30078f), new Vector2(138.3672f, 29.29688f)); + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + + pathBuilder.BeginFigure(new Vector2(138.3672f, 29.29688f)); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + + var path = new CompositionPath(CanvasGeometry.CreatePath(pathBuilder)); + var geometry = compositor.CreatePathGeometry(path); + var shape = compositor.CreateSpriteShape(geometry); + + shape.FillBrush = compositor.CreateColorBrush(new(255, 102, 169, 247)); + + multiShapeVisual.Shapes.Add(shape); + } + + multiShapeVisual.Size = new(200); + ElementCompositionPreview.SetElementChildVisual(compPresenter3, multiShapeVisual); } } } diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs index 365b5c2ec69e..6d3b9e55dd3f 100644 --- a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using Uno.UI.Composition; +using Windows.Foundation; using Windows.Graphics; using Windows.Graphics.Interop; using Windows.Graphics.Interop.Direct2D; @@ -23,8 +25,43 @@ internal class CanvasGeometry : IDisposable, IGeometrySource2D, IGeometrySource2 public void Dispose() { } + public static CanvasGeometry CreateEllipse(ICanvasResourceCreator resourceCreator, Vector2 centerPoint, float radiusX, float radiusY) => new(new CanvasEllipseGeometry(new() { Point = centerPoint.ToPoint(), RadiusX = radiusX, RadiusY = radiusY })); + + public static CanvasGeometry CreateEllipse(ICanvasResourceCreator resourceCreator, float x, float y, float radiusX, float radiusY) => new(new CanvasEllipseGeometry(new() { Point = new(x, y), RadiusX = radiusX, RadiusY = radiusY })); + + public static CanvasGeometry CreateCircle(ICanvasResourceCreator resourceCreator, Vector2 centerPoint, float radius) => CreateEllipse(resourceCreator, centerPoint, radius, radius); + + public static CanvasGeometry CreateCircle(ICanvasResourceCreator resourceCreator, float x, float y, float radius) => CreateEllipse(resourceCreator, x, y, radius, radius); + public static CanvasGeometry CreatePath(CanvasPathBuilder pathBuilder) => new(new CanvasPathGeometry(pathBuilder.Commands)); + public static CanvasGeometry CreatePolygon(ICanvasResourceCreator resourceCreator, Vector2[] points) + { + CanvasPathBuilder pathBuilder = new(resourceCreator); + + if (points.Length > 0) + { + pathBuilder.BeginFigure(points[0], CanvasFigureFill.Default); + + foreach (var point in points) + { + pathBuilder.AddLine(point); + } + + pathBuilder.EndFigure(CanvasFigureLoop.Closed); + } + + return CreatePath(pathBuilder); + } + + public static CanvasGeometry CreateRectangle(ICanvasResourceCreator resourceCreator, Rect rect) => new(new CanvasRectangleGeometry(rect)); + + public static CanvasGeometry CreateRectangle(ICanvasResourceCreator resourceCreator, float x, float y, float w, float h) => new(new CanvasRectangleGeometry(new(x, y, w, h))); + + public static CanvasGeometry CreateRoundedRectangle(ICanvasResourceCreator resourceCreator, Rect rect, float radiusX, float radiusY) => new(new CanvasRoundedRectangleGeometry(new() { Rect = rect, RadiusX = radiusX, RadiusY = radiusY })); + + public static CanvasGeometry CreateRoundedRectangle(ICanvasResourceCreator resourceCreator, float x, float y, float w, float h, float radiusX, float radiusY) => new(new CanvasRoundedRectangleGeometry(new() { Rect = new(x, y, w, h), RadiusX = radiusX, RadiusY = radiusY })); + private class CanvasPathGeometry : ID2D1PathGeometry, ICompositionPathCommandsProvider { private List _commands; @@ -47,4 +84,31 @@ CompositionPathCommandType.BeginFigure or public void Stream(ID2D1GeometrySink geometrySink) => throw new NotImplementedException(); // TODO } + + private class CanvasRectangleGeometry : ID2D1RectangleGeometry + { + private Rect _rect; + + public CanvasRectangleGeometry(Rect rect) => _rect = rect; + + public Rect GetRect() => _rect; + } + + private class CanvasRoundedRectangleGeometry : ID2D1RoundedRectangleGeometry + { + private D2D1RoundedRect _rect; + + public CanvasRoundedRectangleGeometry(D2D1RoundedRect rect) => _rect = rect; + + public D2D1RoundedRect GetRoundedRect() => _rect; + } + + private class CanvasEllipseGeometry : ID2D1EllipseGeometry + { + private D2D1Ellipse _ellipse; + + public CanvasEllipseGeometry(D2D1Ellipse ellipse) => _ellipse = ellipse; + + public D2D1Ellipse GetEllipse() => _ellipse; + } } From c029f28077929eb424c1964ff5889e575dbd05bd Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Mon, 5 Feb 2024 04:12:05 +0200 Subject: [PATCH 06/31] feat(composition): More work on CanvasGeometry, CanvasPathBuilder, and UI sample tests --- .../CompositionPathTests.xaml | 7 ++- .../CompositionPathTests.xaml.cs | 63 +++++++++++++++++++ .../Canvas/Geometry/CanvasGeometry.cs | 4 +- .../Canvas/Geometry/CanvasPathBuilder.cs | 33 ++++++---- 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml index 6a0e3ff7275e..bf6013d8215a 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml @@ -8,9 +8,12 @@ d:DesignHeight="300" d:DesignWidth="400"> - + - + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index ca64d4243a8f..c7b56aff985b 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -739,6 +739,69 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) multiShapeVisual.Size = new(200); ElementCompositionPreview.SetElementChildVisual(compPresenter3, multiShapeVisual); + + // Other CanvasGeometry methods + + // Circle + var circleVisual = compositor.CreateShapeVisual(); + + var circlePath = new CompositionPath(CanvasGeometry.CreateCircle(device, new(100, 100), 100)); + var circleGeometry = compositor.CreatePathGeometry(circlePath); + var circleShape = compositor.CreateSpriteShape(circleGeometry); + + circleShape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightPink); + + circleVisual.Shapes.Add(circleShape); + circleVisual.Size = new(200); + + ElementCompositionPreview.SetElementChildVisual(compPresenter4, circleVisual); + + // Rounded Rectangle + var rectVisual = compositor.CreateShapeVisual(); + + var rectPath = new CompositionPath(CanvasGeometry.CreateRoundedRectangle(device, new(0, 0, 200, 200), 16, 16)); + var rectGeometry = compositor.CreatePathGeometry(rectPath); + var rectShape = compositor.CreateSpriteShape(rectGeometry); + + rectShape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightBlue); + + rectVisual.Shapes.Add(rectShape); + rectVisual.Size = new(200); + + ElementCompositionPreview.SetElementChildVisual(compPresenter5, rectVisual); + + // Polygon + var polyVisual = compositor.CreateShapeVisual(); + + var polyPath = new CompositionPath(CanvasGeometry.CreatePolygon(device, GetStarPoints(new(100), 100, 5, 0.5f))); + var polyGeometry = compositor.CreatePathGeometry(polyPath); + var polyShape = compositor.CreateSpriteShape(polyGeometry); + + polyShape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightYellow); + + polyVisual.Shapes.Add(polyShape); + polyVisual.Size = new(200); + + ElementCompositionPreview.SetElementChildVisual(compPresenter6, polyVisual); + } + + private Vector2[] GetStarPoints(Vector2 center, float radius, int numPoints, float innerRadiusFactor) + { + var angleDelta = 2 * MathF.PI / numPoints; + var points = new Vector2[numPoints * 2]; + + for (int i = 0; i < numPoints * 2; i++) + { + float angle = i * angleDelta; + float currentRadius = (i % 2 == 0) ? radius : radius * innerRadiusFactor; + + float x = center.X + currentRadius * MathF.Cos(angle); + float y = center.Y + currentRadius * MathF.Sin(angle); + + points[i] = new(x, y); + } + + return points; } } } diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs index 6d3b9e55dd3f..a4da1583b2ea 100644 --- a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasGeometry.cs @@ -18,9 +18,9 @@ internal class CanvasGeometry : IDisposable, IGeometrySource2D, IGeometrySource2 private CanvasGeometry(ID2D1Geometry geometry) => _geometry = geometry ?? throw new ArgumentNullException("geometry"); - public ID2D1Geometry GetGeometry() => _geometry; + ID2D1Geometry IGeometrySource2DInterop.GetGeometry() => _geometry; - public ID2D1Geometry? TryGetGeometryUsingFactory(object factory) => null; + ID2D1Geometry? IGeometrySource2DInterop.TryGetGeometryUsingFactory(object factory) => null; public void Dispose() { } diff --git a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs index 5810c235656d..36065bde0d7d 100644 --- a/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs +++ b/src/Uno.UI.Composition/Win2D/Microsoft/Graphics/Canvas/Geometry/CanvasPathBuilder.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using Uno.UI.Composition; using Windows.Foundation; +using Windows.Foundation.Metadata; +using Windows.Graphics.Interop; using Windows.Graphics.Interop.Direct2D; namespace Microsoft.Graphics.Canvas.Geometry; @@ -87,11 +89,16 @@ public void AddCubicBezier(Vector2 controlPoint1, Vector2 controlPoint2, Vector2 _commands.Add(CompositionPathCommand.Create(segment)); } - // TODO - /*public void AddGeometry(CanvasGeometry geometry) + // TODO: Implement rest of supported CanvasGeometry types, we might need something like ID2D1Geometry::Simplify for that + public void AddGeometry(CanvasGeometry geometry) { - - }*/ + if (geometry is IGeometrySource2DInterop geometryInterop + && geometryInterop.GetGeometry() is ICompositionPathCommandsProvider commandsProvider + && commandsProvider.Commands is List { Count: > 0 } commands) + { + Commands.AddRange(commands); + } + } public void AddLine(Vector2 endPoint) { @@ -100,7 +107,7 @@ public void AddLine(Vector2 endPoint) public void AddLine(float x, float y) { - _commands.Add(CompositionPathCommand.Create(new Point(x, y))); + AddLine(new(x, y)); } public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint) @@ -114,24 +121,24 @@ public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint) _commands.Add(CompositionPathCommand.Create(segment)); } - public void BeginFigure(Vector2 startPoint) + public void BeginFigure(Vector2 startPoint, CanvasFigureFill figureFill) { - _commands.Add(CompositionPathCommand.Create(startPoint.ToPoint(), D2D1FigureBegin.Hollow)); + _commands.Add(CompositionPathCommand.Create(startPoint.ToPoint(), (D2D1FigureBegin)figureFill)); } - public void BeginFigure(float startX, float startY) + public void BeginFigure(float startX, float startY, CanvasFigureFill figureFill) { - _commands.Add(CompositionPathCommand.Create(new(startX, startY), D2D1FigureBegin.Hollow)); + BeginFigure(new(startX, startY), figureFill); } - public void BeginFigure(Vector2 startPoint, CanvasFigureFill figureFill) + public void BeginFigure(Vector2 startPoint) { - _commands.Add(CompositionPathCommand.Create(startPoint.ToPoint(), (D2D1FigureBegin)figureFill)); + BeginFigure(startPoint, (CanvasFigureFill)D2D1FigureBegin.Hollow); } - public void BeginFigure(float startX, float startY, CanvasFigureFill figureFill) + public void BeginFigure(float startX, float startY) { - _commands.Add(CompositionPathCommand.Create(new(startX, startY), (D2D1FigureBegin)figureFill)); + BeginFigure(new(startX, startY), (CanvasFigureFill)D2D1FigureBegin.Hollow); } public void EndFigure(CanvasFigureLoop figureLoop) From ae22c1697592cd1367189f1c2f63a6caa109236c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Thu, 25 Jul 2024 23:51:58 +0300 Subject: [PATCH 07/31] fix(composition): Fix RedirectVisual --- src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs b/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs index 5f6b76286d5f..0a9c569935a7 100644 --- a/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs +++ b/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs @@ -10,7 +10,13 @@ public partial class RedirectVisual : ContainerVisual internal override void Paint(in PaintingSession session) { base.Paint(in session); - Source?.Paint(session); + + if (Source is not null && session.Canvas is not null) + { + int save = session.Canvas.Save(); + Source.RenderRootVisual(session.Surface, default, null); + session.Canvas.RestoreToCount(save); + } } } } From a714c87de344a3cad96a9f634f984c1c0c977b9c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Fri, 26 Jul 2024 00:45:11 +0300 Subject: [PATCH 08/31] fix(composition): remove unnecessary canvas save --- src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs b/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs index 0a9c569935a7..0f1572ca0d3b 100644 --- a/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs +++ b/src/Uno.UI.Composition/Composition/RedirectVisual.skia.cs @@ -13,9 +13,7 @@ internal override void Paint(in PaintingSession session) if (Source is not null && session.Canvas is not null) { - int save = session.Canvas.Save(); Source.RenderRootVisual(session.Surface, default, null); - session.Canvas.RestoreToCount(save); } } } From c49dac0fe65171d8535165b2bdb18cc6414c2c39 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sun, 28 Jul 2024 00:11:24 +0300 Subject: [PATCH 09/31] feat(composition): High DPI support for ISkiaSurface --- .../CompositionSurfaceBrush.skia.cs | 22 ++++++++--------- .../CompositionVisualSurface.skia.cs | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Uno.UI.Composition/Composition/CompositionSurfaceBrush.skia.cs b/src/Uno.UI.Composition/Composition/CompositionSurfaceBrush.skia.cs index 37e0d0dad0db..64c4cc9d9881 100644 --- a/src/Uno.UI.Composition/Composition/CompositionSurfaceBrush.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionSurfaceBrush.skia.cs @@ -17,16 +17,13 @@ public partial class CompositionSurfaceBrush : CompositionBrush, IOnlineBrush, I bool ISizedBrush.IsSized => true; - Vector2? ISizedBrush.Size + Vector2? ISizedBrush.Size => Surface switch { - get => Surface switch - { - SkiaCompositionSurface { Image: SKImage img } => new(img.Width, img.Height), - ISkiaSurface { Surface: SKSurface surface } => new(surface.Canvas.DeviceClipBounds.Width, surface.Canvas.DeviceClipBounds.Height), - ISkiaCompositionSurfaceProvider { SkiaCompositionSurface: { Image: SKImage img } } => new(img.Width, img.Height), - _ => null - }; - } + SkiaCompositionSurface { Image: SKImage img } => new(img.Width, img.Height), + ISkiaSurface { Surface: SKSurface surface } => new(surface.Canvas.DeviceClipBounds.Width / surface.Canvas.TotalMatrix.ScaleX, surface.Canvas.DeviceClipBounds.Height / surface.Canvas.TotalMatrix.ScaleY), + ISkiaCompositionSurfaceProvider { SkiaCompositionSurface: { Image: SKImage img } } => new(img.Width, img.Height), + _ => null + }; private Rect GetArrangedImageRect(Size sourceSize, SKRect targetRect) { @@ -132,11 +129,12 @@ internal override void UpdatePaint(SKPaint fillPaint, SKRect bounds) if (skiaSurface.Surface is not null) { - fillPaint.Shader = skiaSurface.Surface.Snapshot().ToShader(SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, TransformMatrix.ToSKMatrix()); + var matrix = TransformMatrix * Matrix3x2.CreateScale(1 / skiaSurface.Surface.Canvas.TotalMatrix.ScaleX); + var image = skiaSurface.Surface.Snapshot(); + + fillPaint.Shader = SKShader.CreateImage(image, SKShaderTileMode.Decal, SKShaderTileMode.Decal, matrix.ToSKMatrix()); fillPaint.IsAntialias = true; fillPaint.FilterQuality = SKFilterQuality.High; - fillPaint.IsAutohinted = true; - fillPaint.IsDither = true; } else { diff --git a/src/Uno.UI.Composition/Composition/CompositionVisualSurface.skia.cs b/src/Uno.UI.Composition/Composition/CompositionVisualSurface.skia.cs index 9d3f89ea379d..5c43941b7fae 100644 --- a/src/Uno.UI.Composition/Composition/CompositionVisualSurface.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionVisualSurface.skia.cs @@ -16,6 +16,7 @@ namespace Microsoft.UI.Composition public partial class CompositionVisualSurface : CompositionObject, ICompositionSurface, ISkiaSurface { private SKSurface? _surface; + private DisplayInformation? _displayInformation; SKSurface? ISkiaSurface.Surface { get => _surface; } @@ -36,9 +37,19 @@ void ISkiaSurface.UpdateSurface(bool recreateSurface) } }; + if (_displayInformation is null) + { + _displayInformation = DisplayInformation.GetForCurrentViewSafe(); + _displayInformation.DpiChanged += DpiChanged; + } + + var scale = _displayInformation.LogicalDpi / DisplayInformation.BaseDpi; + size *= scale; + var info = new SKImageInfo((int)size.X, (int)size.Y, SKImageInfo.PlatformColorType, SKAlphaType.Premul); _surface = SKSurface.Create(info); canvas = _surface.Canvas; + canvas.Scale(scale, scale); } canvas ??= _surface.Canvas; @@ -54,6 +65,14 @@ void ISkiaSurface.UpdateSurface(bool recreateSurface) } } + private void DpiChanged(DisplayInformation sender, object args) + { + if (_surface is not null) + { + ((ISkiaSurface)this).UpdateSurface(true); + } + } + void ISkiaSurface.UpdateSurface(in Visual.PaintingSession session) { if (SourceVisual is not null && session.Canvas is not null) @@ -77,6 +96,11 @@ private protected override void DisposeInternal() base.Dispose(); _surface?.Dispose(); + + if (_displayInformation is not null) + { + _displayInformation.DpiChanged -= DpiChanged; + } } partial void OnSourceVisualChangedPartial(Visual? sourceVisual) => ((ISkiaSurface)this).UpdateSurface(SourceSize == default && sourceVisual?.Size != default); From 3b47eed7abd616477e2e01387e2239bb8d135f76 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Mon, 5 Aug 2024 01:57:21 +0300 Subject: [PATCH 10/31] feat(composition): add Path Shape UI test for CompositionSpriteShape --- .../Given_CompositionSpriteShape.cs | 160 ++++++++++++++++++ .../Given_RedirectVisual.cs | 1 - 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs new file mode 100644 index 000000000000..7172b8376ff0 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Private.Infrastructure; +using Uno.Extensions; +using Uno.UI.RuntimeTests.Helpers; +using Microsoft.UI.Composition; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; +using Microsoft.UI.Xaml.Shapes; +using Microsoft.UI.Xaml.Markup; +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Graphics.Canvas; + +namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Composition; + +[TestClass] +public class Given_CompositionSpriteShape +{ +#if __SKIA__ + [TestMethod] + [RunsOnUIThread] + public async Task When_Using_Path() + { + Vector2 scale = new(200f / 570f); + Vector2 offset = -112.5f * scale; + + var compositor = Compositor.GetSharedCompositor(); + var expected = new ContentControl() + { + Width = 200, + Height = 200, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left, + Content = new Path + { + Margin = new(0, 0, -600, -600), + Fill = new SolidColorBrush(Windows.UI.Colors.Black), + Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), "M656.5 400.5C656.5 350.637 598.572 307.493 514.292 286.708C493.507 202.428 450.363 144.5 400.5 144.5C350.637 144.5 307.493 202.428 286.708 286.708C202.428 307.493 144.5 350.637 144.5 400.5C144.5 450.363 202.428 493.507 286.708 514.292C307.493 598.572 350.637 656.5 400.5 656.5C450.363 656.5 493.507 598.572 514.292 514.292C598.572 493.507 656.5 450.363 656.5 400.5ZM581.519 219.481C546.261 184.222 474.793 194.676 400.5 239.574C326.207 194.676 254.739 184.222 219.481 219.481C184.222 254.739 194.676 326.207 239.574 400.5C194.676 474.792 184.222 546.261 219.481 581.519C254.739 616.778 326.207 606.324 400.5 561.426C474.793 606.324 546.261 616.778 581.519 581.519C616.778 546.261 606.324 474.792 561.426 400.5C606.324 326.207 616.778 254.739 581.519 219.481ZM148.5 112.5L646.5 112.5L646.5 112.5Q647.384 112.5 648.266 112.543Q649.149 112.587 650.029 112.673Q650.908 112.76 651.782 112.89Q652.656 113.019 653.523 113.192Q654.39 113.364 655.247 113.579Q656.104 113.794 656.95 114.05Q657.796 114.307 658.628 114.604Q659.46 114.902 660.277 115.24Q661.093 115.579 661.892 115.956Q662.691 116.334 663.47 116.751Q664.25 117.167 665.008 117.622Q665.766 118.076 666.5 118.567Q667.235 119.058 667.945 119.585Q668.655 120.111 669.338 120.672Q670.021 121.232 670.676 121.826Q671.331 122.419 671.956 123.044Q672.581 123.669 673.174 124.324Q673.768 124.979 674.328 125.662Q674.889 126.345 675.415 127.055Q675.942 127.765 676.433 128.499Q676.924 129.234 677.378 129.992Q677.832 130.75 678.249 131.53Q678.666 132.309 679.043 133.108Q679.421 133.907 679.76 134.723Q680.098 135.54 680.396 136.372Q680.693 137.204 680.95 138.05Q681.206 138.895 681.421 139.753Q681.636 140.61 681.808 141.477Q681.981 142.344 682.11 143.218Q682.24 144.092 682.327 144.971Q682.413 145.851 682.457 146.734Q682.5 147.616 682.5 148.5L682.5 646.5L682.5 646.5Q682.5 647.384 682.457 648.266Q682.413 649.149 682.327 650.029Q682.24 650.908 682.11 651.782Q681.981 652.656 681.808 653.523Q681.636 654.39 681.421 655.247Q681.206 656.104 680.95 656.95Q680.693 657.796 680.396 658.628Q680.098 659.46 679.76 660.277Q679.421 661.093 679.043 661.892Q678.666 662.691 678.249 663.47Q677.832 664.25 677.378 665.008Q676.924 665.766 676.433 666.5Q675.942 667.235 675.415 667.945Q674.889 668.655 674.328 669.338Q673.768 670.021 673.174 670.676Q672.581 671.331 671.956 671.956Q671.331 672.581 670.676 673.174Q670.021 673.768 669.338 674.328Q668.655 674.889 667.945 675.415Q667.235 675.942 666.5 676.433Q665.766 676.924 665.008 677.378Q664.25 677.832 663.47 678.249Q662.691 678.666 661.892 679.043Q661.093 679.421 660.277 679.76Q659.46 680.098 658.628 680.396Q657.796 680.693 656.95 680.95Q656.104 681.206 655.247 681.421Q654.39 681.636 653.523 681.808Q652.656 681.981 651.782 682.11Q650.908 682.24 650.029 682.327Q649.149 682.413 648.266 682.457Q647.384 682.5 646.5 682.5L148.5 682.5L148.5 682.5Q147.616 682.5 146.734 682.457Q145.851 682.413 144.971 682.327Q144.092 682.24 143.218 682.11Q142.344 681.981 141.477 681.808Q140.61 681.636 139.753 681.421Q138.895 681.206 138.05 680.95Q137.204 680.693 136.372 680.396Q135.54 680.098 134.723 679.76Q133.907 679.421 133.108 679.043Q132.309 678.666 131.53 678.249Q130.75 677.832 129.992 677.378Q129.234 676.924 128.499 676.433Q127.765 675.942 127.055 675.415Q126.345 674.889 125.662 674.328Q124.979 673.768 124.324 673.174Q123.669 672.581 123.044 671.956Q122.419 671.331 121.826 670.676Q121.232 670.021 120.672 669.338Q120.111 668.655 119.585 667.945Q119.058 667.235 118.567 666.5Q118.076 665.766 117.622 665.008Q117.167 664.25 116.751 663.47Q116.334 662.691 115.956 661.892Q115.579 661.093 115.24 660.277Q114.902 659.46 114.604 658.628Q114.307 657.796 114.05 656.95Q113.794 656.104 113.579 655.247Q113.364 654.39 113.192 653.523Q113.019 652.656 112.89 651.782Q112.76 650.908 112.673 650.029Q112.587 649.149 112.543 648.266Q112.5 647.384 112.5 646.5L112.5 148.5L112.5 148.5Q112.5 147.616 112.543 146.734Q112.587 145.851 112.673 144.971Q112.76 144.092 112.89 143.218Q113.019 142.344 113.192 141.477Q113.364 140.61 113.579 139.753Q113.794 138.895 114.05 138.05Q114.307 137.204 114.604 136.372Q114.902 135.54 115.24 134.723Q115.579 133.907 115.956 133.108Q116.334 132.309 116.751 131.53Q117.167 130.75 117.622 129.992Q118.076 129.234 118.567 128.499Q119.058 127.765 119.585 127.055Q120.111 126.345 120.672 125.662Q121.232 124.979 121.826 124.324Q122.419 123.669 123.044 123.044Q123.669 122.419 124.324 121.826Q124.979 121.232 125.662 120.672Q126.345 120.111 127.055 119.585Q127.765 119.058 128.499 118.567Q129.234 118.076 129.992 117.622Q130.75 117.167 131.53 116.751Q132.309 116.334 133.108 115.956Q133.907 115.579 134.723 115.24Q135.54 114.902 136.372 114.604Q137.204 114.307 138.05 114.05Q138.895 113.794 139.753 113.579Q140.61 113.364 141.477 113.192Q142.344 113.019 143.218 112.89Q144.092 112.76 144.971 112.673Q145.851 112.587 146.734 112.543Q147.616 112.5 148.5 112.5Z"), + RenderTransform = new CompositeTransform() + { + ScaleX = scale.X, + ScaleY = scale.Y, + TranslateX = offset.X, + TranslateY = offset.Y + } + } + }; + + using (var builder = new CanvasPathBuilder(CanvasDevice.GetSharedDevice())) + { + var visual = compositor.CreateShapeVisual(); + + builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); + + builder.BeginFigure(new Vector2(656.5f, 400.5f)); + builder.AddCubicBezier(new Vector2(656.5f, 350.637f), new Vector2(598.572f, 307.493f), new Vector2(514.292f, 286.708f)); + builder.AddCubicBezier(new Vector2(493.507f, 202.428f), new Vector2(450.363f, 144.5f), new Vector2(400.5f, 144.5f)); + builder.AddCubicBezier(new Vector2(350.637f, 144.5f), new Vector2(307.493f, 202.428f), new Vector2(286.708f, 286.708f)); + builder.AddCubicBezier(new Vector2(202.428f, 307.493f), new Vector2(144.5f, 350.637f), new Vector2(144.5f, 400.5f)); + builder.AddCubicBezier(new Vector2(144.5f, 450.363f), new Vector2(202.428f, 493.507f), new Vector2(286.708f, 514.292f)); + builder.AddCubicBezier(new Vector2(307.493f, 598.572f), new Vector2(350.637f, 656.5f), new Vector2(400.5f, 656.5f)); + builder.AddCubicBezier(new Vector2(450.363f, 656.5f), new Vector2(493.507f, 598.572f), new Vector2(514.292f, 514.292f)); + builder.AddCubicBezier(new Vector2(598.572f, 493.507f), new Vector2(656.5f, 450.363f), new Vector2(656.5f, 400.5f)); + builder.EndFigure(CanvasFigureLoop.Closed); + + builder.BeginFigure(new Vector2(581.519f, 219.481f)); + builder.AddCubicBezier(new Vector2(546.261f, 184.222f), new Vector2(474.793f, 194.676f), new Vector2(400.5f, 239.574f)); + builder.AddCubicBezier(new Vector2(326.207f, 194.676f), new Vector2(254.739f, 184.222f), new Vector2(219.481f, 219.481f)); + builder.AddCubicBezier(new Vector2(184.222f, 254.739f), new Vector2(194.676f, 326.207f), new Vector2(239.574f, 400.5f)); + builder.AddCubicBezier(new Vector2(194.676f, 474.792f), new Vector2(184.222f, 546.261f), new Vector2(219.481f, 581.519f)); + builder.AddCubicBezier(new Vector2(254.739f, 616.778f), new Vector2(326.207f, 606.324f), new Vector2(400.5f, 561.426f)); + builder.AddCubicBezier(new Vector2(474.793f, 606.324f), new Vector2(546.261f, 616.778f), new Vector2(581.519f, 581.519f)); + builder.AddCubicBezier(new Vector2(616.778f, 546.261f), new Vector2(606.324f, 474.792f), new Vector2(561.426f, 400.5f)); + builder.AddCubicBezier(new Vector2(606.324f, 326.207f), new Vector2(616.778f, 254.739f), new Vector2(581.519f, 219.481f)); + builder.EndFigure(CanvasFigureLoop.Closed); + + builder.BeginFigure(new Vector2(148.5f, 112.5f)); + builder.AddLine(new Vector2(646.5f, 112.5f)); + builder.AddLine(new Vector2(646.5f, 112.5f)); + builder.AddArc(new Vector2(682.5f, 148.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(682.5f, 646.5f)); + builder.AddLine(new Vector2(682.5f, 646.5f)); + builder.AddArc(new Vector2(646.5f, 682.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(148.5f, 682.5f)); + builder.AddLine(new Vector2(148.5f, 682.5f)); + builder.AddArc(new Vector2(112.5f, 646.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.AddLine(new Vector2(112.5f, 148.5f)); + builder.AddLine(new Vector2(112.5f, 148.5f)); + builder.AddArc(new Vector2(148.5f, 112.5f), 36, 36, 0, CanvasSweepDirection.Clockwise, CanvasArcSize.Small); + builder.EndFigure(CanvasFigureLoop.Closed); + + var path = new CompositionPath(CanvasGeometry.CreatePath(builder)); + + await RenderPath(path, expected, scale, offset); + } + } + + private async Task RenderPath(CompositionPath path, FrameworkElement expected, Vector2? scale = null, Vector2? offset = null) + { + var compositor = Compositor.GetSharedCompositor(); + using var visual = compositor.CreateShapeVisual(); + using var pathGeometry = compositor.CreatePathGeometry(path); + using var shape = compositor.CreateSpriteShape(pathGeometry); + + if (scale is not null) + { + shape.Scale = scale.Value; + } + + if (offset is not null) + { + shape.Offset = offset.Value; + } + + shape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.Black); + + visual.Shapes.Add(shape); + visual.Size = new(200); + + var sut = new ContentControl + { + Width = 200, + Height = 200 + }; + + ElementCompositionPreview.SetElementChildVisual(sut, visual); + + var result = await Render(expected, sut); + await ImageAssert.AreSimilarAsync(result.actual, result.expected); + } + + private async Task<(RawBitmap expected, RawBitmap actual)> Render(FrameworkElement expected, FrameworkElement sut) + { + await UITestHelper.Load(new Grid + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition() + }, + Children = + { + expected.Apply(e => Grid.SetColumn(e, 0)), + sut.Apply(e => Grid.SetColumn(e, 1)) + }, + Height = 200, + Width = 400 + }); + + return (await UITestHelper.ScreenShot(expected), await UITestHelper.ScreenShot(sut)); + } +#endif +} diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_RedirectVisual.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_RedirectVisual.cs index 61c32d9c9f2e..f00b0e59e854 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_RedirectVisual.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_RedirectVisual.cs @@ -21,7 +21,6 @@ public class Given_RedirectVisual #if __SKIA__ [TestMethod] [RunsOnUIThread] - [Ignore("Disabled because of https://github.com/unoplatform/uno-private/issues/307")] public async Task When_Source_Changes() { var compositor = Window.Current.Compositor; From 93ee9d7146beb462355d471b0a92ca9141552eaf Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Mon, 5 Aug 2024 21:31:22 +0300 Subject: [PATCH 11/31] feat(composition): add more CompositionSpriteShape tests --- .../CompositionPathTests.xaml.cs | 14 +-- .../Given_CompositionSpriteShape.cs | 89 +++++++++++++++++-- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index c7b56aff985b..d5aa9486ce9e 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -777,28 +777,30 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) var polyGeometry = compositor.CreatePathGeometry(polyPath); var polyShape = compositor.CreateSpriteShape(polyGeometry); - polyShape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.LightYellow); + polyShape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.PaleVioletRed); polyVisual.Shapes.Add(polyShape); polyVisual.Size = new(200); + polyVisual.Offset = new(0, 5, 0); ElementCompositionPreview.SetElementChildVisual(compPresenter6, polyVisual); } private Vector2[] GetStarPoints(Vector2 center, float radius, int numPoints, float innerRadiusFactor) { - var angleDelta = 2 * MathF.PI / numPoints; - var points = new Vector2[numPoints * 2]; + Vector2[] points = new Vector2[numPoints * 2]; + float angleStep = 2f * MathF.PI / numPoints; + float innerRadius = radius * innerRadiusFactor; for (int i = 0; i < numPoints * 2; i++) { - float angle = i * angleDelta; - float currentRadius = (i % 2 == 0) ? radius : radius * innerRadiusFactor; + float currentRadius = (i % 2 == 0) ? radius : innerRadius; + float angle = i * angleStep / 2f - MathF.PI / 2f; float x = center.X + currentRadius * MathF.Cos(angle); float y = center.Y + currentRadius * MathF.Sin(angle); - points[i] = new(x, y); + points[i] = new Vector2(x, y); } return points; diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs index 7172b8376ff0..be9e25241b06 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs @@ -17,6 +17,7 @@ using System.Numerics; using Microsoft.Graphics.Canvas.Geometry; using Microsoft.Graphics.Canvas; +using Windows.Foundation; namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Composition; @@ -31,7 +32,6 @@ public async Task When_Using_Path() Vector2 scale = new(200f / 570f); Vector2 offset = -112.5f * scale; - var compositor = Compositor.GetSharedCompositor(); var expected = new ContentControl() { Width = 200, @@ -55,8 +55,6 @@ public async Task When_Using_Path() using (var builder = new CanvasPathBuilder(CanvasDevice.GetSharedDevice())) { - var visual = compositor.CreateShapeVisual(); - builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Alternate); builder.BeginFigure(new Vector2(656.5f, 400.5f)); @@ -102,7 +100,62 @@ public async Task When_Using_Path() } } - private async Task RenderPath(CompositionPath path, FrameworkElement expected, Vector2? scale = null, Vector2? offset = null) + [TestMethod] + [RunsOnUIThread] + public async Task When_Using_Circle() + { + var expected = new Ellipse() + { + Width = 200, + Height = 200, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left, + Fill = new SolidColorBrush(Windows.UI.Colors.Black) + }; + + await RenderPath(new CompositionPath(CanvasGeometry.CreateCircle(CanvasDevice.GetSharedDevice(), new(100, 100), 100)), expected); + } + + [TestMethod] + [RunsOnUIThread] + public async Task When_Using_RoundedRectangle() + { + var expected = new Rectangle() + { + Width = 200, + Height = 200, + RadiusX = 16, + RadiusY = 16, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left, + Fill = new SolidColorBrush(Windows.UI.Colors.Black) + }; + + await RenderPath(new CompositionPath(CanvasGeometry.CreateRoundedRectangle(CanvasDevice.GetSharedDevice(), new(0, 0, 200, 200), 16, 16)), expected); + } + + [TestMethod] + [RunsOnUIThread] + public async Task When_Using_Polygon() + { + var points = GetStarPoints(new(100), 100, 5, 0.5f); + var pointCollection = new PointCollection(); + pointCollection.AddRange(points.Select(VectorExtensions.ToPoint)); + + var expected = new Polygon() + { + Width = 200, + Height = 200, + VerticalAlignment = VerticalAlignment.Top, + HorizontalAlignment = HorizontalAlignment.Left, + Fill = new SolidColorBrush(Windows.UI.Colors.Black), + Points = pointCollection + }; + + await RenderPath(new CompositionPath(CanvasGeometry.CreatePolygon(CanvasDevice.GetSharedDevice(), points)), expected); + } + + private async Task RenderPath(CompositionPath path, FrameworkElement expected, Vector2? scale = null, Vector2? offset = null, Windows.UI.Color? color = null) { var compositor = Compositor.GetSharedCompositor(); using var visual = compositor.CreateShapeVisual(); @@ -119,15 +172,15 @@ private async Task RenderPath(CompositionPath path, FrameworkElement expected, V shape.Offset = offset.Value; } - shape.FillBrush = compositor.CreateColorBrush(Windows.UI.Colors.Black); + shape.FillBrush = compositor.CreateColorBrush(color ?? Windows.UI.Colors.Black); visual.Shapes.Add(shape); - visual.Size = new(200); + visual.Size = new((float)expected.Width, (float)expected.Height); var sut = new ContentControl { - Width = 200, - Height = 200 + Width = expected.Width, + Height = expected.Height }; ElementCompositionPreview.SetElementChildVisual(sut, visual); @@ -156,5 +209,25 @@ await UITestHelper.Load(new Grid return (await UITestHelper.ScreenShot(expected), await UITestHelper.ScreenShot(sut)); } + + private Vector2[] GetStarPoints(Vector2 center, float radius, int numPoints, float innerRadiusFactor) + { + Vector2[] points = new Vector2[numPoints * 2]; + float angleStep = 2f * MathF.PI / numPoints; + float innerRadius = radius * innerRadiusFactor; + + for (int i = 0; i < numPoints * 2; i++) + { + float currentRadius = (i % 2 == 0) ? radius : innerRadius; + float angle = i * angleStep / 2f - MathF.PI / 2f; + + float x = center.X + currentRadius * MathF.Cos(angle); + float y = center.Y + currentRadius * MathF.Sin(angle); + + points[i] = new Vector2(x, y); + } + + return points; + } #endif } From 861ec9b12de8632516c801f82cc99d342201d1ba Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Fri, 9 Aug 2024 00:08:12 +0300 Subject: [PATCH 12/31] feat(composition): add support for all geometry types for CompositionGeometricClip + optimize geometry creation and rendering --- .../CompositionEllipseGeometry.skia.cs | 24 +++++++- .../CompositionGeometricClip.skia.cs | 48 ++++++++-------- .../CompositionLineGeometry.skia.cs | 24 +++++++- .../CompositionPathGeometry.skia.cs | 6 ++ .../CompositionRectangleGeometry.skia.cs | 23 +++++++- ...ompositionRoundedRectangleGeometry.skia.cs | 25 ++++++++- .../Given_CompositionSpriteShape.cs | 55 ++++++++++++++++++- 7 files changed, 174 insertions(+), 31 deletions(-) diff --git a/src/Uno.UI.Composition/Composition/CompositionEllipseGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionEllipseGeometry.skia.cs index da41d832bd84..ee5589286f6e 100644 --- a/src/Uno.UI.Composition/Composition/CompositionEllipseGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionEllipseGeometry.skia.cs @@ -1,12 +1,34 @@ #nullable enable +using System.Windows.Input; using Windows.Graphics; namespace Microsoft.UI.Composition { public partial class CompositionEllipseGeometry : CompositionGeometry { - internal override IGeometrySource2D? BuildGeometry() + private SkiaGeometrySource2D? _geometrySource2D; + + internal override IGeometrySource2D? BuildGeometry() => _geometrySource2D; + + private SkiaGeometrySource2D? InternalBuildGeometry() => new SkiaGeometrySource2D(BuildEllipseGeometry(Center, Radius)); + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + if (propertyName is nameof(Center) or nameof(Radius)) + { + _geometrySource2D?.Dispose(); + _geometrySource2D = InternalBuildGeometry(); + } + + base.OnPropertyChangedCore(propertyName, isSubPropertyChange); + } + + private protected override void DisposeInternal() + { + _geometrySource2D?.Dispose(); + base.DisposeInternal(); + } } } diff --git a/src/Uno.UI.Composition/Composition/CompositionGeometricClip.skia.cs b/src/Uno.UI.Composition/Composition/CompositionGeometricClip.skia.cs index d87b3ac55057..685dd1bfa4ac 100644 --- a/src/Uno.UI.Composition/Composition/CompositionGeometricClip.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionGeometricClip.skia.cs @@ -1,8 +1,10 @@ #nullable enable using System; +using System.IO; using SkiaSharp; using Windows.Foundation; +using Windows.Graphics.Interop; namespace Microsoft.UI.Composition; @@ -10,27 +12,31 @@ partial class CompositionGeometricClip { private protected override Rect? GetBoundsCore(Visual visual) { - switch (Geometry) + if (Geometry is not null) { - case CompositionPathGeometry { Path.GeometrySource: SkiaGeometrySource2D geometrySource }: - return geometrySource.Geometry.TightBounds.ToRect(); - - case CompositionPathGeometry cpg: - throw new InvalidOperationException($"Clipping with source {cpg.Path?.GeometrySource} is not supported"); - - case null: - return null; + var geometry = Geometry.BuildGeometry(); - default: - throw new InvalidOperationException($"Clipping with {Geometry} is not supported"); + if (geometry is SkiaGeometrySource2D geometrySource) + { + return geometrySource.Geometry.TightBounds.ToRect(); + } + else + { + throw new InvalidOperationException($"Clipping with source {geometry} is not supported"); + } } + + return null; } internal override void Apply(SKCanvas canvas, Visual visual) { - switch (Geometry) + if (Geometry is not null) { - case CompositionPathGeometry { Path.GeometrySource: SkiaGeometrySource2D geometrySource }: + var geometry = Geometry.BuildGeometry(); + + if (geometry is SkiaGeometrySource2D geometrySource) + { var path = geometrySource.Geometry; if (!TransformMatrix.IsIdentity) { @@ -40,17 +46,11 @@ internal override void Apply(SKCanvas canvas, Visual visual) } canvas.ClipPath(path, antialias: true); - break; - - case CompositionPathGeometry cpg: - throw new InvalidOperationException($"Clipping with source {cpg.Path?.GeometrySource} is not supported"); - - case null: - // null is nop - break; - - default: - throw new InvalidOperationException($"Clipping with {Geometry} is not supported"); + } + else + { + throw new InvalidOperationException($"Clipping with source {geometry} is not supported"); + } } } } diff --git a/src/Uno.UI.Composition/Composition/CompositionLineGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionLineGeometry.skia.cs index a3e825f69321..ea2ef6654a80 100644 --- a/src/Uno.UI.Composition/Composition/CompositionLineGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionLineGeometry.skia.cs @@ -6,6 +6,28 @@ namespace Microsoft.UI.Composition { public partial class CompositionLineGeometry : CompositionGeometry { - internal override IGeometrySource2D? BuildGeometry() => new SkiaGeometrySource2D(BuildLineGeometry(Start, End)); + private SkiaGeometrySource2D? _geometrySource2D; + + internal override IGeometrySource2D? BuildGeometry() => _geometrySource2D; + + private SkiaGeometrySource2D? InternalBuildGeometry() + => new SkiaGeometrySource2D(BuildLineGeometry(Start, End)); + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + if (propertyName is nameof(Start) or nameof(End)) + { + _geometrySource2D?.Dispose(); + _geometrySource2D = InternalBuildGeometry(); + } + + base.OnPropertyChangedCore(propertyName, isSubPropertyChange); + } + + private protected override void DisposeInternal() + { + _geometrySource2D?.Dispose(); + base.DisposeInternal(); + } } } diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs index 31710c62be4d..4d33a253232d 100644 --- a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs @@ -207,6 +207,12 @@ private protected override void OnPropertyChangedCore(string? propertyName, bool base.OnPropertyChangedCore(propertyName, isSubPropertyChange); } + private protected override void DisposeInternal() + { + _geometrySource2D?.Dispose(); + base.DisposeInternal(); + } + void ID2D1GeometrySink.AddLine(Point point) => _commands.Add(CompositionPathCommand.Create(point)); void ID2D1GeometrySink.AddBezier(D2D1BezierSegment bezier) => _commands.Add(CompositionPathCommand.Create(bezier)); diff --git a/src/Uno.UI.Composition/Composition/CompositionRectangleGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionRectangleGeometry.skia.cs index 50f605803166..4ac39a707bed 100644 --- a/src/Uno.UI.Composition/Composition/CompositionRectangleGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionRectangleGeometry.skia.cs @@ -7,7 +7,28 @@ namespace Microsoft.UI.Composition { public partial class CompositionRectangleGeometry : CompositionGeometry { - internal override IGeometrySource2D? BuildGeometry() + private SkiaGeometrySource2D? _geometrySource2D; + + internal override IGeometrySource2D? BuildGeometry() => _geometrySource2D; + + private SkiaGeometrySource2D? InternalBuildGeometry() => new SkiaGeometrySource2D(BuildRectangleGeometry(Offset, Size)); + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + if (propertyName is nameof(Offset) or nameof(Size)) + { + _geometrySource2D?.Dispose(); + _geometrySource2D = InternalBuildGeometry(); + } + + base.OnPropertyChangedCore(propertyName, isSubPropertyChange); + } + + private protected override void DisposeInternal() + { + _geometrySource2D?.Dispose(); + base.DisposeInternal(); + } } } diff --git a/src/Uno.UI.Composition/Composition/CompositionRoundedRectangleGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionRoundedRectangleGeometry.skia.cs index e2680fe94f51..83788c5af17a 100644 --- a/src/Uno.UI.Composition/Composition/CompositionRoundedRectangleGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionRoundedRectangleGeometry.skia.cs @@ -1,14 +1,18 @@ #nullable enable -using System.Numerics; using SkiaSharp; +using System.Numerics; using Windows.Graphics; namespace Microsoft.UI.Composition { public partial class CompositionRoundedRectangleGeometry : CompositionGeometry { - internal override IGeometrySource2D? BuildGeometry() + private SkiaGeometrySource2D? _geometrySource2D; + + internal override IGeometrySource2D? BuildGeometry() => _geometrySource2D; + + private SkiaGeometrySource2D? InternalBuildGeometry() { SKPath? path; @@ -26,5 +30,22 @@ public partial class CompositionRoundedRectangleGeometry : CompositionGeometry return new SkiaGeometrySource2D(path); } + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + if (propertyName is nameof(Offset) or nameof(Size) or nameof(CornerRadius)) + { + _geometrySource2D?.Dispose(); + _geometrySource2D = InternalBuildGeometry(); + } + + base.OnPropertyChangedCore(propertyName, isSubPropertyChange); + } + + private protected override void DisposeInternal() + { + _geometrySource2D?.Dispose(); + base.DisposeInternal(); + } } } diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs index be9e25241b06..5347f1754974 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Composition/Given_CompositionSpriteShape.cs @@ -42,7 +42,59 @@ public async Task When_Using_Path() { Margin = new(0, 0, -600, -600), Fill = new SolidColorBrush(Windows.UI.Colors.Black), - Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), "M656.5 400.5C656.5 350.637 598.572 307.493 514.292 286.708C493.507 202.428 450.363 144.5 400.5 144.5C350.637 144.5 307.493 202.428 286.708 286.708C202.428 307.493 144.5 350.637 144.5 400.5C144.5 450.363 202.428 493.507 286.708 514.292C307.493 598.572 350.637 656.5 400.5 656.5C450.363 656.5 493.507 598.572 514.292 514.292C598.572 493.507 656.5 450.363 656.5 400.5ZM581.519 219.481C546.261 184.222 474.793 194.676 400.5 239.574C326.207 194.676 254.739 184.222 219.481 219.481C184.222 254.739 194.676 326.207 239.574 400.5C194.676 474.792 184.222 546.261 219.481 581.519C254.739 616.778 326.207 606.324 400.5 561.426C474.793 606.324 546.261 616.778 581.519 581.519C616.778 546.261 606.324 474.792 561.426 400.5C606.324 326.207 616.778 254.739 581.519 219.481ZM148.5 112.5L646.5 112.5L646.5 112.5Q647.384 112.5 648.266 112.543Q649.149 112.587 650.029 112.673Q650.908 112.76 651.782 112.89Q652.656 113.019 653.523 113.192Q654.39 113.364 655.247 113.579Q656.104 113.794 656.95 114.05Q657.796 114.307 658.628 114.604Q659.46 114.902 660.277 115.24Q661.093 115.579 661.892 115.956Q662.691 116.334 663.47 116.751Q664.25 117.167 665.008 117.622Q665.766 118.076 666.5 118.567Q667.235 119.058 667.945 119.585Q668.655 120.111 669.338 120.672Q670.021 121.232 670.676 121.826Q671.331 122.419 671.956 123.044Q672.581 123.669 673.174 124.324Q673.768 124.979 674.328 125.662Q674.889 126.345 675.415 127.055Q675.942 127.765 676.433 128.499Q676.924 129.234 677.378 129.992Q677.832 130.75 678.249 131.53Q678.666 132.309 679.043 133.108Q679.421 133.907 679.76 134.723Q680.098 135.54 680.396 136.372Q680.693 137.204 680.95 138.05Q681.206 138.895 681.421 139.753Q681.636 140.61 681.808 141.477Q681.981 142.344 682.11 143.218Q682.24 144.092 682.327 144.971Q682.413 145.851 682.457 146.734Q682.5 147.616 682.5 148.5L682.5 646.5L682.5 646.5Q682.5 647.384 682.457 648.266Q682.413 649.149 682.327 650.029Q682.24 650.908 682.11 651.782Q681.981 652.656 681.808 653.523Q681.636 654.39 681.421 655.247Q681.206 656.104 680.95 656.95Q680.693 657.796 680.396 658.628Q680.098 659.46 679.76 660.277Q679.421 661.093 679.043 661.892Q678.666 662.691 678.249 663.47Q677.832 664.25 677.378 665.008Q676.924 665.766 676.433 666.5Q675.942 667.235 675.415 667.945Q674.889 668.655 674.328 669.338Q673.768 670.021 673.174 670.676Q672.581 671.331 671.956 671.956Q671.331 672.581 670.676 673.174Q670.021 673.768 669.338 674.328Q668.655 674.889 667.945 675.415Q667.235 675.942 666.5 676.433Q665.766 676.924 665.008 677.378Q664.25 677.832 663.47 678.249Q662.691 678.666 661.892 679.043Q661.093 679.421 660.277 679.76Q659.46 680.098 658.628 680.396Q657.796 680.693 656.95 680.95Q656.104 681.206 655.247 681.421Q654.39 681.636 653.523 681.808Q652.656 681.981 651.782 682.11Q650.908 682.24 650.029 682.327Q649.149 682.413 648.266 682.457Q647.384 682.5 646.5 682.5L148.5 682.5L148.5 682.5Q147.616 682.5 146.734 682.457Q145.851 682.413 144.971 682.327Q144.092 682.24 143.218 682.11Q142.344 681.981 141.477 681.808Q140.61 681.636 139.753 681.421Q138.895 681.206 138.05 680.95Q137.204 680.693 136.372 680.396Q135.54 680.098 134.723 679.76Q133.907 679.421 133.108 679.043Q132.309 678.666 131.53 678.249Q130.75 677.832 129.992 677.378Q129.234 676.924 128.499 676.433Q127.765 675.942 127.055 675.415Q126.345 674.889 125.662 674.328Q124.979 673.768 124.324 673.174Q123.669 672.581 123.044 671.956Q122.419 671.331 121.826 670.676Q121.232 670.021 120.672 669.338Q120.111 668.655 119.585 667.945Q119.058 667.235 118.567 666.5Q118.076 665.766 117.622 665.008Q117.167 664.25 116.751 663.47Q116.334 662.691 115.956 661.892Q115.579 661.093 115.24 660.277Q114.902 659.46 114.604 658.628Q114.307 657.796 114.05 656.95Q113.794 656.104 113.579 655.247Q113.364 654.39 113.192 653.523Q113.019 652.656 112.89 651.782Q112.76 650.908 112.673 650.029Q112.587 649.149 112.543 648.266Q112.5 647.384 112.5 646.5L112.5 148.5L112.5 148.5Q112.5 147.616 112.543 146.734Q112.587 145.851 112.673 144.971Q112.76 144.092 112.89 143.218Q113.019 142.344 113.192 141.477Q113.364 140.61 113.579 139.753Q113.794 138.895 114.05 138.05Q114.307 137.204 114.604 136.372Q114.902 135.54 115.24 134.723Q115.579 133.907 115.956 133.108Q116.334 132.309 116.751 131.53Q117.167 130.75 117.622 129.992Q118.076 129.234 118.567 128.499Q119.058 127.765 119.585 127.055Q120.111 126.345 120.672 125.662Q121.232 124.979 121.826 124.324Q122.419 123.669 123.044 123.044Q123.669 122.419 124.324 121.826Q124.979 121.232 125.662 120.672Q126.345 120.111 127.055 119.585Q127.765 119.058 128.499 118.567Q129.234 118.076 129.992 117.622Q130.75 117.167 131.53 116.751Q132.309 116.334 133.108 115.956Q133.907 115.579 134.723 115.24Q135.54 114.902 136.372 114.604Q137.204 114.307 138.05 114.05Q138.895 113.794 139.753 113.579Q140.61 113.364 141.477 113.192Q142.344 113.019 143.218 112.89Q144.092 112.76 144.971 112.673Q145.851 112.587 146.734 112.543Q147.616 112.5 148.5 112.5Z"), + Data = (Geometry)XamlBindingHelper.ConvertValue(typeof(Geometry), + """ + M656.5 400.5C656.5 350.637 598.572 307.493 514.292 286.708C493.507 + 202.428 450.363 144.5 400.5 144.5C350.637 144.5 307.493 202.428 286.708 + 286.708C202.428 307.493 144.5 350.637 144.5 400.5C144.5 450.363 202.428 + 493.507 286.708 514.292C307.493 598.572 350.637 656.5 400.5 656.5C450.363 + 656.5 493.507 598.572 514.292 514.292C598.572 493.507 656.5 450.363 656.5 + 400.5ZM581.519 219.481C546.261 184.222 474.793 194.676 400.5 239.574C326.207 + 194.676 254.739 184.222 219.481 219.481C184.222 254.739 194.676 326.207 239.574 + 400.5C194.676 474.792 184.222 546.261 219.481 581.519C254.739 616.778 326.207 + 606.324 400.5 561.426C474.793 606.324 546.261 616.778 581.519 581.519C616.778 + 546.261 606.324 474.792 561.426 400.5C606.324 326.207 616.778 254.739 581.519 + 219.481ZM148.5 112.5L646.5 112.5L646.5 112.5Q647.384 112.5 648.266 112.543Q649.149 + 112.587 650.029 112.673Q650.908 112.76 651.782 112.89Q652.656 113.019 653.523 113.192Q654.39 + 113.364 655.247 113.579Q656.104 113.794 656.95 114.05Q657.796 114.307 658.628 114.604Q659.46 + 114.902 660.277 115.24Q661.093 115.579 661.892 115.956Q662.691 116.334 663.47 116.751Q664.25 + 117.167 665.008 117.622Q665.766 118.076 666.5 118.567Q667.235 119.058 667.945 119.585Q668.655 + 120.111 669.338 120.672Q670.021 121.232 670.676 121.826Q671.331 122.419 671.956 123.044Q672.581 + 123.669 673.174 124.324Q673.768 124.979 674.328 125.662Q674.889 126.345 675.415 127.055Q675.942 + 127.765 676.433 128.499Q676.924 129.234 677.378 129.992Q677.832 130.75 678.249 131.53Q678.666 132.309 + 679.043 133.108Q679.421 133.907 679.76 134.723Q680.098 135.54 680.396 136.372Q680.693 137.204 + 680.95 138.05Q681.206 138.895 681.421 139.753Q681.636 140.61 681.808 141.477Q681.981 142.344 682.11 + 143.218Q682.24 144.092 682.327 144.971Q682.413 145.851 682.457 146.734Q682.5 147.616 682.5 148.5L682.5 + 646.5L682.5 646.5Q682.5 647.384 682.457 648.266Q682.413 649.149 682.327 650.029Q682.24 650.908 682.11 + 651.782Q681.981 652.656 681.808 653.523Q681.636 654.39 681.421 655.247Q681.206 656.104 680.95 656.95Q680.693 + 657.796 680.396 658.628Q680.098 659.46 679.76 660.277Q679.421 661.093 679.043 661.892Q678.666 662.691 678.249 + 663.47Q677.832 664.25 677.378 665.008Q676.924 665.766 676.433 666.5Q675.942 667.235 675.415 667.945Q674.889 + 668.655 674.328 669.338Q673.768 670.021 673.174 670.676Q672.581 671.331 671.956 671.956Q671.331 672.581 + 670.676 673.174Q670.021 673.768 669.338 674.328Q668.655 674.889 667.945 675.415Q667.235 675.942 666.5 + 676.433Q665.766 676.924 665.008 677.378Q664.25 677.832 663.47 678.249Q662.691 678.666 661.892 679.043Q661.093 + 679.421 660.277 679.76Q659.46 680.098 658.628 680.396Q657.796 680.693 656.95 680.95Q656.104 681.206 655.247 + 681.421Q654.39 681.636 653.523 681.808Q652.656 681.981 651.782 682.11Q650.908 682.24 650.029 682.327Q649.149 + 682.413 648.266 682.457Q647.384 682.5 646.5 682.5L148.5 682.5L148.5 682.5Q147.616 682.5 146.734 682.457Q145.851 + 682.413 144.971 682.327Q144.092 682.24 143.218 682.11Q142.344 681.981 141.477 681.808Q140.61 681.636 139.753 + 681.421Q138.895 681.206 138.05 680.95Q137.204 680.693 136.372 680.396Q135.54 680.098 134.723 679.76Q133.907 + 679.421 133.108 679.043Q132.309 678.666 131.53 678.249Q130.75 677.832 129.992 677.378Q129.234 676.924 128.499 + 676.433Q127.765 675.942 127.055 675.415Q126.345 674.889 125.662 674.328Q124.979 673.768 124.324 673.174Q123.669 + 672.581 123.044 671.956Q122.419 671.331 121.826 670.676Q121.232 670.021 120.672 669.338Q120.111 668.655 119.585 + 667.945Q119.058 667.235 118.567 666.5Q118.076 665.766 117.622 665.008Q117.167 664.25 116.751 663.47Q116.334 662.691 + 115.956 661.892Q115.579 661.093 115.24 660.277Q114.902 659.46 114.604 658.628Q114.307 657.796 114.05 656.95Q113.794 + 656.104 113.579 655.247Q113.364 654.39 113.192 653.523Q113.019 652.656 112.89 651.782Q112.76 650.908 112.673 + 650.029Q112.587 649.149 112.543 648.266Q112.5 647.384 112.5 646.5L112.5 148.5L112.5 148.5Q112.5 147.616 + 112.543 146.734Q112.587 145.851 112.673 144.971Q112.76 144.092 112.89 143.218Q113.019 142.344 113.192 + 141.477Q113.364 140.61 113.579 139.753Q113.794 138.895 114.05 138.05Q114.307 137.204 114.604 136.372Q114.902 + 135.54 115.24 134.723Q115.579 133.907 115.956 133.108Q116.334 132.309 116.751 131.53Q117.167 130.75 117.622 + 129.992Q118.076 129.234 118.567 128.499Q119.058 127.765 119.585 127.055Q120.111 126.345 120.672 125.662Q121.232 + 124.979 121.826 124.324Q122.419 123.669 123.044 123.044Q123.669 122.419 124.324 121.826Q124.979 121.232 125.662 + 120.672Q126.345 120.111 127.055 119.585Q127.765 119.058 128.499 118.567Q129.234 118.076 129.992 117.622Q130.75 + 117.167 131.53 116.751Q132.309 116.334 133.108 115.956Q133.907 115.579 134.723 115.24Q135.54 114.902 + 136.372 114.604Q137.204 114.307 138.05 114.05Q138.895 113.794 139.753 113.579Q140.61 113.364 141.477 + 113.192Q142.344 113.019 143.218 112.89Q144.092 112.76 144.971 112.673Q145.851 112.587 146.734 + 112.543Q147.616 112.5 148.5 112.5Z + """), RenderTransform = new CompositeTransform() { ScaleX = scale.X, @@ -95,7 +147,6 @@ public async Task When_Using_Path() builder.EndFigure(CanvasFigureLoop.Closed); var path = new CompositionPath(CanvasGeometry.CreatePath(builder)); - await RenderPath(path, expected, scale, offset); } } From 2d436c77e12cc1a516ae2a0d3633258f27888960 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Fri, 9 Aug 2024 21:02:00 +0300 Subject: [PATCH 13/31] fix(composition): Fix a crash that can happen when setting CompositionPathGeometry.Path to null --- .../Composition/CompositionPathGeometry.skia.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs index 4d33a253232d..2f645894acdd 100644 --- a/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionPathGeometry.skia.cs @@ -201,7 +201,16 @@ private protected override void OnPropertyChangedCore(string? propertyName, bool if (propertyName is nameof(Path)) { _commands.Clear(); - InternalBuildGeometry(); + + if (Path is not null) + { + InternalBuildGeometry(); + } + else + { + _geometrySource2D?.Dispose(); + _geometrySource2D = null; + } } base.OnPropertyChangedCore(propertyName, isSubPropertyChange); From b6bf6fcfb25706472326740cbcb8f581019cfb5c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 10 Aug 2024 00:22:26 +0300 Subject: [PATCH 14/31] feat(composition): add Composition Path Clipping sample --- .../CompositionPathTests.xaml | 14 ++++++++------ .../CompositionPathTests.xaml.cs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml index bf6013d8215a..6c2ca931f863 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml @@ -9,11 +9,13 @@ d:DesignWidth="400"> - - - - - - + + + + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index d5aa9486ce9e..87f37014f468 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -784,6 +784,25 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) polyVisual.Offset = new(0, 5, 0); ElementCompositionPreview.SetElementChildVisual(compPresenter6, polyVisual); + + // Path Clipping + var surface = Microsoft.UI.Xaml.Media.LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/test_image_200_200.png")); + surface.LoadCompleted += (s, o) => + { + if (o.Status == Microsoft.UI.Xaml.Media.LoadedImageSourceLoadStatus.Success) + { + var imgBrush = compositor.CreateSurfaceBrush(surface); + var imgVisual = compositor.CreateSpriteVisual(); + imgVisual.Size = new(200, 200); + imgVisual.Brush = imgBrush; + imgVisual.Offset = new(0, 5, 0); + + var clip = compositor.CreateGeometricClip(polyGeometry); + imgVisual.Clip = clip; + + ElementCompositionPreview.SetElementChildVisual(compPresenter7, imgVisual); + } + }; } private Vector2[] GetStarPoints(Vector2 center, float radius, int numPoints, float innerRadiusFactor) From 30b0407fd4275a212589a984d975b82024e03f7e Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Tue, 13 Aug 2024 20:41:23 +0300 Subject: [PATCH 15/31] feat(composition): animations support for CompositionGeometry + sample --- .../CompositionPathTests.xaml | 1 + .../CompositionPathTests.xaml.cs | 39 +++++++++++- .../Composition/CompositionGeometry.cs | 63 ++++++++++++++++++- .../CompositionSpriteShape.skia.cs | 18 ++++++ .../BooleanKeyFrameAnimation.cs | 8 ++- .../KeyFrameAnimations/KeyFrameEvaluator.cs | 2 +- .../ScalarKeyFrameAnimation.cs | 8 ++- .../Vector2KeyFrameAnimation.cs | 8 ++- .../Vector3KeyFrameAnimation.cs | 7 ++- .../Vector4KeyFrameAnimation.cs | 7 ++- 10 files changed, 151 insertions(+), 10 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml index 6c2ca931f863..b868bed05195 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml @@ -17,5 +17,6 @@ + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index 87f37014f468..1e5187103257 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -34,7 +34,7 @@ public CompositionPathTests() private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) { - var compositor = Microsoft.UI.Xaml.Window.Current.Compositor; + var compositor = Compositor.GetSharedCompositor(); var device = CanvasDevice.GetSharedDevice(); // Simple shape @@ -803,6 +803,43 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) ElementCompositionPreview.SetElementChildVisual(compPresenter7, imgVisual); } }; + + // Trim Animation + var polyVisual2 = compositor.CreateShapeVisual(); + var polyGeometry2 = compositor.CreatePathGeometry(polyPath); + var polyShape2 = compositor.CreateSpriteShape(polyGeometry2); + + polyShape2.StrokeBrush = compositor.CreateColorBrush(Windows.UI.Colors.PaleVioletRed); + polyShape2.StrokeDashCap = CompositionStrokeCap.Round; + polyShape2.StrokeEndCap = CompositionStrokeCap.Round; + polyShape2.StrokeStartCap = CompositionStrokeCap.Round; + polyShape2.StrokeMiterLimit = 4; + polyShape2.StrokeThickness = 2; + + polyVisual2.Shapes.Add(polyShape2); + polyVisual2.Size = new(200); + polyVisual2.Offset = new(0, 5, 0); + + polyGeometry2.TrimEnd = 1f; + + var duration = TimeSpan.FromSeconds(2); + + var trimStartAnimation = compositor.CreateScalarKeyFrameAnimation(); + trimStartAnimation.InsertKeyFrame(0, 0); + trimStartAnimation.InsertKeyFrame(1, 1); + trimStartAnimation.Duration = duration; + trimStartAnimation.IterationBehavior = AnimationIterationBehavior.Forever; + + var trimEndAnimation = compositor.CreateScalarKeyFrameAnimation(); + trimEndAnimation.InsertKeyFrame(0, 0); + trimEndAnimation.InsertKeyFrame(1, 1.25f); + trimEndAnimation.Duration = duration; + trimEndAnimation.IterationBehavior = AnimationIterationBehavior.Forever; + + polyGeometry2.StartAnimation(nameof(CompositionGeometry.TrimStart), trimStartAnimation); + polyGeometry2.StartAnimation(nameof(CompositionGeometry.TrimEnd), trimEndAnimation); + + ElementCompositionPreview.SetElementChildVisual(compPresenter8, polyVisual2); } private Vector2[] GetStarPoints(Vector2 center, float radius, int numPoints, float innerRadiusFactor) diff --git a/src/Uno.UI.Composition/Composition/CompositionGeometry.cs b/src/Uno.UI.Composition/Composition/CompositionGeometry.cs index d5fbe1720ab8..d10da90a6df0 100644 --- a/src/Uno.UI.Composition/Composition/CompositionGeometry.cs +++ b/src/Uno.UI.Composition/Composition/CompositionGeometry.cs @@ -1,22 +1,79 @@ #nullable enable +using System; using Windows.Graphics; namespace Microsoft.UI.Composition { public partial class CompositionGeometry : CompositionObject { + private float _trimStart; + private float _trimOffset; + private float _trimEnd; + internal CompositionGeometry(Compositor compositor) : base(compositor) { } - public float TrimStart { get; set; } + public float TrimStart + { + get => _trimStart; + set => SetProperty(ref _trimStart, value); + } - public float TrimOffset { get; set; } + public float TrimOffset + { + get => _trimOffset; + set => SetProperty(ref _trimOffset, value); + } - public float TrimEnd { get; set; } + public float TrimEnd + { + get => _trimEnd; + set => SetProperty(ref _trimEnd, value); + } internal virtual IGeometrySource2D? BuildGeometry() => null; + + internal override object GetAnimatableProperty(string propertyName, string subPropertyName) + { + if (propertyName.Equals(nameof(TrimStart), StringComparison.OrdinalIgnoreCase)) + { + return TrimStart; + } + else if (propertyName.Equals(nameof(TrimOffset), StringComparison.OrdinalIgnoreCase)) + { + return TrimOffset; + } + else if (propertyName.Equals(nameof(TrimEnd), StringComparison.OrdinalIgnoreCase)) + { + return TrimEnd; + } + else + { + return base.GetAnimatableProperty(propertyName, subPropertyName); + } + } + + private protected override void SetAnimatableProperty(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, object? propertyValue) + { + if (propertyName.Equals(nameof(TrimStart), StringComparison.OrdinalIgnoreCase)) + { + TrimStart = SubPropertyHelpers.ValidateValue(propertyValue); + } + else if (propertyName.Equals(nameof(TrimOffset), StringComparison.OrdinalIgnoreCase)) + { + TrimOffset = SubPropertyHelpers.ValidateValue(propertyValue); + } + else if (propertyName.Equals(nameof(TrimEnd), StringComparison.OrdinalIgnoreCase)) + { + TrimEnd = SubPropertyHelpers.ValidateValue(propertyValue); + } + else + { + base.SetAnimatableProperty(propertyName, subPropertyName, propertyValue); + } + } } } diff --git a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs index 81cdb203a298..a26c229af751 100644 --- a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs @@ -35,6 +35,11 @@ internal override void Paint(in Visual.PaintingSession session) fill = wrapper.WrappedBrush; } + if (Geometry.TrimStart != default || Geometry.TrimEnd != default) + { + fillPaint.PathEffect = SKPathEffect.CreateTrim(Geometry.TrimStart, Geometry.TrimEnd); + } + if (fill is CompositionEffectBrush { HasBackdropBrushInput: true }) { // workaround until SkiaSharp adds support for SaveLayerRec @@ -62,6 +67,19 @@ internal override void Paint(in Visual.PaintingSession session) strokePaint.PathEffect = SKPathEffect.CreateDash(strokeDashArray.ToEvenArray(), 0); } + if (Geometry.TrimStart != default || Geometry.TrimEnd != default) + { + var pathEffect = SKPathEffect.CreateTrim(Geometry.TrimStart, Geometry.TrimEnd); + if (strokePaint.PathEffect is SKPathEffect effect) + { + strokePaint.PathEffect = SKPathEffect.CreateSum(effect, pathEffect); + } + else + { + strokePaint.PathEffect = pathEffect; + } + } + // Generate stroke geometry for bounds that will be passed to a brush. // - [Future]: This generated geometry should also be used for hit testing. diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs index 04994361694b..7879bb5c00a8 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Microsoft.UI.Composition; @@ -26,8 +27,13 @@ public void InsertKeyFrame(float normalizedProgressKey, bool value) startValue = (bool)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } + Func lerp = (value1, value2, _) => value1; - _keyframeEvaluator = new KeyFrameEvaluator(startValue, _keyFrames[1.0f], Duration, _keyFrames, lerp, IterationCount, IterationBehavior, Compositor); + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, lerp, IterationCount, IterationBehavior, Compositor); return startValue; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs index e78089733997..b073d88d7220 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs @@ -47,7 +47,7 @@ public KeyFrameEvaluator( elapsed = TimeSpan.FromTicks(elapsed.Ticks % _duration.Ticks); var currentFrame = (float)elapsed.Ticks / _duration.Ticks; - var nextKeyFrame = _keyFrames.Keys.First(k => k >= currentFrame); + var nextKeyFrame = _keyFrames.Keys.FirstOrDefault(k => k >= currentFrame, _keyFrames.Keys.Last()); if (nextKeyFrame == currentFrame) { // currentFrame is one that exists in the dictionary already. diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs index a8930b6e017f..dc8c0e4398b1 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using Uno; namespace Microsoft.UI.Composition; @@ -36,7 +37,12 @@ public void InsertKeyFrame(float normalizedProgressKey, float value, Composition startValue = (float)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, _keyFrames[1.0f], Duration, _keyFrames, float.Lerp, IterationCount, IterationBehavior, Compositor); + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, float.Lerp, IterationCount, IterationBehavior, Compositor); return startValue; } #endif diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs index b2cae1fc7ffb..1cc4a6994c2a 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; namespace Microsoft.UI.Composition; @@ -27,7 +28,12 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector2 value) startValue = (Vector2)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, _keyFrames[1.0f], Duration, _keyFrames, Vector2.Lerp, IterationCount, IterationBehavior, Compositor); + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector2.Lerp, IterationCount, IterationBehavior, Compositor); return startValue; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs index 6507323cc3d6..bff93e612f80 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs @@ -29,7 +29,12 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector3 value) startValue = (Vector3)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, _keyFrames[1.0f], Duration, _keyFrames, Vector3.Lerp, IterationCount, IterationBehavior, Compositor); + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector3.Lerp, IterationCount, IterationBehavior, Compositor); return startValue; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs index 7945d41e8f85..f280e56d69f3 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs @@ -29,7 +29,12 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector4 value) startValue = (Vector4)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, _keyFrames[1.0f], Duration, _keyFrames, Vector4.Lerp, IterationCount, IterationBehavior, Compositor); + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector4.Lerp, IterationCount, IterationBehavior, Compositor); return startValue; } } From d3854500eaf984277dc20c226f43100d20a0b53d Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Wed, 14 Aug 2024 01:25:49 +0300 Subject: [PATCH 16/31] fix(composition): fix keyframe animation locking on animating sub-properties of visuals --- .../CompositionPathTests.xaml.cs | 3 ++ .../CompositionSpriteShape.skia.cs | 8 ++--- .../Composition/CompositionVisualSurface.cs | 32 +++++++++++++++++++ .../ScalarKeyFrameAnimation.cs | 10 +++--- src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs | 2 +- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs index 1e5187103257..3c65e9e8f16c 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/CompositionPathTests.xaml.cs @@ -18,6 +18,7 @@ using Microsoft.Graphics.Canvas.Geometry; using Microsoft.Graphics.Canvas; using System.Numerics; +using System.Threading.Tasks; // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 @@ -822,6 +823,8 @@ private void CompositionPathTests_Loaded(object sender, RoutedEventArgs e) polyGeometry2.TrimEnd = 1f; + //await Task.Run(async () => await Task.Delay(2000)); + var duration = TimeSpan.FromSeconds(2); var trimStartAnimation = compositor.CreateScalarKeyFrameAnimation(); diff --git a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs index a26c229af751..9df5be222cd0 100644 --- a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs @@ -72,12 +72,10 @@ internal override void Paint(in Visual.PaintingSession session) var pathEffect = SKPathEffect.CreateTrim(Geometry.TrimStart, Geometry.TrimEnd); if (strokePaint.PathEffect is SKPathEffect effect) { - strokePaint.PathEffect = SKPathEffect.CreateSum(effect, pathEffect); - } - else - { - strokePaint.PathEffect = pathEffect; + pathEffect = SKPathEffect.CreateSum(effect, pathEffect); } + + strokePaint.PathEffect = pathEffect; } // Generate stroke geometry for bounds that will be passed to a brush. diff --git a/src/Uno.UI.Composition/Composition/CompositionVisualSurface.cs b/src/Uno.UI.Composition/Composition/CompositionVisualSurface.cs index 727ea4e112d0..33a085c324d8 100644 --- a/src/Uno.UI.Composition/Composition/CompositionVisualSurface.cs +++ b/src/Uno.UI.Composition/Composition/CompositionVisualSurface.cs @@ -62,5 +62,37 @@ public Vector2 SourceSize get => _sourceSize; set => SetProperty(ref _sourceSize, value); } + + internal override object GetAnimatableProperty(string propertyName, string subPropertyName) + { + if (propertyName.Equals(nameof(SourceOffset), StringComparison.OrdinalIgnoreCase)) + { + return SubPropertyHelpers.GetVector2(subPropertyName, SourceOffset); + } + else if (propertyName.Equals(nameof(SourceSize), StringComparison.OrdinalIgnoreCase)) + { + return SubPropertyHelpers.GetVector2(subPropertyName, SourceSize); + } + else + { + return base.GetAnimatableProperty(propertyName, subPropertyName); + } + } + + private protected override void SetAnimatableProperty(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, object? propertyValue) + { + if (propertyName.Equals(nameof(SourceOffset), StringComparison.OrdinalIgnoreCase)) + { + SourceOffset = SubPropertyHelpers.ValidateValue(propertyValue); + } + else if (propertyName.Equals(nameof(SourceSize), StringComparison.OrdinalIgnoreCase)) + { + SourceSize = SubPropertyHelpers.ValidateValue(propertyValue); + } + else + { + base.SetAnimatableProperty(propertyName, subPropertyName, propertyValue); + } + } } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs index dc8c0e4398b1..416bcfb73cb2 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs @@ -37,12 +37,12 @@ public void InsertKeyFrame(float normalizedProgressKey, float value, Composition startValue = (float)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); } - if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) - { - finalValue = _keyFrames.Values.LastOrDefault(startValue); - } + if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) + { + finalValue = _keyFrames.Values.LastOrDefault(startValue); + } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, float.Lerp, IterationCount, IterationBehavior, Compositor); + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, float.Lerp, IterationCount, IterationBehavior, Compositor); return startValue; } #endif diff --git a/src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs b/src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs index 45a2543e3a30..fa0d841ee016 100644 --- a/src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs +++ b/src/Uno.UI.Runtime.Skia.Wpf/WpfHost.cs @@ -82,7 +82,7 @@ protected override Task RunLoop() private void InitializeDispatcher() { - Windows.UI.Core.CoreDispatcher.DispatchOverride = (d, p) => _dispatcher.BeginInvoke(d, p == Uno.UI.Dispatching.NativeDispatcherPriority.Idle ? DispatcherPriority.SystemIdle : DispatcherPriority.Normal); + Windows.UI.Core.CoreDispatcher.DispatchOverride = (d, p) => _dispatcher.BeginInvoke(d, p == Uno.UI.Dispatching.NativeDispatcherPriority.Idle ? DispatcherPriority.SystemIdle : DispatcherPriority.Render); Windows.UI.Core.CoreDispatcher.HasThreadAccessOverride = _dispatcher.CheckAccess; } From 7c12cc44a1a792ca7e1ec5e8e6ace20e93bc3394 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Wed, 14 Aug 2024 21:16:45 +0300 Subject: [PATCH 17/31] feat(composition): initial implementation of AnimationController --- .../Composition/AnimationController.cs | 70 +++++++++++++++++++ .../Composition/CompositionObject.cs | 60 ++++++++++++++++ .../Composition/KeyFrameAnimation.cs | 25 +++++++ .../KeyFrameAnimations/IKeyFrameEvaluator.cs | 5 ++ .../KeyFrameAnimations/KeyFrameEvaluator.cs | 61 +++++++++++++++- .../AnimationController.cs | 40 ++--------- 6 files changed, 225 insertions(+), 36 deletions(-) create mode 100644 src/Uno.UI.Composition/Composition/AnimationController.cs diff --git a/src/Uno.UI.Composition/Composition/AnimationController.cs b/src/Uno.UI.Composition/Composition/AnimationController.cs new file mode 100644 index 000000000000..ae1a6fcfe74b --- /dev/null +++ b/src/Uno.UI.Composition/Composition/AnimationController.cs @@ -0,0 +1,70 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class AnimationController +{ + private readonly CompositionObject _ownerObject; + private readonly string _propertyName; + private KeyFrameAnimation? _animation; + + private float? _progress; + + internal AnimationController(CompositionObject ownerObject, string propertyName, KeyFrameAnimation animation) : base(ownerObject.Compositor) + { + _ownerObject = ownerObject; + _propertyName = propertyName; + _animation = animation; + + _animation.Stopped += Animation_Stopped; + } + + public void Resume() + { + _progress = null; + EnsureAnimation().Resume(); + } + + public void Pause() + { + EnsureAnimation().Pause(); + } + + public float Progress + { + get => _progress is not null ? _progress.Value : EnsureAnimation().Progress; + set + { + var animation = EnsureAnimation(); + _progress = value; + _ownerObject.SeekAnimation(animation, value); + } + } + + private void Animation_Stopped(object? sender, EventArgs e) + { + _animation = null; + _progress = null; + } + + private KeyFrameAnimation EnsureAnimation() + { + if (_animation == null) + { + _animation = _ownerObject.GetKeyFrameAnimation(_propertyName); + } + + if (_animation == null) + { + throw new InvalidOperationException($"No animation is running on the target object for property {_propertyName}"); + } + + return _animation; + } +} diff --git a/src/Uno.UI.Composition/Composition/CompositionObject.cs b/src/Uno.UI.Composition/Composition/CompositionObject.cs index 4c59be5c6f64..b22a1f3083b7 100644 --- a/src/Uno.UI.Composition/Composition/CompositionObject.cs +++ b/src/Uno.UI.Composition/Composition/CompositionObject.cs @@ -135,6 +135,37 @@ private void ReEvaluateAnimation(CompositionAnimation animation) } } + private void ReEvaluateAnimation(KeyFrameAnimation animation, float progress) + { + if (_animations == null) + { + return; + } + + foreach (var (key, value) in _animations) + { + if (value == animation) + { + var propertyName = key; + ReadOnlySpan firstPropertyName; + ReadOnlySpan subPropertyName; + var firstDotIndex = propertyName.IndexOf('.'); + if (firstDotIndex > -1) + { + firstPropertyName = propertyName.AsSpan().Slice(0, firstDotIndex); + subPropertyName = propertyName.AsSpan().Slice(firstDotIndex + 1); + } + else + { + firstPropertyName = propertyName; + subPropertyName = default; + } + + this.SetAnimatableProperty(firstPropertyName, subPropertyName, animation.Evaluate(progress)); + } + } + } + public void StopAnimation(string propertyName) { if (_animations?.TryGetValue(propertyName, out var animation) == true) @@ -145,6 +176,35 @@ public void StopAnimation(string propertyName) } } + // AnimationController only supports KeyFrameAnimations + internal void PauseAnimation(KeyFrameAnimation animation) + { + animation.AnimationFrame -= ReEvaluateAnimation; + animation.Pause(); + } + + internal void ResumeAnimation(KeyFrameAnimation animation) + { + animation.Resume(); + animation.AnimationFrame += ReEvaluateAnimation; + } + + internal void SeekAnimation(KeyFrameAnimation animation, float progress) + { + PauseAnimation(animation); + ReEvaluateAnimation(animation, progress); + } + + internal KeyFrameAnimation? GetKeyFrameAnimation(string propertyName) + { + if (_animations?.TryGetValue(propertyName, out var animation) == true && animation is KeyFrameAnimation keyFrameAnimation) + { + return keyFrameAnimation; + } + + return null; + } + public void Dispose() => DisposeInternal(); private protected virtual void DisposeInternal() diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimation.cs index 4a5ebcdc4c68..0dbae7e14d63 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimation.cs @@ -38,6 +38,8 @@ internal KeyFrameAnimation(Compositor compositor) : base(compositor) [NotImplemented] public AnimationDirection Direction { get; set; } + internal event EventHandler? Stopped; + internal override object Evaluate() { var (value, shouldStop) = _keyframeEvaluator!.Evaluate(); @@ -48,5 +50,28 @@ internal override object Evaluate() return value; } + + internal object Evaluate(float progress) + { + return _keyframeEvaluator!.Evaluate(progress); + } + + internal override void Stop() + { + base.Stop(); + Stopped?.Invoke(this, EventArgs.Empty); + } + + internal void Pause() + { + _keyframeEvaluator!.Pause(); + } + + internal void Resume() + { + _keyframeEvaluator!.Resume(); + } + + internal float Progress => _keyframeEvaluator!.Progress; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/IKeyFrameEvaluator.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/IKeyFrameEvaluator.cs index 664e4a22a1e6..2c0ad4481573 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/IKeyFrameEvaluator.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/IKeyFrameEvaluator.cs @@ -3,4 +3,9 @@ internal interface IKeyFrameEvaluator { (object Value, bool ShouldStop) Evaluate(); + object Evaluate(float progress); + void Pause(); + void Resume(); + + float Progress { get; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs index b073d88d7220..fbdb1a6a991a 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs @@ -16,6 +16,9 @@ internal sealed class KeyFrameEvaluator : IKeyFrameEvaluator private readonly Func _lerp; private readonly Compositor _compositor; + private long? _pauseTimestamp; + private long _totalPause; + public KeyFrameEvaluator( T initialValue, T finalValue, @@ -38,7 +41,7 @@ public KeyFrameEvaluator( public (object Value, bool ShouldStop) Evaluate() { - var elapsed = new TimeSpan(_compositor.TimestampInTicks - _startTimestamp); + var elapsed = new TimeSpan(_compositor.TimestampInTicks - _totalPause - _startTimestamp); if (elapsed >= _totalDuration) { return (_finalValue, true); @@ -46,7 +49,21 @@ public KeyFrameEvaluator( elapsed = TimeSpan.FromTicks(elapsed.Ticks % _duration.Ticks); - var currentFrame = (float)elapsed.Ticks / _duration.Ticks; + return EvaluateInternal((float)elapsed.Ticks / _duration.Ticks); + } + + public object Evaluate(float progress) + { + if (progress >= 1.0f) + { + return (_finalValue, true); + } + + return EvaluateInternal(progress).Value; + } + + private (object Value, bool ShouldStop) EvaluateInternal(float currentFrame) + { var nextKeyFrame = _keyFrames.Keys.FirstOrDefault(k => k >= currentFrame, _keyFrames.Keys.Last()); if (nextKeyFrame == currentFrame) { @@ -60,4 +77,44 @@ public KeyFrameEvaluator( var newValue = _lerp(previousValue, nextValue, (currentFrame - previousKeyFrame) / (nextKeyFrame - previousKeyFrame)); return (newValue, false); } + + + public void Pause() + { + if (_pauseTimestamp is not null) + { + return; + } + + _pauseTimestamp = _compositor.TimestampInTicks; + } + + public void Resume() + { + if (_pauseTimestamp is null) + { + return; + } + + _totalPause += (_compositor.TimestampInTicks - _pauseTimestamp.Value); + _pauseTimestamp = null; + } + + public float Progress + { + get + { + long timestamp = _pauseTimestamp is null ? _compositor.TimestampInTicks - _totalPause : _pauseTimestamp.Value; + + var elapsed = new TimeSpan(timestamp - _startTimestamp); + if (elapsed >= _totalDuration) + { + return 1.0f; + } + + elapsed = TimeSpan.FromTicks(elapsed.Ticks % _duration.Ticks); + + return (float)elapsed.Ticks / _duration.Ticks; + } + } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/AnimationController.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/AnimationController.cs index 416766da0c66..cdcbd7730d85 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/AnimationController.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/AnimationController.cs @@ -3,16 +3,11 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class AnimationController : global::Microsoft.UI.Composition.CompositionObject { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal AnimationController() - { - } -#endif #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Microsoft.UI.Composition.AnimationControllerProgressBehavior ProgressBehavior @@ -27,20 +22,7 @@ internal AnimationController() } } #endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Progress - { - get - { - throw new global::System.NotImplementedException("The member float AnimationController.Progress is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20AnimationController.Progress"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.AnimationController", "float AnimationController.Progress"); - } - } -#endif + #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public float PlaybackRate @@ -76,26 +58,16 @@ public static float MinPlaybackRate } #endif // Forced skipping of method Microsoft.UI.Composition.AnimationController.ProgressBehavior.get -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void Resume() - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.AnimationController", "void AnimationController.Resume()"); - } -#endif // Forced skipping of method Microsoft.UI.Composition.AnimationController.PlaybackRate.set // Forced skipping of method Microsoft.UI.Composition.AnimationController.PlaybackRate.get // Forced skipping of method Microsoft.UI.Composition.AnimationController.Progress.get // Forced skipping of method Microsoft.UI.Composition.AnimationController.Progress.set // Forced skipping of method Microsoft.UI.Composition.AnimationController.ProgressBehavior.set -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void Pause() - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.AnimationController", "void AnimationController.Pause()"); - } -#endif // Forced skipping of method Microsoft.UI.Composition.AnimationController.MaxPlaybackRate.get // Forced skipping of method Microsoft.UI.Composition.AnimationController.MinPlaybackRate.get + + // Skipping already declared property Progress + // Skipping already declared method Microsoft.UI.Composition.AnimationController.Pause() + // Skipping already declared method Microsoft.UI.Composition.AnimationController.Resume() } } From d50c9341b27804e75b238b6c0346328948af605a Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Wed, 14 Aug 2024 21:45:27 +0300 Subject: [PATCH 18/31] feat(composition): implement property animator for AnimationController --- .../Composition/AnimationController.cs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Uno.UI.Composition/Composition/AnimationController.cs b/src/Uno.UI.Composition/Composition/AnimationController.cs index ae1a6fcfe74b..9cad1ba91094 100644 --- a/src/Uno.UI.Composition/Composition/AnimationController.cs +++ b/src/Uno.UI.Composition/Composition/AnimationController.cs @@ -31,10 +31,7 @@ public void Resume() EnsureAnimation().Resume(); } - public void Pause() - { - EnsureAnimation().Pause(); - } + public void Pause() => EnsureAnimation().Pause(); public float Progress { @@ -43,10 +40,35 @@ public float Progress { var animation = EnsureAnimation(); _progress = value; + OnPropertyChanged(nameof(Progress), false); _ownerObject.SeekAnimation(animation, value); } } + internal override object GetAnimatableProperty(string propertyName, string subPropertyName) + { + if (propertyName.Equals(nameof(Progress), StringComparison.OrdinalIgnoreCase)) + { + return Progress; + } + else + { + return base.GetAnimatableProperty(propertyName, subPropertyName); + } + } + + private protected override void SetAnimatableProperty(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, object? propertyValue) + { + if (propertyName.Equals(nameof(Progress), StringComparison.OrdinalIgnoreCase)) + { + Progress = SubPropertyHelpers.ValidateValue(propertyValue); + } + else + { + base.SetAnimatableProperty(propertyName, subPropertyName, propertyValue); + } + } + private void Animation_Stopped(object? sender, EventArgs e) { _animation = null; From 4625b9765b3e7ca6d8602e3e602a46dff3dff18d Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Wed, 14 Aug 2024 22:32:09 +0300 Subject: [PATCH 19/31] feat(composition): more work on AnimationController --- .../Composition/AnimationController.cs | 30 +++++++++++++++++-- .../Composition/CompositionObject.cs | 23 ++++++++++++++ .../CompositionObject.cs | 16 ++-------- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/Uno.UI.Composition/Composition/AnimationController.cs b/src/Uno.UI.Composition/Composition/AnimationController.cs index 9cad1ba91094..fd138bd14296 100644 --- a/src/Uno.UI.Composition/Composition/AnimationController.cs +++ b/src/Uno.UI.Composition/Composition/AnimationController.cs @@ -10,12 +10,14 @@ namespace Microsoft.UI.Composition; public partial class AnimationController { - private readonly CompositionObject _ownerObject; - private readonly string _propertyName; + private CompositionObject? _ownerObject; + private string? _propertyName; private KeyFrameAnimation? _animation; private float? _progress; + // TODO: Support multiple KeyFrameAnimation association like on Windows + internal AnimationController(CompositionObject ownerObject, string propertyName, KeyFrameAnimation animation) : base(ownerObject.Compositor) { _ownerObject = ownerObject; @@ -25,6 +27,23 @@ internal AnimationController(CompositionObject ownerObject, string propertyName, _animation.Stopped += Animation_Stopped; } + internal AnimationController(Compositor compositor) : base(compositor) { } + + internal void Initialize(CompositionObject ownerObject, string propertyName, KeyFrameAnimation animation) + { + if (_animation is not null) + { + _animation.Stopped -= Animation_Stopped; + } + + _ownerObject = ownerObject; + _propertyName = propertyName; + _animation = animation; + _progress = null; + + _animation.Stopped += Animation_Stopped; + } + public void Resume() { _progress = null; @@ -41,7 +60,7 @@ public float Progress var animation = EnsureAnimation(); _progress = value; OnPropertyChanged(nameof(Progress), false); - _ownerObject.SeekAnimation(animation, value); + _ownerObject?.SeekAnimation(animation, value); } } @@ -77,6 +96,11 @@ private void Animation_Stopped(object? sender, EventArgs e) private KeyFrameAnimation EnsureAnimation() { + if (_ownerObject is null || _propertyName is null) + { + throw new InvalidOperationException("The AnimationController has not been associated with a target object or animation"); + } + if (_animation == null) { _animation = _ownerObject.GetKeyFrameAnimation(_propertyName); diff --git a/src/Uno.UI.Composition/Composition/CompositionObject.cs b/src/Uno.UI.Composition/Composition/CompositionObject.cs index b22a1f3083b7..5069d10163e3 100644 --- a/src/Uno.UI.Composition/Composition/CompositionObject.cs +++ b/src/Uno.UI.Composition/Composition/CompositionObject.cs @@ -56,6 +56,8 @@ internal virtual object GetAnimatableProperty(string propertyName, string subPro private protected virtual void SetAnimatableProperty(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, object? propertyValue) => TryUpdateFromProperties(Properties, propertyName, subPropertyName, propertyValue); + // TODO: Clone the animation object to support scenarios where the same animation is used on multiple objects + public void StartAnimation(string propertyName, CompositionAnimation animation) { #if __IOS__ @@ -104,6 +106,16 @@ public void StartAnimation(string propertyName, CompositionAnimation animation) } } + public void StartAnimation(string propertyName, CompositionAnimation animation, AnimationController animationController) + { + StartAnimation(propertyName, animation); + + if (animation is KeyFrameAnimation keyFrameAnimation) + { + animationController.Initialize(this, propertyName, keyFrameAnimation); + } + } + private void ReEvaluateAnimation(CompositionAnimation animation) { if (_animations == null) @@ -176,6 +188,17 @@ public void StopAnimation(string propertyName) } } + public AnimationController? TryGetAnimationController(string propertyName) + { + if (_animations?.TryGetValue(propertyName, out var animation) == true && animation is KeyFrameAnimation keyFrameAnimation) + { + return new(this, propertyName, keyFrameAnimation); + } + + // TODO: verify if returning null is the correct behavior + return null; + } + // AnimationController only supports KeyFrameAnimations internal void PauseAnimation(KeyFrameAnimation animation) { diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionObject.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionObject.cs index 3218afd4b594..0582b08213f5 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionObject.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionObject.cs @@ -46,7 +46,9 @@ public void PopulatePropertyInfo(string propertyName, global::Microsoft.UI.Compo #endif // Forced skipping of method Microsoft.UI.Composition.CompositionObject.Properties.get // Skipping already declared method Microsoft.UI.Composition.CompositionObject.StartAnimation(string, Microsoft.UI.Composition.CompositionAnimation) + // Skipping already declared method Microsoft.UI.Composition.CompositionObject.StartAnimation(string, Microsoft.UI.Composition.CompositionAnimation, Microsoft.UI.Composition.AnimationController) // Skipping already declared method Microsoft.UI.Composition.CompositionObject.StopAnimation(string) + // Skipping already declared method Microsoft.UI.Composition.CompositionObject.TryGetAnimationController(string) // Forced skipping of method Microsoft.UI.Composition.CompositionObject.Comment.get // Forced skipping of method Microsoft.UI.Composition.CompositionObject.Compositor.get // Forced skipping of method Microsoft.UI.Composition.CompositionObject.ImplicitAnimations.get @@ -66,20 +68,6 @@ public void StopAnimationGroup(global::Microsoft.UI.Composition.ICompositionAnim } #endif // Forced skipping of method Microsoft.UI.Composition.CompositionObject.DispatcherQueue.get -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.AnimationController TryGetAnimationController(string propertyName) - { - throw new global::System.NotImplementedException("The member AnimationController CompositionObject.TryGetAnimationController(string propertyName) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=AnimationController%20CompositionObject.TryGetAnimationController%28string%20propertyName%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void StartAnimation(string propertyName, global::Microsoft.UI.Composition.CompositionAnimation animation, global::Microsoft.UI.Composition.AnimationController animationController) - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.CompositionObject", "void CompositionObject.StartAnimation(string propertyName, CompositionAnimation animation, AnimationController animationController)"); - } -#endif #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public static void StartAnimationWithIAnimationObject(global::Microsoft.UI.Composition.IAnimationObject target, string propertyName, global::Microsoft.UI.Composition.CompositionAnimation animation) From 1a094f9e4a27d12a901264c39e599c132b7bd506 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Mon, 9 Sep 2024 06:33:14 +0300 Subject: [PATCH 20/31] feat(composition): initial support for Composition easing functions --- .../Composition/Compositor.cs | 9 +++++ .../KeyFrameAnimations/AnimationFrame.cs | 13 +++++++ .../BooleanKeyFrameAnimation.cs | 13 ++++--- .../CompositionEasingFunction.cs | 20 +++++++++++ .../CubicBezierEasingFunction.cs | 36 +++++++++++++++++++ .../KeyFrameAnimations/KeyFrameEvaluator.cs | 22 ++++++------ .../ScalarKeyFrameAnimation.cs | 23 ++++++++---- .../Vector2KeyFrameAnimation.cs | 22 +++++++++--- .../Vector3KeyFrameAnimation.cs | 22 +++++++++--- .../Vector4KeyFrameAnimation.cs | 22 +++++++++--- .../CompositionEasingFunction.cs | 16 ++------- .../Microsoft.UI.Composition/Compositor.cs | 8 +---- .../CubicBezierEasingFunction.cs | 29 ++------------- .../Vector2KeyFrameAnimation.cs | 8 +---- .../Vector3KeyFrameAnimation.cs | 8 +---- .../Vector4KeyFrameAnimation.cs | 8 +---- 16 files changed, 174 insertions(+), 105 deletions(-) create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/AnimationFrame.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/CubicBezierEasingFunction.cs diff --git a/src/Uno.UI.Composition/Composition/Compositor.cs b/src/Uno.UI.Composition/Composition/Compositor.cs index 852dba0747d3..a10d720c450e 100644 --- a/src/Uno.UI.Composition/Composition/Compositor.cs +++ b/src/Uno.UI.Composition/Composition/Compositor.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Numerics; +using System.Runtime.CompilerServices; using Windows.Graphics.Effects; using Windows.UI; @@ -13,6 +14,8 @@ public partial class Compositor : global::System.IDisposable { private static Lazy _sharedCompositorLazy = new(() => new()); + private static Lazy _defaultEasingFunction = new(() => new CubicBezierEasingFunction(GetSharedCompositor(), new(0.41f, 0.52f), new(0.0f, 0.94f))); + public Compositor() { } @@ -27,6 +30,9 @@ public Compositor() internal static Compositor GetSharedCompositor() => _sharedCompositorLazy.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static CompositionEasingFunction GetDefaultEasingFunction() => _defaultEasingFunction.Value; + public ContainerVisual CreateContainerVisual() => new ContainerVisual(this); @@ -199,6 +205,9 @@ public CompositionEffectFactory CreateEffectFactory(IGraphicsEffect graphicsEffe public CompositionEffectFactory CreateEffectFactory(IGraphicsEffect graphicsEffect, IEnumerable animatableProperties) => new CompositionEffectFactory(this, graphicsEffect, animatableProperties); + public CubicBezierEasingFunction CreateCubicBezierEasingFunction(Vector2 controlPoint1, Vector2 controlPoint2) + => new(this, controlPoint1, controlPoint2); + partial void InvalidateRenderPartial(Visual visual); } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/AnimationFrame.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/AnimationFrame.cs new file mode 100644 index 000000000000..e51a3412c88f --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/AnimationFrame.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +internal struct AnimationKeyFrame +{ + public T Value; + public CompositionEasingFunction EasingFunction; +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs index 7879bb5c00a8..ee63d8b34d61 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BooleanKeyFrameAnimation.cs @@ -8,7 +8,7 @@ namespace Microsoft.UI.Composition; public partial class BooleanKeyFrameAnimation : KeyFrameAnimation { - private readonly SortedDictionary _keyFrames = new(); + private readonly SortedDictionary> _keyFrames = new(); internal BooleanKeyFrameAnimation(Compositor compositor) : base(compositor) { @@ -17,14 +17,17 @@ internal BooleanKeyFrameAnimation(Compositor compositor) : base(compositor) private protected override int KeyFrameCountCore => _keyFrames.Count; public void InsertKeyFrame(float normalizedProgressKey, bool value) - => _keyFrames[normalizedProgressKey] = value; + => _keyFrames[normalizedProgressKey] = new() { Value = value }; internal override object? Start(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, CompositionObject compositionObject) { base.Start(propertyName, subPropertyName, compositionObject); if (!_keyFrames.TryGetValue(0, out var startValue)) { - startValue = (bool)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); + startValue = new() + { + Value = (bool)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()) + }; } if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) @@ -32,8 +35,8 @@ public void InsertKeyFrame(float normalizedProgressKey, bool value) finalValue = _keyFrames.Values.LastOrDefault(startValue); } - Func lerp = (value1, value2, _) => value1; + Func, AnimationKeyFrame, float, bool> lerp = (value1, value2, _) => value1.Value; _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, lerp, IterationCount, IterationBehavior, Compositor); - return startValue; + return startValue.Value; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs new file mode 100644 index 000000000000..f554f8f0ec47 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class CompositionEasingFunction +{ + internal CompositionEasingFunction() { } + + internal CompositionEasingFunction(Compositor owner) : base(owner) { } + + public static CubicBezierEasingFunction CreateCubicBezierEasingFunction(Compositor owner, Vector2 controlPoint1, Vector2 controlPoint2) + => new(owner, controlPoint1, controlPoint2); + + internal virtual float Ease(float t) => t; +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CubicBezierEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CubicBezierEasingFunction.cs new file mode 100644 index 000000000000..74968073b07f --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CubicBezierEasingFunction.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class CubicBezierEasingFunction +{ + public Vector2 ControlPoint1 { get; } + public Vector2 ControlPoint2 { get; } + + internal CubicBezierEasingFunction(Compositor owner, Vector2 controlPoint1, Vector2 controlPoint2) : base(owner) + { + ControlPoint1 = controlPoint1; + ControlPoint2 = controlPoint2; + } + + internal override float Ease(float t) + => EaseInternal(Vector2.Zero, ControlPoint1, ControlPoint2, Vector2.One, t).Y; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector2 EaseInternal(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + { + var x = 1 - t; + + return + x * x * x * p0 + + 3 * x * x * t * p1 + + 3 * x * t * t * p2 + + t * t * t * p3; + } +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs index fbdb1a6a991a..e253b7880995 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/KeyFrameEvaluator.cs @@ -8,23 +8,23 @@ namespace Microsoft.UI.Composition; internal sealed class KeyFrameEvaluator : IKeyFrameEvaluator { private readonly long _startTimestamp; - private readonly T _initialValue; - private readonly T _finalValue; + private readonly AnimationKeyFrame _initialValue; + private readonly AnimationKeyFrame _finalValue; private readonly TimeSpan _duration; private readonly TimeSpan _totalDuration; - private readonly SortedDictionary _keyFrames; - private readonly Func _lerp; + private readonly SortedDictionary> _keyFrames; + private readonly Func, AnimationKeyFrame, float, T> _lerp; private readonly Compositor _compositor; private long? _pauseTimestamp; private long _totalPause; public KeyFrameEvaluator( - T initialValue, - T finalValue, + AnimationKeyFrame initialValue, + AnimationKeyFrame finalValue, TimeSpan duration, - SortedDictionary keyFrames, - Func lerp, + SortedDictionary> keyFrames, + Func, AnimationKeyFrame, float, T> lerp, int iterationCount, AnimationIterationBehavior iterationBehavior, Compositor compositor) @@ -44,7 +44,7 @@ public KeyFrameEvaluator( var elapsed = new TimeSpan(_compositor.TimestampInTicks - _totalPause - _startTimestamp); if (elapsed >= _totalDuration) { - return (_finalValue, true); + return (_finalValue.Value, true); } elapsed = TimeSpan.FromTicks(elapsed.Ticks % _duration.Ticks); @@ -56,7 +56,7 @@ public object Evaluate(float progress) { if (progress >= 1.0f) { - return (_finalValue, true); + return (_finalValue.Value, true); } return EvaluateInternal(progress).Value; @@ -68,7 +68,7 @@ public object Evaluate(float progress) if (nextKeyFrame == currentFrame) { // currentFrame is one that exists in the dictionary already. - return (_keyFrames[currentFrame], false); + return (_keyFrames[currentFrame].Value, false); } var previousKeyFrame = _keyFrames.Keys.LastOrDefault(k => k <= currentFrame); diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs index 416bcfb73cb2..8afb417da53a 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ScalarKeyFrameAnimation.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using Uno; namespace Microsoft.UI.Composition; @@ -11,7 +12,7 @@ namespace Microsoft.UI.Composition; public partial class ScalarKeyFrameAnimation : KeyFrameAnimation { #if !__IOS__ - private readonly SortedDictionary _keyFrames = new(); + private readonly SortedDictionary> _keyFrames = new(); #endif internal ScalarKeyFrameAnimation(Compositor compositor) : base(compositor) @@ -22,19 +23,23 @@ internal ScalarKeyFrameAnimation(Compositor compositor) : base(compositor) private protected override int KeyFrameCountCore => _keyFrames.Count; public void InsertKeyFrame(float normalizedProgressKey, float value) - => _keyFrames[normalizedProgressKey] = value; + => InsertKeyFrame(normalizedProgressKey, value, Compositor.GetDefaultEasingFunction()); - [NotImplemented] public void InsertKeyFrame(float normalizedProgressKey, float value, CompositionEasingFunction easingFunction) - => InsertKeyFrame(normalizedProgressKey, value); + => _keyFrames[normalizedProgressKey] = new() { Value = value, EasingFunction = easingFunction }; internal override object? Start(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, CompositionObject compositionObject) { base.Start(propertyName, subPropertyName, compositionObject); + if (!_keyFrames.TryGetValue(0, out var startValue)) { - startValue = (float)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); + startValue = new() + { + Value = (float)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()), + EasingFunction = Compositor.GetDefaultEasingFunction() + }; } if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) @@ -42,8 +47,12 @@ public void InsertKeyFrame(float normalizedProgressKey, float value, Composition finalValue = _keyFrames.Values.LastOrDefault(startValue); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, float.Lerp, IterationCount, IterationBehavior, Compositor); - return startValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static float Lerp(AnimationKeyFrame value1, AnimationKeyFrame value2, float amount) + => float.Lerp(value1.Value, value2.Value, value2.EasingFunction.Ease(amount)); + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Lerp, IterationCount, IterationBehavior, Compositor); + return startValue.Value; } #endif } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs index 1cc4a6994c2a..7d6de29458d0 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector2KeyFrameAnimation.cs @@ -4,12 +4,13 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; namespace Microsoft.UI.Composition; public partial class Vector2KeyFrameAnimation : KeyFrameAnimation { - private readonly SortedDictionary _keyFrames = new(); + private readonly SortedDictionary> _keyFrames = new(); internal Vector2KeyFrameAnimation(Compositor compositor) : base(compositor) { @@ -17,15 +18,22 @@ internal Vector2KeyFrameAnimation(Compositor compositor) : base(compositor) private protected override int KeyFrameCountCore => _keyFrames.Count; + public void InsertKeyFrame(float normalizedProgressKey, Vector2 value, CompositionEasingFunction easingFunction) + => _keyFrames[normalizedProgressKey] = new() { Value = value, EasingFunction = easingFunction }; + public void InsertKeyFrame(float normalizedProgressKey, Vector2 value) - => _keyFrames[normalizedProgressKey] = value; + => InsertKeyFrame(normalizedProgressKey, value, Compositor.GetDefaultEasingFunction()); internal override object? Start(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, CompositionObject compositionObject) { base.Start(propertyName, subPropertyName, compositionObject); if (!_keyFrames.TryGetValue(0, out var startValue)) { - startValue = (Vector2)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); + startValue = new() + { + Value = (Vector2)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()), + EasingFunction = Compositor.GetDefaultEasingFunction() + }; } if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) @@ -33,7 +41,11 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector2 value) finalValue = _keyFrames.Values.LastOrDefault(startValue); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector2.Lerp, IterationCount, IterationBehavior, Compositor); - return startValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector2 Lerp(AnimationKeyFrame value1, AnimationKeyFrame value2, float amount) + => Vector2.Lerp(value1.Value, value2.Value, value2.EasingFunction.Ease(amount)); + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Lerp, IterationCount, IterationBehavior, Compositor); + return startValue.Value; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs index bff93e612f80..c2fb789732ca 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector3KeyFrameAnimation.cs @@ -5,12 +5,13 @@ using System.Diagnostics; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; namespace Microsoft.UI.Composition; public partial class Vector3KeyFrameAnimation : KeyFrameAnimation { - private readonly SortedDictionary _keyFrames = new(); + private readonly SortedDictionary> _keyFrames = new(); internal Vector3KeyFrameAnimation(Compositor compositor) : base(compositor) { @@ -18,15 +19,22 @@ internal Vector3KeyFrameAnimation(Compositor compositor) : base(compositor) private protected override int KeyFrameCountCore => _keyFrames.Count; + public void InsertKeyFrame(float normalizedProgressKey, Vector3 value, CompositionEasingFunction easingFunction) + => _keyFrames[normalizedProgressKey] = new() { Value = value, EasingFunction = easingFunction }; + public void InsertKeyFrame(float normalizedProgressKey, Vector3 value) - => _keyFrames[normalizedProgressKey] = value; + => InsertKeyFrame(normalizedProgressKey, value, Compositor.GetDefaultEasingFunction()); internal override object? Start(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, CompositionObject compositionObject) { base.Start(propertyName, subPropertyName, compositionObject); if (!_keyFrames.TryGetValue(0, out var startValue)) { - startValue = (Vector3)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); + startValue = new() + { + Value = (Vector3)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()), + EasingFunction = Compositor.GetDefaultEasingFunction() + }; } if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) @@ -34,7 +42,11 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector3 value) finalValue = _keyFrames.Values.LastOrDefault(startValue); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector3.Lerp, IterationCount, IterationBehavior, Compositor); - return startValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector3 Lerp(AnimationKeyFrame value1, AnimationKeyFrame value2, float amount) + => Vector3.Lerp(value1.Value, value2.Value, value2.EasingFunction.Ease(amount)); + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Lerp, IterationCount, IterationBehavior, Compositor); + return startValue.Value; } } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs index f280e56d69f3..d83d75550af1 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/Vector4KeyFrameAnimation.cs @@ -5,12 +5,13 @@ using System.Diagnostics; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; namespace Microsoft.UI.Composition; public partial class Vector4KeyFrameAnimation : KeyFrameAnimation { - private readonly SortedDictionary _keyFrames = new(); + private readonly SortedDictionary> _keyFrames = new(); internal Vector4KeyFrameAnimation(Compositor compositor) : base(compositor) { @@ -18,15 +19,22 @@ internal Vector4KeyFrameAnimation(Compositor compositor) : base(compositor) private protected override int KeyFrameCountCore => _keyFrames.Count; + public void InsertKeyFrame(float normalizedProgressKey, Vector4 value, CompositionEasingFunction easingFunction) + => _keyFrames[normalizedProgressKey] = new() { Value = value, EasingFunction = easingFunction }; + public void InsertKeyFrame(float normalizedProgressKey, Vector4 value) - => _keyFrames[normalizedProgressKey] = value; + => InsertKeyFrame(normalizedProgressKey, value, Compositor.GetDefaultEasingFunction()); internal override object? Start(ReadOnlySpan propertyName, ReadOnlySpan subPropertyName, CompositionObject compositionObject) { base.Start(propertyName, subPropertyName, compositionObject); if (!_keyFrames.TryGetValue(0, out var startValue)) { - startValue = (Vector4)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()); + startValue = new() + { + Value = (Vector4)compositionObject.GetAnimatableProperty(propertyName.ToString(), subPropertyName.ToString()), + EasingFunction = Compositor.GetDefaultEasingFunction() + }; } if (!_keyFrames.TryGetValue(1.0f, out var finalValue)) @@ -34,7 +42,11 @@ public void InsertKeyFrame(float normalizedProgressKey, Vector4 value) finalValue = _keyFrames.Values.LastOrDefault(startValue); } - _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Vector4.Lerp, IterationCount, IterationBehavior, Compositor); - return startValue; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector4 Lerp(AnimationKeyFrame value1, AnimationKeyFrame value2, float amount) + => Vector4.Lerp(value1.Value, value2.Value, value2.EasingFunction.Ease(amount)); + + _keyframeEvaluator = new KeyFrameEvaluator(startValue, finalValue, Duration, _keyFrames, Lerp, IterationCount, IterationBehavior, Compositor); + return startValue.Value; } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs index 243282e3d046..a2f05ec72d69 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs @@ -3,23 +3,13 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class CompositionEasingFunction : global::Microsoft.UI.Composition.CompositionObject { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal CompositionEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.CubicBezierEasingFunction CreateCubicBezierEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::System.Numerics.Vector2 controlPoint1, global::System.Numerics.Vector2 controlPoint2) - { - throw new global::System.NotImplementedException("The member CubicBezierEasingFunction CompositionEasingFunction.CreateCubicBezierEasingFunction(Compositor owner, Vector2 controlPoint1, Vector2 controlPoint2) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CubicBezierEasingFunction%20CompositionEasingFunction.CreateCubicBezierEasingFunction%28Compositor%20owner%2C%20Vector2%20controlPoint1%2C%20Vector2%20controlPoint2%29"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateCubicBezierEasingFunction(Microsoft.UI.Composition.Compositor, System.Numerics.Vector2, System.Numerics.Vector2) + #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public static global::Microsoft.UI.Composition.LinearEasingFunction CreateLinearEasingFunction(global::Microsoft.UI.Composition.Compositor owner) diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs index 35aa1850c1ce..457b46dbe18c 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs @@ -198,13 +198,7 @@ public void Dispose() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateColorBrush() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateColorBrush(Windows.UI.Color) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateContainerVisual() -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CubicBezierEasingFunction CreateCubicBezierEasingFunction(global::System.Numerics.Vector2 controlPoint1, global::System.Numerics.Vector2 controlPoint2) - { - throw new global::System.NotImplementedException("The member CubicBezierEasingFunction Compositor.CreateCubicBezierEasingFunction(Vector2 controlPoint1, Vector2 controlPoint2) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CubicBezierEasingFunction%20Compositor.CreateCubicBezierEasingFunction%28Vector2%20controlPoint1%2C%20Vector2%20controlPoint2%29"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateCubicBezierEasingFunction(System.Numerics.Vector2, System.Numerics.Vector2) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateEffectFactory(Windows.Graphics.Effects.IGraphicsEffect) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateEffectFactory(Windows.Graphics.Effects.IGraphicsEffect, System.Collections.Generic.IEnumerable) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateExpressionAnimation() diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CubicBezierEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CubicBezierEasingFunction.cs index 536fd90b5f64..d3846c97d031 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CubicBezierEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CubicBezierEasingFunction.cs @@ -3,36 +3,13 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class CubicBezierEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal CubicBezierEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::System.Numerics.Vector2 ControlPoint1 - { - get - { - throw new global::System.NotImplementedException("The member Vector2 CubicBezierEasingFunction.ControlPoint1 is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=Vector2%20CubicBezierEasingFunction.ControlPoint1"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::System.Numerics.Vector2 ControlPoint2 - { - get - { - throw new global::System.NotImplementedException("The member Vector2 CubicBezierEasingFunction.ControlPoint2 is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=Vector2%20CubicBezierEasingFunction.ControlPoint2"); - } - } -#endif + // Skipping already declared property ControlPoint1 + // Skipping already declared property ControlPoint2 // Forced skipping of method Microsoft.UI.Composition.CubicBezierEasingFunction.ControlPoint1.get // Forced skipping of method Microsoft.UI.Composition.CubicBezierEasingFunction.ControlPoint2.get } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector2KeyFrameAnimation.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector2KeyFrameAnimation.cs index 0ed2c73c8464..73f7ceafc040 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector2KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector2KeyFrameAnimation.cs @@ -8,13 +8,7 @@ namespace Microsoft.UI.Composition #endif public partial class Vector2KeyFrameAnimation : global::Microsoft.UI.Composition.KeyFrameAnimation { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void InsertKeyFrame(float normalizedProgressKey, global::System.Numerics.Vector2 value, global::Microsoft.UI.Composition.CompositionEasingFunction easingFunction) - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.Vector2KeyFrameAnimation", "void Vector2KeyFrameAnimation.InsertKeyFrame(float normalizedProgressKey, Vector2 value, CompositionEasingFunction easingFunction)"); - } -#endif // Skipping already declared method Microsoft.UI.Composition.Vector2KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector2) + // Skipping already declared method Microsoft.UI.Composition.Vector2KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector2, Microsoft.UI.Composition.CompositionEasingFunction) } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector3KeyFrameAnimation.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector3KeyFrameAnimation.cs index 80bc6f51aa47..eeb993ce5f85 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector3KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector3KeyFrameAnimation.cs @@ -9,12 +9,6 @@ namespace Microsoft.UI.Composition public partial class Vector3KeyFrameAnimation : global::Microsoft.UI.Composition.KeyFrameAnimation { // Skipping already declared method Microsoft.UI.Composition.Vector3KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector3) -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void InsertKeyFrame(float normalizedProgressKey, global::System.Numerics.Vector3 value, global::Microsoft.UI.Composition.CompositionEasingFunction easingFunction) - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.Vector3KeyFrameAnimation", "void Vector3KeyFrameAnimation.InsertKeyFrame(float normalizedProgressKey, Vector3 value, CompositionEasingFunction easingFunction)"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.Vector3KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector3, Microsoft.UI.Composition.CompositionEasingFunction) } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector4KeyFrameAnimation.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector4KeyFrameAnimation.cs index ed24d44f42a9..1415e080557d 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector4KeyFrameAnimation.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Vector4KeyFrameAnimation.cs @@ -8,13 +8,7 @@ namespace Microsoft.UI.Composition #endif public partial class Vector4KeyFrameAnimation : global::Microsoft.UI.Composition.KeyFrameAnimation { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void InsertKeyFrame(float normalizedProgressKey, global::System.Numerics.Vector4 value, global::Microsoft.UI.Composition.CompositionEasingFunction easingFunction) - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.Vector4KeyFrameAnimation", "void Vector4KeyFrameAnimation.InsertKeyFrame(float normalizedProgressKey, Vector4 value, CompositionEasingFunction easingFunction)"); - } -#endif // Skipping already declared method Microsoft.UI.Composition.Vector4KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector4) + // Skipping already declared method Microsoft.UI.Composition.Vector4KeyFrameAnimation.InsertKeyFrame(float, System.Numerics.Vector4, Microsoft.UI.Composition.CompositionEasingFunction) } } From 8e9c96451088fb96bc02794ac2a4db57355a0d8c Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 21 Sep 2024 19:36:59 +0300 Subject: [PATCH 21/31] feat(composition): implement all Composition easing functions --- .../Composition/Compositor.cs | 9 ++ ...kerActiveInputInertiaHandler.AxisHelper.cs | 15 +- .../KeyFrameAnimations/BackEasingFunction.cs | 27 ++++ .../BounceEasingFunction.cs | 46 ++++++ .../CircleEasingFunction.cs | 23 +++ .../CompositionEasingFunction.cs | 42 +++++ .../ElasticEasingFunction.cs | 34 ++++ .../ExponentialEasingFunction.cs | 36 +++++ .../LinearEasingFunction.cs | 14 ++ .../KeyFrameAnimations/PowerEasingFunction.cs | 26 +++ .../KeyFrameAnimations/SineEasingFunction.cs | 24 +++ .../KeyFrameAnimations/StepEasingFunction.cs | 152 ++++++++++++++++++ .../Composition/Uno/CompositionMathHelpers.cs | 15 ++ .../BackEasingFunction.cs | 29 +--- .../BounceEasingFunction.cs | 40 +---- .../CircleEasingFunction.cs | 18 +-- .../CompositionEasingFunction.cs | 81 ++-------- .../Microsoft.UI.Composition/Compositor.cs | 24 +-- .../ElasticEasingFunction.cs | 40 +---- .../ExponentialEasingFunction.cs | 29 +--- .../LinearEasingFunction.cs | 8 +- .../PowerEasingFunction.cs | 30 +--- .../SineEasingFunction.cs | 18 +-- .../StepEasingFunction.cs | 84 +--------- 24 files changed, 495 insertions(+), 369 deletions(-) create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/BackEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/BounceEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/CircleEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/ElasticEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/ExponentialEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/LinearEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/PowerEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/SineEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/KeyFrameAnimations/StepEasingFunction.cs create mode 100644 src/Uno.UI.Composition/Composition/Uno/CompositionMathHelpers.cs diff --git a/src/Uno.UI.Composition/Composition/Compositor.cs b/src/Uno.UI.Composition/Composition/Compositor.cs index a10d720c450e..cfeefd2cc726 100644 --- a/src/Uno.UI.Composition/Composition/Compositor.cs +++ b/src/Uno.UI.Composition/Composition/Compositor.cs @@ -208,6 +208,15 @@ public CompositionEffectFactory CreateEffectFactory(IGraphicsEffect graphicsEffe public CubicBezierEasingFunction CreateCubicBezierEasingFunction(Vector2 controlPoint1, Vector2 controlPoint2) => new(this, controlPoint1, controlPoint2); + public LinearEasingFunction CreateLinearEasingFunction() + => new(this); + + public StepEasingFunction CreateStepEasingFunction() + => new(this); + + public StepEasingFunction CreateStepEasingFunction(int stepCount) + => new(this, stepCount); + partial void InvalidateRenderPartial(Visual visual); } } diff --git a/src/Uno.UI.Composition/Composition/InteractionTracker/InteractionTrackerActiveInputInertiaHandler.AxisHelper.cs b/src/Uno.UI.Composition/Composition/InteractionTracker/InteractionTrackerActiveInputInertiaHandler.AxisHelper.cs index ea45baefdb4e..d8d830bfe340 100644 --- a/src/Uno.UI.Composition/Composition/InteractionTracker/InteractionTrackerActiveInputInertiaHandler.AxisHelper.cs +++ b/src/Uno.UI.Composition/Composition/InteractionTracker/InteractionTrackerActiveInputInertiaHandler.AxisHelper.cs @@ -2,6 +2,7 @@ using System; using System.Numerics; +using Uno.UI.Composition; namespace Microsoft.UI.Composition.Interactions; @@ -46,12 +47,6 @@ public AxisHelper(InteractionTrackerActiveInputInertiaHandler handler, Vector3 v FinalModifiedValue = Math.Clamp(FinalValue, GetValue(Handler._interactionTracker.MinPosition), GetValue(Handler._interactionTracker.MaxPosition)); } - private static bool IsCloseReal(float a, float b, float epsilon) - => MathF.Abs(a - b) <= epsilon; - - private static bool IsCloseRealZero(float a, float epsilon) - => MathF.Abs(a) < epsilon; - private float GetValue(Vector3 vector) { return Axis switch @@ -76,9 +71,9 @@ float TimeToMinimumVelocityCore(float initialVelocity, float decayRate, float in var time = 0.0f; if (initialVelocity > minimumVelocity) { - if (!IsCloseReal(decayRate, 1.0f, epsilon)) + if (!CompositionMathHelpers.IsCloseReal(decayRate, 1.0f, epsilon)) { - if (IsCloseRealZero(decayRate, epsilon) /*|| !_isInertiaEnabled*/) + if (CompositionMathHelpers.IsCloseRealZero(decayRate, epsilon) /*|| !_isInertiaEnabled*/) { return 0.0f; } @@ -104,11 +99,11 @@ private float CalculateDeltaPosition(float time) { float epsilon = 0.0000011920929f; - if (IsCloseReal(DecayRate, 1.0f, epsilon)) + if (CompositionMathHelpers.IsCloseReal(DecayRate, 1.0f, epsilon)) { return InitialVelocity * time; } - else if (IsCloseRealZero(DecayRate, epsilon) /*|| !_isInertiaEnabled*/) + else if (CompositionMathHelpers.IsCloseRealZero(DecayRate, epsilon) /*|| !_isInertiaEnabled*/) { return 0.0f; } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BackEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BackEasingFunction.cs new file mode 100644 index 000000000000..0c63dd1eca53 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BackEasingFunction.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; + +namespace Microsoft.UI.Composition; + +public partial class BackEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + public float Amplitude { get; } + + internal BackEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float amplitude) : base(owner) + { + Mode = mode; + Amplitude = float.IsFinite(amplitude) && amplitude >= 0 ? amplitude : 0.0f; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) + => MathF.Pow(t, 3) - t * Amplitude * MathF.Sin(t * MathF.PI); +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BounceEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BounceEasingFunction.cs new file mode 100644 index 000000000000..d45f39709f11 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/BounceEasingFunction.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; + +namespace Microsoft.UI.Composition; + +public partial class BounceEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + public int Bounces { get; } + public float Bounciness { get; } + + internal BounceEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int bounces, float bounciness) : base(owner) + { + Mode = mode; + Bounces = Math.Max(0, bounces); + Bounciness = float.IsFinite(bounciness) && bounciness >= 1.01f ? bounciness : 1.01f; + } + + internal override float Ease(float t) => Ease(t, Mode); + + // References: + // https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/BounceEase.cs + // https://github.com/microsoft/microsoft-ui-xaml/blob/main/src/dxaml/xcp/components/animation/EasingFunctions.cpp + + internal override float EaseIn(float t) + { + var fn = Bounces; + var fb = Bounciness; + + var a = ((1.0f - MathF.Pow(fb, fn)) / (1.0f - fb)) + (MathF.Pow(fb, fn) / 2.0f); + var b = MathF.Floor(MathF.Log(1.0f - a * t * (1.0f - fb)) / MathF.Log(fb)); + var c = (1.0f - MathF.Pow(fb, b)) / ((1.0f - fb) * a); + var d = (1.0f - MathF.Pow(fb, b + 1.0f)) / ((1.0f - fb) * a); + var e = 1.0f / MathF.Pow(fb, fn - b); + var f = (d - c) / 2.0f; + var g = t - (c + f); + + return (-e / (f * f)) * (g - f) * (g + f); + } +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CircleEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CircleEasingFunction.cs new file mode 100644 index 000000000000..52855b2e2bd2 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CircleEasingFunction.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class CircleEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + + internal CircleEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) : base(owner) + { + Mode = mode; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) => 1.0f - MathF.Sqrt(1.0f - t * t); +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs index f554f8f0ec47..66dfffa14b0c 100644 --- a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/CompositionEasingFunction.cs @@ -16,5 +16,47 @@ internal CompositionEasingFunction(Compositor owner) : base(owner) { } public static CubicBezierEasingFunction CreateCubicBezierEasingFunction(Compositor owner, Vector2 controlPoint1, Vector2 controlPoint2) => new(owner, controlPoint1, controlPoint2); + public static LinearEasingFunction CreateLinearEasingFunction(Compositor owner) + => new(owner); + + public static StepEasingFunction CreateStepEasingFunction(Compositor owner, int stepCount) + => new(owner, stepCount); + + public static StepEasingFunction CreateStepEasingFunction(Compositor owner) + => new(owner); + + public static BackEasingFunction CreateBackEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float amplitude) + => new(owner, mode, amplitude); + + public static BounceEasingFunction CreateBounceEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int bounces, float bounciness) + => new(owner, mode, bounces, bounciness); + + public static CircleEasingFunction CreateCircleEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) + => new(owner, mode); + + public static ElasticEasingFunction CreateElasticEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int oscillations, float springiness) + => new(owner, mode, oscillations, springiness); + + public static ExponentialEasingFunction CreateExponentialEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float exponent) + => new(owner, mode, exponent); + + public static PowerEasingFunction CreatePowerEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float power) + => new(owner, mode, power); + + public static SineEasingFunction CreateSineEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) + => new(owner, mode); + internal virtual float Ease(float t) => t; + + internal virtual float EaseIn(float t) => t; + + internal virtual float Ease(float t, CompositionEasingFunctionMode mode) => mode switch + { + CompositionEasingFunctionMode.In => EaseIn(t), + CompositionEasingFunctionMode.Out => 1.0f - EaseIn(1.0f - t), + CompositionEasingFunctionMode.InOut => (t < 0.5f) ? + EaseIn(t * 2.0f) * 0.5f : + (1.0f - EaseIn((1.0f - t) * 2.0f)) * 0.5f + 0.5f, + _ => t, + }; } diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ElasticEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ElasticEasingFunction.cs new file mode 100644 index 000000000000..f2cdf48d28af --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ElasticEasingFunction.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; + +namespace Microsoft.UI.Composition; + +public partial class ElasticEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + public int Oscillations { get; } + public float Springiness { get; } + + internal ElasticEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int oscillations, float springiness) : base(owner) + { + Mode = mode; + Oscillations = Math.Max(0, oscillations); + Springiness = float.IsFinite(springiness) ? springiness : 0.0f; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) + { + var amplitude = CompositionMathHelpers.IsCloseRealZero(Springiness) ? t : + (MathF.Exp(Springiness * t) - 1.0f) / (MathF.Exp(Springiness) - 1.0f); + + return amplitude * MathF.Sin(t * (2.0f * MathF.PI * Oscillations + MathF.PI / 2.0f)); + } +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ExponentialEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ExponentialEasingFunction.cs new file mode 100644 index 000000000000..1ef27bfdde84 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/ExponentialEasingFunction.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; + +namespace Microsoft.UI.Composition; + +public partial class ExponentialEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + public float Exponent { get; } + + internal ExponentialEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float exponent) : base(owner) + { + Mode = mode; + Exponent = float.IsFinite(exponent) ? exponent : 0.0f; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) + { + if (CompositionMathHelpers.IsCloseRealZero(Exponent)) + { + return t; + } + else + { + return (MathF.Exp(Exponent * t) - 1) / (MathF.Exp(Exponent) - 1); + } + } +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/LinearEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/LinearEasingFunction.cs new file mode 100644 index 000000000000..cd673cd6b5f6 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/LinearEasingFunction.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class LinearEasingFunction +{ + internal LinearEasingFunction(Compositor owner) : base(owner) { } +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/PowerEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/PowerEasingFunction.cs new file mode 100644 index 000000000000..86365966c03f --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/PowerEasingFunction.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class PowerEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + public float Power { get; } + + internal PowerEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float power) : base(owner) + { + Mode = mode; + Power = float.IsFinite(power) ? power : 0.0f; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) + => MathF.Pow(t, Power); +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/SineEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/SineEasingFunction.cs new file mode 100644 index 000000000000..641e1ace9beb --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/SineEasingFunction.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.UI.Composition; + +public partial class SineEasingFunction +{ + public CompositionEasingFunctionMode Mode { get; } + + internal SineEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) : base(owner) + { + Mode = mode; + } + + internal override float Ease(float t) => Ease(t, Mode); + + internal override float EaseIn(float t) + => 1.0f - MathF.Sin(MathF.PI * 0.5f * (1.0f - t)); +} diff --git a/src/Uno.UI.Composition/Composition/KeyFrameAnimations/StepEasingFunction.cs b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/StepEasingFunction.cs new file mode 100644 index 000000000000..7d7620cadf3c --- /dev/null +++ b/src/Uno.UI.Composition/Composition/KeyFrameAnimations/StepEasingFunction.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Uno.UI.Composition; + +namespace Microsoft.UI.Composition; + +public partial class StepEasingFunction +{ + private int _stepCount; + private int _finalStep; + private int _initialStep; + private bool _isFinalStepSingleFrame; + private bool _isInitialStepSingleFrame; + + private int _userFinalStep; + private int _userInitialStep; + private bool _initialStepAdjusted; + private bool _finalStepAdjusted; + private float _length; + + internal StepEasingFunction(Compositor owner, int stepCount = 1) : base(owner) + { + StepCount = stepCount; + InitialStep = 0; + FinalStep = stepCount; + IsFinalStepSingleFrame = false; + IsInitialStepSingleFrame = false; + + EnsureAnimationParameters(); + } + + public int StepCount + { + get => _stepCount; + set + { + _stepCount = value; + EnsureAnimationParameters(); + } + } + + public int FinalStep + { + get => _userFinalStep; + set + { + _finalStep = value; + + _finalStepAdjusted = false; + EnsureAnimationParameters(); + } + } + + public int InitialStep + { + get => _userInitialStep; + set + { + _initialStep = value; + + _initialStepAdjusted = false; + EnsureAnimationParameters(); + } + } + + public bool IsFinalStepSingleFrame + { + get => _isFinalStepSingleFrame; + set + { + _isFinalStepSingleFrame = value; + EnsureAnimationParameters(); + } + } + + public bool IsInitialStepSingleFrame + { + get => _isInitialStepSingleFrame; + set + { + _isInitialStepSingleFrame = value; + EnsureAnimationParameters(); + } + } + + private void EnsureAnimationParameters() + { + if (_stepCount < 1) + { + _stepCount = 1; + } + + _initialStep = Math.Clamp(_initialStep, 0, _stepCount); + _finalStep = Math.Clamp(_finalStep, 0, _stepCount); + + if (((_finalStep - _initialStep) == 1) && _isInitialStepSingleFrame && _isFinalStepSingleFrame) + { + _isFinalStepSingleFrame = false; + } + else if (((_finalStep - _initialStep) == 0) && (_isInitialStepSingleFrame || _isFinalStepSingleFrame)) + { + _isInitialStepSingleFrame = false; + _isFinalStepSingleFrame = false; + } + + _userInitialStep = _initialStep; + _userFinalStep = _finalStep; + + if (_isInitialStepSingleFrame && !_initialStepAdjusted) + { + _initialStep++; + _initialStepAdjusted = true; + } + + if (_isFinalStepSingleFrame && !_finalStepAdjusted) + { + _finalStep--; + _finalStepAdjusted = true; + } + + _length = 1.0f / (_finalStep - _initialStep + 1); + } + + internal override float Ease(float t) + { + var segment = (int)MathF.Floor(t / _length); + + if (t == 1.0f) + { + segment--; + } + + var step = _initialStep + segment; + + if ((t == 0.0f) && _isInitialStepSingleFrame) + { + step--; + } + else if ((t == 1.0f) && _isFinalStepSingleFrame) + { + step++; + } + + return (float)step / (float)_stepCount; + } +} diff --git a/src/Uno.UI.Composition/Composition/Uno/CompositionMathHelpers.cs b/src/Uno.UI.Composition/Composition/Uno/CompositionMathHelpers.cs new file mode 100644 index 000000000000..e0107e1604a8 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/Uno/CompositionMathHelpers.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Uno.UI.Composition; + +internal static class CompositionMathHelpers +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsCloseReal(float a, float b, float epsilon = 10.0f * float.Epsilon) + => MathF.Abs(a - b) <= epsilon; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsCloseRealZero(float a, float epsilon = 10.0f * float.Epsilon) + => MathF.Abs(a) < epsilon; +} diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BackEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BackEasingFunction.cs index e8a00e0d5a90..ae0189eb0728 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BackEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BackEasingFunction.cs @@ -3,36 +3,13 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class BackEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal BackEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Amplitude - { - get - { - throw new global::System.NotImplementedException("The member float BackEasingFunction.Amplitude is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20BackEasingFunction.Amplitude"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode BackEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20BackEasingFunction.Mode"); - } - } -#endif + // Skipping already declared property Amplitude + // Skipping already declared property Mode // Forced skipping of method Microsoft.UI.Composition.BackEasingFunction.Amplitude.get // Forced skipping of method Microsoft.UI.Composition.BackEasingFunction.Mode.get } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BounceEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BounceEasingFunction.cs index 3649aaa360f6..92ef17ae6554 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BounceEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/BounceEasingFunction.cs @@ -3,46 +3,14 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class BounceEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal BounceEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int Bounces - { - get - { - throw new global::System.NotImplementedException("The member int BounceEasingFunction.Bounces is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=int%20BounceEasingFunction.Bounces"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Bounciness - { - get - { - throw new global::System.NotImplementedException("The member float BounceEasingFunction.Bounciness is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20BounceEasingFunction.Bounciness"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode BounceEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20BounceEasingFunction.Mode"); - } - } -#endif + // Skipping already declared property Mode + // Skipping already declared property Bounciness + // Skipping already declared property Bounces // Forced skipping of method Microsoft.UI.Composition.BounceEasingFunction.Bounciness.get // Forced skipping of method Microsoft.UI.Composition.BounceEasingFunction.Mode.get // Forced skipping of method Microsoft.UI.Composition.BounceEasingFunction.Bounces.get diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CircleEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CircleEasingFunction.cs index 3c01925094f6..f082e0f622bd 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CircleEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CircleEasingFunction.cs @@ -3,26 +3,12 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class CircleEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal CircleEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode CircleEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20CircleEasingFunction.Mode"); - } - } -#endif + // Skipping already declared property Mode // Forced skipping of method Microsoft.UI.Composition.CircleEasingFunction.Mode.get } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs index a2f05ec72d69..b35f276fef4d 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionEasingFunction.cs @@ -9,76 +9,15 @@ namespace Microsoft.UI.Composition public partial class CompositionEasingFunction : global::Microsoft.UI.Composition.CompositionObject { // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateCubicBezierEasingFunction(Microsoft.UI.Composition.Compositor, System.Numerics.Vector2, System.Numerics.Vector2) - -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.LinearEasingFunction CreateLinearEasingFunction(global::Microsoft.UI.Composition.Compositor owner) - { - throw new global::System.NotImplementedException("The member LinearEasingFunction CompositionEasingFunction.CreateLinearEasingFunction(Compositor owner) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=LinearEasingFunction%20CompositionEasingFunction.CreateLinearEasingFunction%28Compositor%20owner%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.StepEasingFunction CreateStepEasingFunction(global::Microsoft.UI.Composition.Compositor owner) - { - throw new global::System.NotImplementedException("The member StepEasingFunction CompositionEasingFunction.CreateStepEasingFunction(Compositor owner) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=StepEasingFunction%20CompositionEasingFunction.CreateStepEasingFunction%28Compositor%20owner%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.StepEasingFunction CreateStepEasingFunction(global::Microsoft.UI.Composition.Compositor owner, int stepCount) - { - throw new global::System.NotImplementedException("The member StepEasingFunction CompositionEasingFunction.CreateStepEasingFunction(Compositor owner, int stepCount) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=StepEasingFunction%20CompositionEasingFunction.CreateStepEasingFunction%28Compositor%20owner%2C%20int%20stepCount%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.BackEasingFunction CreateBackEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode, float amplitude) - { - throw new global::System.NotImplementedException("The member BackEasingFunction CompositionEasingFunction.CreateBackEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float amplitude) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=BackEasingFunction%20CompositionEasingFunction.CreateBackEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%2C%20float%20amplitude%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.BounceEasingFunction CreateBounceEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode, int bounces, float bounciness) - { - throw new global::System.NotImplementedException("The member BounceEasingFunction CompositionEasingFunction.CreateBounceEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int bounces, float bounciness) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=BounceEasingFunction%20CompositionEasingFunction.CreateBounceEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%2C%20int%20bounces%2C%20float%20bounciness%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.CircleEasingFunction CreateCircleEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode) - { - throw new global::System.NotImplementedException("The member CircleEasingFunction CompositionEasingFunction.CreateCircleEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CircleEasingFunction%20CompositionEasingFunction.CreateCircleEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.ElasticEasingFunction CreateElasticEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode, int oscillations, float springiness) - { - throw new global::System.NotImplementedException("The member ElasticEasingFunction CompositionEasingFunction.CreateElasticEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, int oscillations, float springiness) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=ElasticEasingFunction%20CompositionEasingFunction.CreateElasticEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%2C%20int%20oscillations%2C%20float%20springiness%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.ExponentialEasingFunction CreateExponentialEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode, float exponent) - { - throw new global::System.NotImplementedException("The member ExponentialEasingFunction CompositionEasingFunction.CreateExponentialEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float exponent) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=ExponentialEasingFunction%20CompositionEasingFunction.CreateExponentialEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%2C%20float%20exponent%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.PowerEasingFunction CreatePowerEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode, float power) - { - throw new global::System.NotImplementedException("The member PowerEasingFunction CompositionEasingFunction.CreatePowerEasingFunction(Compositor owner, CompositionEasingFunctionMode mode, float power) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=PowerEasingFunction%20CompositionEasingFunction.CreatePowerEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%2C%20float%20power%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public static global::Microsoft.UI.Composition.SineEasingFunction CreateSineEasingFunction(global::Microsoft.UI.Composition.Compositor owner, global::Microsoft.UI.Composition.CompositionEasingFunctionMode mode) - { - throw new global::System.NotImplementedException("The member SineEasingFunction CompositionEasingFunction.CreateSineEasingFunction(Compositor owner, CompositionEasingFunctionMode mode) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=SineEasingFunction%20CompositionEasingFunction.CreateSineEasingFunction%28Compositor%20owner%2C%20CompositionEasingFunctionMode%20mode%29"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateLinearEasingFunction(Microsoft.UI.Composition.Compositor) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateStepEasingFunction(Microsoft.UI.Composition.Compositor) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateStepEasingFunction(Microsoft.UI.Composition.Compositor, int) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateBackEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode, float) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateBounceEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode, int, float) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateCircleEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateElasticEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode, int, float) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateExpoEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode, float) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreateSineEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode) + // Skipping already declared method Microsoft.UI.Composition.CompositionEasingFunction.CreatePowerEasingFunction(Microsoft.UI.Composition.Compositor, Microsoft.UI.Composition.CompositionEasingFunctionMode, float) } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs index 457b46dbe18c..8afa2035912b 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/Compositor.cs @@ -205,13 +205,7 @@ public void Dispose() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateExpressionAnimation(string) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateInsetClip() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateInsetClip(float, float, float, float) -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.LinearEasingFunction CreateLinearEasingFunction() - { - throw new global::System.NotImplementedException("The member LinearEasingFunction Compositor.CreateLinearEasingFunction() is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=LinearEasingFunction%20Compositor.CreateLinearEasingFunction%28%29"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateLinearEasingFunction() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreatePropertySet() #if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] @@ -294,20 +288,8 @@ public void Dispose() throw new global::System.NotImplementedException("The member SpotLight Compositor.CreateSpotLight() is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=SpotLight%20Compositor.CreateSpotLight%28%29"); } #endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.StepEasingFunction CreateStepEasingFunction() - { - throw new global::System.NotImplementedException("The member StepEasingFunction Compositor.CreateStepEasingFunction() is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=StepEasingFunction%20Compositor.CreateStepEasingFunction%28%29"); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.StepEasingFunction CreateStepEasingFunction(int stepCount) - { - throw new global::System.NotImplementedException("The member StepEasingFunction Compositor.CreateStepEasingFunction(int stepCount) is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=StepEasingFunction%20Compositor.CreateStepEasingFunction%28int%20stepCount%29"); - } -#endif + // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateStepEasingFunction() + // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateStepEasingFunction(int) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateColorGradientStop() // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateColorGradientStop(float, Windows.UI.Color) // Skipping already declared method Microsoft.UI.Composition.Compositor.CreateLinearGradientBrush() diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ElasticEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ElasticEasingFunction.cs index 8054ce534f68..2a795ac89adf 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ElasticEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ElasticEasingFunction.cs @@ -3,46 +3,14 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class ElasticEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal ElasticEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode ElasticEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20ElasticEasingFunction.Mode"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int Oscillations - { - get - { - throw new global::System.NotImplementedException("The member int ElasticEasingFunction.Oscillations is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=int%20ElasticEasingFunction.Oscillations"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Springiness - { - get - { - throw new global::System.NotImplementedException("The member float ElasticEasingFunction.Springiness is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20ElasticEasingFunction.Springiness"); - } - } -#endif + // Skipping already declared property Mode + // Skipping already declared property Oscillations + // Skipping already declared property Springiness // Forced skipping of method Microsoft.UI.Composition.ElasticEasingFunction.Oscillations.get // Forced skipping of method Microsoft.UI.Composition.ElasticEasingFunction.Mode.get // Forced skipping of method Microsoft.UI.Composition.ElasticEasingFunction.Springiness.get diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ExponentialEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ExponentialEasingFunction.cs index d6dcba070c10..dc5e3a62c3a2 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ExponentialEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/ExponentialEasingFunction.cs @@ -3,36 +3,13 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class ExponentialEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal ExponentialEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Exponent - { - get - { - throw new global::System.NotImplementedException("The member float ExponentialEasingFunction.Exponent is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20ExponentialEasingFunction.Exponent"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode ExponentialEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20ExponentialEasingFunction.Mode"); - } - } -#endif + // Skipping already declared property Exponent + // Skipping already declared property Mode // Forced skipping of method Microsoft.UI.Composition.ExponentialEasingFunction.Mode.get // Forced skipping of method Microsoft.UI.Composition.ExponentialEasingFunction.Exponent.get } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/LinearEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/LinearEasingFunction.cs index 7ae6d4787013..91cf154fbb61 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/LinearEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/LinearEasingFunction.cs @@ -3,15 +3,11 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class LinearEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal LinearEasingFunction() - { - } -#endif + } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/PowerEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/PowerEasingFunction.cs index f3d7f8020e5b..795d64a0376c 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/PowerEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/PowerEasingFunction.cs @@ -3,37 +3,13 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class PowerEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal PowerEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode PowerEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20PowerEasingFunction.Mode"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float Power - { - get - { - throw new global::System.NotImplementedException("The member float PowerEasingFunction.Power is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=float%20PowerEasingFunction.Power"); - } - } -#endif - // Forced skipping of method Microsoft.UI.Composition.PowerEasingFunction.Mode.get + // Skipping already declared property Mode + // Skipping already declared property Power // Forced skipping of method Microsoft.UI.Composition.PowerEasingFunction.Power.get } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/SineEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/SineEasingFunction.cs index c7fd278b5686..7244ea1e1961 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/SineEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/SineEasingFunction.cs @@ -3,26 +3,12 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class SineEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal SineEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::Microsoft.UI.Composition.CompositionEasingFunctionMode Mode - { - get - { - throw new global::System.NotImplementedException("The member CompositionEasingFunctionMode SineEasingFunction.Mode is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=CompositionEasingFunctionMode%20SineEasingFunction.Mode"); - } - } -#endif + // Skipping already declared property Mode // Forced skipping of method Microsoft.UI.Composition.SineEasingFunction.Mode.get } } diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/StepEasingFunction.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/StepEasingFunction.cs index 6b3540790a61..5b12b112119c 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/StepEasingFunction.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/StepEasingFunction.cs @@ -3,88 +3,16 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class StepEasingFunction : global::Microsoft.UI.Composition.CompositionEasingFunction { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal StepEasingFunction() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int StepCount - { - get - { - throw new global::System.NotImplementedException("The member int StepEasingFunction.StepCount is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=int%20StepEasingFunction.StepCount"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.StepEasingFunction", "int StepEasingFunction.StepCount"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public bool IsInitialStepSingleFrame - { - get - { - throw new global::System.NotImplementedException("The member bool StepEasingFunction.IsInitialStepSingleFrame is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=bool%20StepEasingFunction.IsInitialStepSingleFrame"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.StepEasingFunction", "bool StepEasingFunction.IsInitialStepSingleFrame"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public bool IsFinalStepSingleFrame - { - get - { - throw new global::System.NotImplementedException("The member bool StepEasingFunction.IsFinalStepSingleFrame is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=bool%20StepEasingFunction.IsFinalStepSingleFrame"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.StepEasingFunction", "bool StepEasingFunction.IsFinalStepSingleFrame"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int InitialStep - { - get - { - throw new global::System.NotImplementedException("The member int StepEasingFunction.InitialStep is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=int%20StepEasingFunction.InitialStep"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.StepEasingFunction", "int StepEasingFunction.InitialStep"); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int FinalStep - { - get - { - throw new global::System.NotImplementedException("The member int StepEasingFunction.FinalStep is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=int%20StepEasingFunction.FinalStep"); - } - set - { - global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Microsoft.UI.Composition.StepEasingFunction", "int StepEasingFunction.FinalStep"); - } - } -#endif - // Forced skipping of method Microsoft.UI.Composition.StepEasingFunction.StepCount.get - // Forced skipping of method Microsoft.UI.Composition.StepEasingFunction.IsInitialStepSingleFrame.set + // Skipping already declared property StepCount + // Skipping already declared property FinalStep + // Skipping already declared property InitialStep + // Skipping already declared property IsFinalStepSingleFrame + // Skipping already declared property IsInitialStepSingleFrame // Forced skipping of method Microsoft.UI.Composition.StepEasingFunction.StepCount.set // Forced skipping of method Microsoft.UI.Composition.StepEasingFunction.InitialStep.get // Forced skipping of method Microsoft.UI.Composition.StepEasingFunction.FinalStep.set From 01834b197146c97f7225d3f7b522b0d8f5e3cd5b Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 21 Sep 2024 19:53:12 +0300 Subject: [PATCH 22/31] chore(composition): fix build --- src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj b/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj index 9e6ac5947771..f86014c02a32 100644 --- a/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj +++ b/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj @@ -33,7 +33,7 @@ - + From 4616ed20b88373929bf0a0c3f42dc6e92d50f749 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Sat, 21 Sep 2024 22:29:57 +0300 Subject: [PATCH 23/31] feat(composition): add easing functions UI tests --- .../Vector3KeyFrameAnimationSample.xaml | 12 ++++++++ .../Vector3KeyFrameAnimationSample.xaml.cs | 29 +++++++++++++++---- .../CompositionSpriteShape.skia.cs | 4 +-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/Vector3KeyFrameAnimationSample.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/Vector3KeyFrameAnimationSample.xaml index 186f8168f25f..90dcd2d6bbff 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/Vector3KeyFrameAnimationSample.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/Vector3KeyFrameAnimationSample.xaml @@ -13,6 +13,18 @@ + + + + + + + + + + + +