From 38db8d67c739ed095d1734753fa6e14ade149177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=C4=9Bzslav=20Imr=C3=BD=C5=A1ek?= Date: Fri, 30 Jul 2021 14:10:57 +0200 Subject: [PATCH] feat: Added CompositionGradientBrush implementation --- .../Border/BorderLayerRenderer.skia.cs | 89 +---- src/Uno.UI/UI/Xaml/Media/Brush.skia.cs | 357 +++++++++++++++--- .../CompositionGradientBrush.cs | 22 +- .../CompositionGradientExtendMode.cs | 10 +- .../CompositionMappingMode.cs | 8 +- .../Windows.UI.Composition/Compositor.cs | 16 +- .../UI/Composition/CompositionBrush.skia.cs | 2 +- .../Composition/CompositionColorBrush.skia.cs | 4 +- .../Composition/CompositionGradientBrush.cs | 109 ++++++ .../CompositionGradientBrush.skia.cs | 133 +++++++ .../CompositionGradientExtendMode.cs | 9 + .../UI/Composition/CompositionMappingMode.cs | 8 + .../CompositionSpriteShape.skia.cs | 92 ++--- .../CompositionSurfaceBrush.skia.cs | 2 +- src/Uno.UWP/UI/Composition/Compositor.cs | 29 +- .../UI/Composition/SkiaExtensions.skia.cs | 3 + .../UI/Composition/SpriteVisual.skia.cs | 19 +- 17 files changed, 690 insertions(+), 222 deletions(-) create mode 100644 src/Uno.UWP/UI/Composition/CompositionGradientBrush.cs create mode 100644 src/Uno.UWP/UI/Composition/CompositionGradientBrush.skia.cs create mode 100644 src/Uno.UWP/UI/Composition/CompositionGradientExtendMode.cs create mode 100644 src/Uno.UWP/UI/Composition/CompositionMappingMode.cs diff --git a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs index 36665651f31f..f808ff6151ab 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.skia.cs @@ -117,45 +117,19 @@ private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state) var backgroundShape = compositor.CreateSpriteShape(); var outerShape = compositor.CreateSpriteShape(); - Brush.AssignAndObserveBrush(borderBrush, color => borderShape.FillBrush = compositor.CreateColorBrush(color)) - .DisposeWith(disposables); - - if (background is GradientBrush gradientBackground) - { - //var fillMask = new CAShapeLayer() - //{ - // Path = path, - // Frame = area, - // // We only use the fill color to create the mask area - // FillColor = _Color.White.CGColor, - //}; - //// We reduce the adjustedArea again so that the gradient is inside the border (like in Windows) - //adjustedArea = adjustedArea.Shrink((float)adjustedStrokeThicknessOffset); - - //CreateGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask); - } - else if (background is SolidColorBrush scbBackground) - { - Brush.AssignAndObserveBrush(scbBackground, color => backgroundShape.FillBrush = compositor.CreateColorBrush(color)) + // Border brush + Brush.AssignAndObserveBrush(borderBrush, compositor, brush => borderShape.FillBrush = brush) .DisposeWith(disposables); - } - else if (background is ImageBrush imgBackground) + + // Background brush + if (background is ImageBrush imgBackground) { adjustedArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground); } - else if (background is AcrylicBrush acrylicBrush) - { - Brush.AssignAndObserveBrush(acrylicBrush, color => backgroundShape.FillBrush = compositor.CreateColorBrush(color)) - .DisposeWith(disposables); - } - else if (background is XamlCompositionBrushBase unsupportedCompositionBrush) - { - Brush.AssignAndObserveBrush(unsupportedCompositionBrush, color => backgroundShape.FillBrush = compositor.CreateColorBrush(color)) - .DisposeWith(disposables); - } else { - backgroundShape.FillBrush = null; + Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush) + .DisposeWith(disposables); } var borderPath = GetRoundedRect(cornerRadius, innerCornerRadius, area, adjustedArea); @@ -190,44 +164,14 @@ private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state) var backgroundArea = area; - if (background is GradientBrush gradientBackground) - { - var fullArea = new Rect( - area.X + borderThickness.Left, - area.Y + borderThickness.Top, - area.Width - borderThickness.Left - borderThickness.Right, - area.Height - borderThickness.Top - borderThickness.Bottom); - - var insideArea = new Rect(default, fullArea.Size); - // var insertionIndex = 0; - - // CreateGradientBrushLayers(fullArea, insideArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask: null); - } - else if (background is SolidColorBrush scbBackground) - { - Brush.AssignAndObserveBrush(scbBackground, c => backgroundShape.FillBrush = compositor.CreateColorBrush(c)) - .DisposeWith(disposables); - - // This is required because changing the CornerRadius changes the background drawing - // implementation and we don't want a rectangular background behind a rounded background. - Disposable.Create(() => backgroundShape.FillBrush = null) - .DisposeWith(disposables); - } - else if (background is ImageBrush imgBackground) + // Background brush + if (background is ImageBrush imgBackground) { backgroundArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, backgroundArea, imgBackground); } - else if (background is AcrylicBrush acrylicBrush) - { - Brush.AssignAndObserveBrush(acrylicBrush, c => backgroundShape.FillBrush = compositor.CreateColorBrush(c)) - .DisposeWith(disposables); - - Disposable.Create(() => backgroundShape.FillBrush = null) - .DisposeWith(disposables); - } - else if (background is XamlCompositionBrushBase unsupportedCompositionBrush) + else { - Brush.AssignAndObserveBrush(unsupportedCompositionBrush, c => backgroundShape.FillBrush = compositor.CreateColorBrush(c)) + Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush) .DisposeWith(disposables); // This is required because changing the CornerRadius changes the background drawing @@ -235,10 +179,6 @@ private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state) Disposable.Create(() => backgroundShape.FillBrush = null) .DisposeWith(disposables); } - else - { - backgroundShape.FillBrush = null; - } var geometrySource = new SkiaGeometrySource2D(); var geometry = geometrySource.Geometry; @@ -251,13 +191,14 @@ private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state) if (borderThickness != Thickness.Empty) { - Action> createLayer = builder => + Action> createLayer = builder => { var spriteShape = compositor.CreateSpriteShape(); var geometry = new SkiaGeometrySource2D(); - Brush.AssignAndObserveBrush(borderBrush, c => spriteShape.StrokeBrush = compositor.CreateColorBrush(c)) - .DisposeWith(disposables); + // Border brush + Brush.AssignAndObserveBrush(borderBrush, compositor, brush => spriteShape.StrokeBrush = brush) + .DisposeWith(disposables); builder(spriteShape, geometry.Geometry); spriteShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(geometry)); diff --git a/src/Uno.UI/UI/Xaml/Media/Brush.skia.cs b/src/Uno.UI/UI/Xaml/Media/Brush.skia.cs index 31915704b2f8..1c0e1dbef070 100644 --- a/src/Uno.UI/UI/Xaml/Media/Brush.skia.cs +++ b/src/Uno.UI/UI/Xaml/Media/Brush.skia.cs @@ -1,90 +1,341 @@ using System; -using System.Collections.Generic; -using System.Text; -using Uno.Extensions; +using System.Numerics; using Uno.Disposables; -using Windows.UI; +using Windows.UI.Composition; namespace Windows.UI.Xaml.Media { public abstract partial class Brush { - internal delegate void ColorSetterHandler(Color color); + internal delegate void BrushSetterHandler(CompositionBrush brush); - internal static IDisposable AssignAndObserveBrush(Brush b, ColorSetterHandler colorSetter, Action imageBrushCallback = null) + /// + /// Registers observers for property changes on the specified Brush. + /// Note that skia implementation of this method should only be used for property observervation. + /// + internal static IDisposable AssignAndObserveBrush(Brush brush, Action colorSetter, Action imageBrushCallback = null) { + if (brush == null) + { + colorSetter(SolidColorBrushHelper.Transparent.Color); + + return null; + } + var disposables = new CompositeDisposable(); + if (brush is SolidColorBrush colorBrush) + { + brush.RegisterDisposablePropertyChangedCallback( + SolidColorBrush.ColorProperty, + (s, colorArg) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); - if (b != null) + brush.RegisterDisposablePropertyChangedCallback( + SolidColorBrush.OpacityProperty, + (s, colorArg) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); + } + else if (brush is GradientBrush gradientBrush) { - if (b is SolidColorBrush colorBrush) + if (gradientBrush is LinearGradientBrush linearGradient) { - colorSetter(colorBrush.ColorWithOpacity); - - colorBrush.RegisterDisposablePropertyChangedCallback( - SolidColorBrush.ColorProperty, - (s, colorArg) => colorSetter((s as SolidColorBrush).ColorWithOpacity) - ) + gradientBrush.RegisterDisposablePropertyChangedCallback( + LinearGradientBrush.StartPointProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) .DisposeWith(disposables); - colorBrush.RegisterDisposablePropertyChangedCallback( - SolidColorBrush.OpacityProperty, - (s, colorArg) => colorSetter((s as SolidColorBrush).ColorWithOpacity) - ) + gradientBrush.RegisterDisposablePropertyChangedCallback( + LinearGradientBrush.EndPointProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) .DisposeWith(disposables); } - //else if (b is ImageBrush imageBrush) - //{ - // Action<_Image> action = _ => colorSetter(SolidColorBrushHelper.Transparent.Color); - // imageBrush.ImageChanged += action; + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.GradientStopsProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); + + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.MappingModeProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); + + //gradientBrush.RegisterDisposablePropertyChangedCallback( + // GradientBrush.OpacityProperty, + // (s, e) => compositionBrush.Opacity = e.NewValue + // ) + //.DisposeWith(disposables); - // disposables.Add(() => imageBrush.ImageChanged -= action); - //} - else if (b is AcrylicBrush acrylicBrush) - { - colorSetter(acrylicBrush.FallbackColorWithOpacity); + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.SpreadMethodProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); - acrylicBrush.RegisterDisposablePropertyChangedCallback( + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.RelativeTransformProperty, + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); + } + //else if (b is ImageBrush imageBrush) + //{ + //} + else if (brush is AcrylicBrush acrylicBrush) + { + acrylicBrush.RegisterDisposablePropertyChangedCallback( AcrylicBrush.FallbackColorProperty, - (s, args) => colorSetter((s as AcrylicBrush).FallbackColorWithOpacity)) - .DisposeWith(disposables); + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); - acrylicBrush.RegisterDisposablePropertyChangedCallback( + acrylicBrush.RegisterDisposablePropertyChangedCallback( AcrylicBrush.OpacityProperty, - (s, args) => colorSetter((s as AcrylicBrush).FallbackColorWithOpacity)) - .DisposeWith(disposables); + (s, e) => colorSetter(SolidColorBrushHelper.Transparent.Color) + ) + .DisposeWith(disposables); + } - return disposables; - } - else if (b is XamlCompositionBrushBase unsupportedCompositionBrush) - { - colorSetter(unsupportedCompositionBrush.FallbackColorWithOpacity); + colorSetter(SolidColorBrushHelper.Transparent.Color); + + return disposables; + } + + internal static IDisposable AssignAndObserveBrush(Brush brush, Compositor compositor, BrushSetterHandler brushSetter, Action imageBrushCallback = null) + { + if (brush == null) + { + brushSetter(null); + + return null; + } - unsupportedCompositionBrush.RegisterDisposablePropertyChangedCallback( - XamlCompositionBrushBase.FallbackColorProperty, - (s, colorArg) => colorSetter((s as XamlCompositionBrushBase).FallbackColorWithOpacity) + if (brush is SolidColorBrush colorBrush) + { + return AssignAndObserveSolidColorBrush(colorBrush, compositor, brushSetter); + } + else if (brush is GradientBrush gradientBrush) + { + return AssignAndObserveGradientBrush(gradientBrush, compositor, brushSetter); + } + //else if (b is ImageBrush imageBrush) + //{ + // Action<_Image> action = _ => colorSetter(SolidColorBrushHelper.Transparent.Color); + + // imageBrush.ImageChanged += action; + + // disposables.Add(() => imageBrush.ImageChanged -= action); + //} + else if (brush is AcrylicBrush acrylicBrush) + { + return AssignAndObserveAcrylicBrush(acrylicBrush, compositor, brushSetter); + } + else if (brush is XamlCompositionBrushBase xamlCompositionBrushBase) + { + return AssignAndObserveXamlCompositionBrush(xamlCompositionBrushBase, compositor, brushSetter); + } + else + { + return AssignAndObservePlaceholderBrush(compositor, brushSetter); + } + } + + private static IDisposable AssignAndObserveSolidColorBrush(SolidColorBrush brush, Compositor compositor, BrushSetterHandler brushSetter) + { + var disposables = new CompositeDisposable(); + + var compositionBrush = compositor.CreateColorBrush(brush.ColorWithOpacity); + + brush.RegisterDisposablePropertyChangedCallback( + SolidColorBrush.ColorProperty, + (s, colorArg) => compositionBrush.Color = brush.ColorWithOpacity + ) + .DisposeWith(disposables); + + brush.RegisterDisposablePropertyChangedCallback( + SolidColorBrush.OpacityProperty, + (s, colorArg) => compositionBrush.Color = brush.ColorWithOpacity + ) + .DisposeWith(disposables); + + brushSetter(compositionBrush); + + return disposables; + } + + private static IDisposable AssignAndObserveGradientBrush(GradientBrush gradientBrush, Compositor compositor, BrushSetterHandler brushSetter) + { + var disposables = new CompositeDisposable(); + + var compositionBrush = CreateCompositionGradientBrush(gradientBrush, compositor); + + if (gradientBrush is LinearGradientBrush linearGradient) + { + var clgb = (CompositionLinearGradientBrush)compositionBrush; + + gradientBrush.RegisterDisposablePropertyChangedCallback( + LinearGradientBrush.StartPointProperty, + (s, e) => clgb.StartPoint = (Vector2)e.NewValue ) - .DisposeWith(disposables); + .DisposeWith(disposables); - unsupportedCompositionBrush.RegisterDisposablePropertyChangedCallback( - OpacityProperty, - (s, colorArg) => colorSetter((s as XamlCompositionBrushBase).FallbackColorWithOpacity) + gradientBrush.RegisterDisposablePropertyChangedCallback( + LinearGradientBrush.EndPointProperty, + (s, e) => clgb.EndPoint = (Vector2)e.NewValue ) - .DisposeWith(disposables); - } - else - { - colorSetter(SolidColorBrushHelper.Transparent.Color); - } + .DisposeWith(disposables); + } + + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.GradientStopsProperty, + (s, e) => ConvertGradientColorStops(compositionBrush.Compositor, compositionBrush, (GradientStopCollection)e.NewValue) + ) + .DisposeWith(disposables); + + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.MappingModeProperty, + (s, e) => compositionBrush.MappingMode = (CompositionMappingMode)e.NewValue + ) + .DisposeWith(disposables); + + //gradientBrush.RegisterDisposablePropertyChangedCallback( + // GradientBrush.OpacityProperty, + // (s, e) => compositionBrush.Opacity = e.NewValue + // ) + //.DisposeWith(disposables); + + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.SpreadMethodProperty, + (s, e) => compositionBrush.ExtendMode = ConvertGradientExtendMode((GradientSpreadMethod)e.NewValue) + ) + .DisposeWith(disposables); + + gradientBrush.RegisterDisposablePropertyChangedCallback( + GradientBrush.RelativeTransformProperty, + (s, e) => compositionBrush.RelativeTransformMatrix = ((Transform)e.NewValue)?.MatrixCore ?? Matrix3x2.Identity + ) + .DisposeWith(disposables); + + brushSetter(compositionBrush); + + return disposables; + } + + private static IDisposable AssignAndObserveAcrylicBrush(AcrylicBrush acrylicBrush, Compositor compositor, BrushSetterHandler brushSetter) + { + var disposables = new CompositeDisposable(); + + var compositionBrush = compositor.CreateColorBrush(acrylicBrush.FallbackColorWithOpacity); + + acrylicBrush.RegisterDisposablePropertyChangedCallback( + AcrylicBrush.FallbackColorProperty, + (s, colorArg) => compositionBrush.Color = acrylicBrush.FallbackColorWithOpacity + ) + .DisposeWith(disposables); + + acrylicBrush.RegisterDisposablePropertyChangedCallback( + AcrylicBrush.OpacityProperty, + (s, colorArg) => compositionBrush.Color = acrylicBrush.FallbackColorWithOpacity + ) + .DisposeWith(disposables); + + brushSetter(compositionBrush); + + return disposables; + } + + private static IDisposable AssignAndObserveXamlCompositionBrush(XamlCompositionBrushBase brush, Compositor compositor, BrushSetterHandler brushSetter) + { + var disposables = new CompositeDisposable(); + + var compositionBrush = brush.CompositionBrush; + + //brush.RegisterDisposablePropertyChangedCallback( + // XamlCompositionBrushBase.CompositionBrushProperty, + // (s, e) => brushSetter(((CompositionBrush)e.NewValue)) + //) + //.DisposeWith(disposables); + + brushSetter(compositionBrush); + + return disposables; + } + + private static IDisposable AssignAndObservePlaceholderBrush(Compositor compositor, BrushSetterHandler brushSetter) + { + var disposables = new CompositeDisposable(); + + var compositionBrush = compositor.CreateColorBrush(SolidColorBrushHelper.Transparent.Color); + + brushSetter(compositionBrush); + + return disposables; + } + + private static CompositionGradientBrush CreateCompositionGradientBrush(GradientBrush gradientBrush, Compositor compositor) + { + CompositionGradientBrush compositionBrush; + if (gradientBrush is LinearGradientBrush linearGradient) + { + CompositionLinearGradientBrush compositionLinearGradientBrush = compositor.CreateLinearGradientBrush(); + compositionLinearGradientBrush.StartPoint = linearGradient.StartPoint.ToVector2(); + compositionLinearGradientBrush.EndPoint = linearGradient.EndPoint.ToVector2(); + + compositionBrush = compositionLinearGradientBrush; } else { - colorSetter(SolidColorBrushHelper.Transparent.Color); + return null; } - return disposables; + compositionBrush.RelativeTransformMatrix = gradientBrush.RelativeTransform?.MatrixCore ?? Matrix3x2.Identity; + compositionBrush.ExtendMode = ConvertGradientExtendMode(gradientBrush.SpreadMethod); + compositionBrush.MappingMode = ConvertBrushMappingMode(gradientBrush.MappingMode); + ConvertGradientColorStops(compositor, compositionBrush, gradientBrush.GradientStops); + + return compositionBrush; } + private static void ConvertGradientColorStops(Compositor compositor, CompositionGradientBrush compositionBrush, GradientStopCollection gradientStops) + { + compositionBrush.ColorStops.Clear(); + + foreach (var stop in gradientStops) + { + compositionBrush.ColorStops.Add(compositor.CreateColorGradientStop((float)stop.Offset, stop.Color)); + } + } + + private static CompositionGradientExtendMode ConvertGradientExtendMode(GradientSpreadMethod spreadMethod) + { + switch (spreadMethod) + { + case GradientSpreadMethod.Repeat: + return CompositionGradientExtendMode.Wrap; + case GradientSpreadMethod.Reflect: + return CompositionGradientExtendMode.Mirror; + case GradientSpreadMethod.Pad: + default: + return CompositionGradientExtendMode.Clamp; + } + } + + private static CompositionMappingMode ConvertBrushMappingMode(BrushMappingMode mappingMode) + { + switch (mappingMode) + { + case BrushMappingMode.Absolute: + return CompositionMappingMode.Absolute; + case BrushMappingMode.RelativeToBoundingBox: + default: + return CompositionMappingMode.Relative; + } + } } } diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientBrush.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientBrush.cs index 6449c7d2a29f..9533eb53ff15 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientBrush.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientBrush.cs @@ -2,12 +2,12 @@ #pragma warning disable 114 // new keyword hiding namespace Windows.UI.Composition { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented] #endif public partial class CompositionGradientBrush : global::Windows.UI.Composition.CompositionBrush { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::System.Numerics.Matrix3x2 TransformMatrix { @@ -21,7 +21,7 @@ public partial class CompositionGradientBrush : global::Windows.UI.Composition. } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::System.Numerics.Vector2 Scale { @@ -35,7 +35,7 @@ public partial class CompositionGradientBrush : global::Windows.UI.Composition. } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public float RotationAngleInDegrees { @@ -49,7 +49,7 @@ public float RotationAngleInDegrees } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public float RotationAngle { @@ -63,7 +63,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::System.Numerics.Vector2 Offset { @@ -91,7 +91,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionGradientExtendMode ExtendMode { @@ -105,7 +105,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::System.Numerics.Vector2 CenterPoint { @@ -119,7 +119,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::System.Numerics.Vector2 AnchorPoint { @@ -133,7 +133,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionColorGradientStopCollection ColorStops { @@ -143,7 +143,7 @@ public float RotationAngle } } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionMappingMode MappingMode { diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientExtendMode.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientExtendMode.cs index e6e825346d38..43447937961a 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientExtendMode.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionGradientExtendMode.cs @@ -2,19 +2,19 @@ #pragma warning disable 114 // new keyword hiding namespace Windows.UI.Composition { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false + #if false [global::Uno.NotImplemented] #endif public enum CompositionGradientExtendMode { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false Clamp, #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false Wrap, #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false Mirror, #endif } diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionMappingMode.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionMappingMode.cs index a3676eeabd90..f719c60281dd 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionMappingMode.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/CompositionMappingMode.cs @@ -2,16 +2,16 @@ #pragma warning disable 114 // new keyword hiding namespace Windows.UI.Composition { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false + #if false [global::Uno.NotImplemented] #endif public enum CompositionMappingMode { - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false Absolute, #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false Relative, #endif } diff --git a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/Compositor.cs b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/Compositor.cs index 5ee73a8b09a0..a2f1dba38949 100644 --- a/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/Compositor.cs +++ b/src/Uno.UWP/Generated/3.0.0.0/Windows.UI.Composition/Compositor.cs @@ -261,21 +261,21 @@ public static float MinGlobalPlaybackRate throw new global::System.NotImplementedException("The member CompositionBackdropBrush Compositor.CreateHostBackdropBrush() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionColorGradientStop CreateColorGradientStop() { throw new global::System.NotImplementedException("The member CompositionColorGradientStop Compositor.CreateColorGradientStop() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionColorGradientStop CreateColorGradientStop( float offset, global::Windows.UI.Color color) { throw new global::System.NotImplementedException("The member CompositionColorGradientStop Compositor.CreateColorGradientStop(float offset, Color color) is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionLinearGradientBrush CreateLinearGradientBrush() { @@ -335,14 +335,14 @@ public static float MinGlobalPlaybackRate throw new global::System.NotImplementedException("The member CompositionContainerShape Compositor.CreateContainerShape() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionEllipseGeometry CreateEllipseGeometry() { throw new global::System.NotImplementedException("The member CompositionEllipseGeometry Compositor.CreateEllipseGeometry() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionLineGeometry CreateLineGeometry() { @@ -358,14 +358,14 @@ public static float MinGlobalPlaybackRate throw new global::System.NotImplementedException("The member PathKeyFrameAnimation Compositor.CreatePathKeyFrameAnimation() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionRectangleGeometry CreateRectangleGeometry() { throw new global::System.NotImplementedException("The member CompositionRectangleGeometry Compositor.CreateRectangleGeometry() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionRoundedRectangleGeometry CreateRoundedRectangleGeometry() { @@ -431,7 +431,7 @@ public static float MinGlobalPlaybackRate throw new global::System.NotImplementedException("The member CompositionProjectedShadowReceiver Compositor.CreateProjectedShadowReceiver() is not implemented in Uno."); } #endif - #if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ + #if false [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Windows.UI.Composition.CompositionRadialGradientBrush CreateRadialGradientBrush() { diff --git a/src/Uno.UWP/UI/Composition/CompositionBrush.skia.cs b/src/Uno.UWP/UI/Composition/CompositionBrush.skia.cs index 08842e429c4a..5571dad3a999 100644 --- a/src/Uno.UWP/UI/Composition/CompositionBrush.skia.cs +++ b/src/Uno.UWP/UI/Composition/CompositionBrush.skia.cs @@ -6,7 +6,7 @@ namespace Windows.UI.Composition { public partial class CompositionBrush { - internal virtual void UpdatePaint(SKPaint fillPaint) + internal virtual void UpdatePaint(SKPaint paint, SKRect bounds) { } } diff --git a/src/Uno.UWP/UI/Composition/CompositionColorBrush.skia.cs b/src/Uno.UWP/UI/Composition/CompositionColorBrush.skia.cs index 44950ce4aa1e..8cb8c47de7a7 100644 --- a/src/Uno.UWP/UI/Composition/CompositionColorBrush.skia.cs +++ b/src/Uno.UWP/UI/Composition/CompositionColorBrush.skia.cs @@ -8,9 +8,9 @@ namespace Windows.UI.Composition { public partial class CompositionColorBrush { - internal override void UpdatePaint(SKPaint fillPaint) + internal override void UpdatePaint(SKPaint paint, SKRect bounds) { - fillPaint.Color = Color.ToSKColor(Compositor.CurrentOpacity); + paint.Color = Color.ToSKColor(Compositor.CurrentOpacity); } } } diff --git a/src/Uno.UWP/UI/Composition/CompositionGradientBrush.cs b/src/Uno.UWP/UI/Composition/CompositionGradientBrush.cs new file mode 100644 index 000000000000..562b60f66265 --- /dev/null +++ b/src/Uno.UWP/UI/Composition/CompositionGradientBrush.cs @@ -0,0 +1,109 @@ +#nullable enable + +using System; +using System.Numerics; + +namespace Windows.UI.Composition +{ + public partial class CompositionGradientBrush : CompositionBrush + { + private CompositionGradientExtendMode _extendMode; + private CompositionMappingMode _mappingMode; + private Matrix3x2 _transformMatrix = Matrix3x2.Identity; + private Matrix3x2 _relativeTransformMatrix = Matrix3x2.Identity; + private Vector2 _scale = new Vector2(1, 1); + private float _rotationAngleInDegrees; + private float _rotationAngle; + private Vector2 _offset; + private Vector2 _centerPoint; + + internal CompositionGradientBrush(Compositor compositor) + : base(compositor) + { + ColorStops = new CompositionColorGradientStopCollection(this); + } + + public CompositionColorGradientStopCollection ColorStops { get; } + + public CompositionGradientExtendMode ExtendMode + { + get => _extendMode; + set => SetProperty(ref _extendMode, value); + } + + public CompositionMappingMode MappingMode + { + get => _mappingMode; + set => SetProperty(ref _mappingMode, value); + } + + public Matrix3x2 TransformMatrix + { + get => _transformMatrix; + set => SetProperty(ref _transformMatrix, value); + } + + public Vector2 Scale + { + get => _scale; + set => SetProperty(ref _scale, value); + } + + public float RotationAngleInDegrees + { + get => _rotationAngleInDegrees; + set { _rotationAngle = value * (float)(Math.PI / 180); SetProperty(ref _rotationAngleInDegrees, value); } + } + + public float RotationAngle + { + get => _rotationAngle; + set { _rotationAngleInDegrees = value * 180 / (float)Math.PI; SetProperty(ref _rotationAngle, value); } + } + + public Vector2 Offset + { + get => _offset; + set => SetProperty(ref _offset, value); + } + + public Vector2 CenterPoint + { + get => _centerPoint; + set => SetProperty(ref _centerPoint, value); + } + + internal Matrix3x2 RelativeTransformMatrix + { + get => _relativeTransformMatrix; + set => SetProperty(ref _relativeTransformMatrix, value); + } + + internal void InvalidateColorStops() + { + OnPropertyChanged(nameof(ColorStops), true); + } + + private protected override void OnPropertyChangedCore(string? propertyName, bool isSubPropertyChange) + { + switch (propertyName) + { + case nameof(ColorStops): + OnColorStopsChanged(ColorStops); + break; + case nameof(ExtendMode): + OnExtendModeChanged(ExtendMode); + break; + case nameof(MappingMode): + OnMappingModeChanged(MappingMode); + break; + default: + break; + } + } + + partial void OnExtendModeChanged(CompositionGradientExtendMode extendMode); + partial void OnColorStopsChanged(CompositionColorGradientStopCollection colorStops); + partial void OnMappingModeChanged(CompositionMappingMode mappingMode); + } +} diff --git a/src/Uno.UWP/UI/Composition/CompositionGradientBrush.skia.cs b/src/Uno.UWP/UI/Composition/CompositionGradientBrush.skia.cs new file mode 100644 index 000000000000..9e577053ad75 --- /dev/null +++ b/src/Uno.UWP/UI/Composition/CompositionGradientBrush.skia.cs @@ -0,0 +1,133 @@ +#nullable enable + +using System.Numerics; +using SkiaSharp; + +namespace Windows.UI.Composition +{ + public partial class CompositionGradientBrush + { + private bool _isColorStopsValid; + + private SKColor[]? _colors; + private float[]? _colorPositions; + private SKShaderTileMode _tileMode; + + private protected SKColor[]? Colors => _colors; + private protected float[]? ColorPositions => _colorPositions; + private protected SKShaderTileMode TileMode => _tileMode; + + internal override sealed void UpdatePaint(SKPaint paint, SKRect bounds) + { + if (!_isColorStopsValid) + { + UpdateColorStops(ColorStops); + } + + UpdatePaintCore(paint, bounds); + } + + private protected virtual void UpdatePaintCore(SKPaint paint, SKRect bounds) + { + + } + + private protected SKMatrix CreateTransformMatrix(SKRect bounds) + { + var transform = SKMatrix.Identity; + + // Translate to origin + if (CenterPoint != Vector2.Zero) + { + transform = SKMatrix.CreateTranslation(-CenterPoint.X, -CenterPoint.Y); + } + + // Scaling + if (Scale != Vector2.One) + { + transform = transform.PostConcat(SKMatrix.CreateScale(Scale.X, Scale.Y)); + } + + // Rotating + if (RotationAngle != 0) + { + transform = transform.PostConcat(SKMatrix.CreateRotation(RotationAngle)); + } + + // Translating + if (Offset != Vector2.Zero) + { + transform = transform.PostConcat(SKMatrix.CreateTranslation(Offset.X, Offset.Y)); + } + + // Translate back + if (CenterPoint != Vector2.Zero) + { + transform = transform.PostConcat(SKMatrix.CreateTranslation(CenterPoint.X, CenterPoint.Y)); + } + + if (!TransformMatrix.IsIdentity) + { + transform = transform.PostConcat(TransformMatrix.ToSKMatrix()); + } + + var relativeTransform = RelativeTransformMatrix.IsIdentity ? SKMatrix.Identity : RelativeTransformMatrix.ToSKMatrix(); + if (!relativeTransform.IsIdentity) + { + relativeTransform.TransX *= bounds.Width; + relativeTransform.TransY *= bounds.Height; + + transform = transform.PostConcat(relativeTransform); + } + + return transform; + } + + private void UpdateColorStops(CompositionColorGradientStopCollection colorStops) + { + var stopCount = colorStops.Count; + var colors = _colors; + var colorPositions = _colorPositions; + + if (colors == null || colors.Length != stopCount) + { + colors = new SKColor[stopCount]; + colorPositions = new float[stopCount]; + } + + for (int i = 0; i < colorStops.Count; i++) + { + var gradientStop = colorStops[i]; + + colors[i] = gradientStop.Color.ToSKColor(); + colorPositions![i] = gradientStop.Offset; + } + + _colors = colors; + _colorPositions = colorPositions; + _isColorStopsValid = true; + } + + partial void OnColorStopsChanged(CompositionColorGradientStopCollection colorStops) => _isColorStopsValid = false; + + partial void OnExtendModeChanged(CompositionGradientExtendMode extendMode) + { + SKShaderTileMode tileMode; + switch (extendMode) + { + default: + case CompositionGradientExtendMode.Clamp: + tileMode = SKShaderTileMode.Clamp; + break; + case CompositionGradientExtendMode.Mirror: + tileMode = SKShaderTileMode.Mirror; + break; + case CompositionGradientExtendMode.Wrap: + tileMode = SKShaderTileMode.Repeat; + break; + } + + _tileMode = tileMode; + } + } +} diff --git a/src/Uno.UWP/UI/Composition/CompositionGradientExtendMode.cs b/src/Uno.UWP/UI/Composition/CompositionGradientExtendMode.cs new file mode 100644 index 000000000000..9eb1eae0b279 --- /dev/null +++ b/src/Uno.UWP/UI/Composition/CompositionGradientExtendMode.cs @@ -0,0 +1,9 @@ +namespace Windows.UI.Composition +{ + public enum CompositionGradientExtendMode + { + Clamp = 0, + Wrap = 1, + Mirror = 2 + } +} diff --git a/src/Uno.UWP/UI/Composition/CompositionMappingMode.cs b/src/Uno.UWP/UI/Composition/CompositionMappingMode.cs new file mode 100644 index 000000000000..7c0c3fba5e90 --- /dev/null +++ b/src/Uno.UWP/UI/Composition/CompositionMappingMode.cs @@ -0,0 +1,8 @@ +namespace Windows.UI.Composition +{ + public enum CompositionMappingMode + { + Absolute = 0, + Relative = 1 + } +} diff --git a/src/Uno.UWP/UI/Composition/CompositionSpriteShape.skia.cs b/src/Uno.UWP/UI/Composition/CompositionSpriteShape.skia.cs index 80412de3d761..e1d474166eb8 100644 --- a/src/Uno.UWP/UI/Composition/CompositionSpriteShape.skia.cs +++ b/src/Uno.UWP/UI/Composition/CompositionSpriteShape.skia.cs @@ -1,7 +1,6 @@ #nullable enable using SkiaSharp; -using System; namespace Windows.UI.Composition { @@ -12,65 +11,72 @@ public partial class CompositionSpriteShape : CompositionShape internal override void Render(SKSurface surface, SKImageInfo info) { - if (Geometry is CompositionPathGeometry cpg) + SkiaGeometrySource2D? geometrySource = Geometry?.BuildGeometry() as SkiaGeometrySource2D; + + SKPath? geometry = geometrySource?.Geometry; + if (geometry == null) { - if (cpg.Path?.GeometrySource is SkiaGeometrySource2D geometrySource) - { - if(FillBrush != null) - { - FillBrush.UpdatePaint(TryCreateFillPaint()); + return; + } - surface.Canvas.DrawPath(geometrySource.Geometry, _fillPaint); - } + if (FillBrush != null) + { + var fillPaint = TryCreateAndClearFillPaint(); - if (StrokeBrush != null && StrokeThickness > 0) - { - var strokePaint = TryCreateStrokePaint(); + FillBrush.UpdatePaint(fillPaint, geometry.Bounds); - if (StrokeBrush is CompositionColorBrush stroke) - { - strokePaint.StrokeWidth = StrokeThickness; - strokePaint.Color = stroke.Color.ToSKColor(Compositor.CurrentOpacity); - } + surface.Canvas.DrawPath(geometry, fillPaint); + } - surface.Canvas.DrawPath(geometrySource.Geometry, _strokePaint); - } - } - else if(cpg.Path?.GeometrySource is null) - { + if (StrokeBrush != null && StrokeThickness > 0) + { + var fillPaint = TryCreateAndClearFillPaint(); + var strokePaint = TryCreateAndClearStrokePaint(); - } - else + // Set stroke thickness + strokePaint.StrokeWidth = StrokeThickness; + // TODO: Add support for dashes here + // strokePaint.PathEffect = SKPathEffect.CreateDash(); + + // Generate stroke geometry for bounds that will be passed to a brush. + // - [Future]: This generated geometry should also be used for hit testing. + using (var strokeGeometry = strokePaint.GetFillPath(geometry)) { - throw new InvalidOperationException($"CompositionPath does not support the {cpg.Path?.GeometrySource} geometry source"); + StrokeBrush.UpdatePaint(fillPaint, strokeGeometry.Bounds); + + surface.Canvas.DrawPath(strokeGeometry, fillPaint); } } } + private SKPaint TryCreateAndClearStrokePaint() => TryCreateAndClearPaint(ref _strokePaint, true); + + private SKPaint TryCreateAndClearFillPaint() => TryCreateAndClearPaint(ref _fillPaint, false); - private SKPaint TryCreateStrokePaint() + private SKPaint TryCreateAndClearPaint(ref SKPaint? paint, bool isStroke) { - if (_strokePaint == null) + if (paint == null) { - _strokePaint = new SKPaint(); - _strokePaint.IsStroke = true; - _strokePaint.IsAntialias = true; - _strokePaint.IsAutohinted = true; + // Initialization + paint = new SKPaint(); + paint.IsStroke = isStroke; + paint.IsAntialias = true; + paint.IsAutohinted = true; } - - return _strokePaint; - } - private SKPaint TryCreateFillPaint() - { - if (_fillPaint == null) + else { - _fillPaint = new SKPaint(); - _fillPaint.IsStroke = false; - _fillPaint.IsAntialias = true; - _fillPaint.IsAutohinted = true; + // Cleanup + // - Brushes can change, we cant leave color and shader garbage + // from last rendering around for the next pass. + paint.Color = SKColors.White; // Transparent color wouldnt draw anything + if (paint.Shader != null) + { + paint.Shader.Dispose(); + paint.Shader = null; + } } - - return _fillPaint; + + return paint; } } } diff --git a/src/Uno.UWP/UI/Composition/CompositionSurfaceBrush.skia.cs b/src/Uno.UWP/UI/Composition/CompositionSurfaceBrush.skia.cs index 25e2e0c61f60..bd024432c5c1 100644 --- a/src/Uno.UWP/UI/Composition/CompositionSurfaceBrush.skia.cs +++ b/src/Uno.UWP/UI/Composition/CompositionSurfaceBrush.skia.cs @@ -8,7 +8,7 @@ namespace Windows.UI.Composition { public partial class CompositionSurfaceBrush : CompositionBrush { - internal override void UpdatePaint(SKPaint fillPaint) + internal override void UpdatePaint(SKPaint fillPaint, SKRect bounds) { if (Surface is SkiaCompositionSurface scs) { diff --git a/src/Uno.UWP/UI/Composition/Compositor.cs b/src/Uno.UWP/UI/Composition/Compositor.cs index 7d26aad93105..99f9a73800bb 100644 --- a/src/Uno.UWP/UI/Composition/Compositor.cs +++ b/src/Uno.UWP/UI/Composition/Compositor.cs @@ -1,7 +1,5 @@ #nullable enable -using Windows.UI; - namespace Windows.UI.Composition { public partial class Compositor : global::System.IDisposable @@ -50,6 +48,18 @@ public CompositionPathGeometry CreatePathGeometry() public CompositionPathGeometry CreatePathGeometry(CompositionPath path) => new CompositionPathGeometry(this, path); + public CompositionEllipseGeometry CreateEllipseGeometry() + => new CompositionEllipseGeometry(this); + + public CompositionLineGeometry CreateLineGeometry() + => new CompositionLineGeometry(this); + + public CompositionRectangleGeometry CreateRectangleGeometry() + => new CompositionRectangleGeometry(this); + + public CompositionRoundedRectangleGeometry CreateRoundedRectangleGeometry() + => new CompositionRoundedRectangleGeometry(this); + public CompositionSurfaceBrush CreateSurfaceBrush() => new CompositionSurfaceBrush(this); @@ -76,6 +86,21 @@ public InsetClip CreateInsetClip(float leftInset, float topInset, float rightIns BottomInset = bottomInset }; + public CompositionLinearGradientBrush CreateLinearGradientBrush() + => new CompositionLinearGradientBrush(this); + + public CompositionRadialGradientBrush CreateRadialGradientBrush() + => new CompositionRadialGradientBrush(this); + + public CompositionColorGradientStop CreateColorGradientStop() + => new CompositionColorGradientStop(this); + + public CompositionColorGradientStop CreateColorGradientStop(float offset, Color color) + => new CompositionColorGradientStop(this) { + Offset = offset, + Color = color + }; + internal void InvalidateRender() => InvalidateRenderPartial(); partial void InvalidateRenderPartial(); diff --git a/src/Uno.UWP/UI/Composition/SkiaExtensions.skia.cs b/src/Uno.UWP/UI/Composition/SkiaExtensions.skia.cs index d05f6e44f98b..533a5cc36048 100644 --- a/src/Uno.UWP/UI/Composition/SkiaExtensions.skia.cs +++ b/src/Uno.UWP/UI/Composition/SkiaExtensions.skia.cs @@ -27,6 +27,9 @@ public static SKColor ToSKColor(this Color color) public static SKColor ToSKColor(this Color color, double alphaMultiplier) => new SKColor(red: color.R, green: color.G, blue: color.B, alpha: (byte)(color.A * alphaMultiplier)); + public static SKPoint ToSKPoint(this Vector2 vector) + => new SKPoint(vector.X, vector.Y); + public static SKMatrix44 ToSKMatrix44(this Matrix4x4 m) { var ret = new SKMatrix44(); diff --git a/src/Uno.UWP/UI/Composition/SpriteVisual.skia.cs b/src/Uno.UWP/UI/Composition/SpriteVisual.skia.cs index 540225440073..45665f5a98b3 100644 --- a/src/Uno.UWP/UI/Composition/SpriteVisual.skia.cs +++ b/src/Uno.UWP/UI/Composition/SpriteVisual.skia.cs @@ -1,20 +1,11 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Numerics; using SkiaSharp; -using Uno.Disposables; -using Uno.Extensions; -using Uno.Logging; -using Windows.UI.WebUI; namespace Windows.UI.Composition { public partial class SpriteVisual : ContainerVisual { - private readonly SerialDisposable _csbSubscription = new SerialDisposable(); - private readonly SKPaint _paint = new SKPaint() { @@ -23,20 +14,12 @@ private readonly SKPaint _paint partial void OnBrushChangedPartial(CompositionBrush? brush) { - _csbSubscription.Disposable = null; - - if (brush is CompositionSurfaceBrush csb) - { - csb.PropertyChanged += UpdatePaint; - _csbSubscription.Disposable = Disposable.Create(() => csb.PropertyChanged -= UpdatePaint); - } - UpdatePaint(); } private void UpdatePaint() { - Brush?.UpdatePaint(_paint); + Brush?.UpdatePaint(_paint, new SKRect(left: 0, top: 0, right: Size.X, bottom: Size.Y)); } internal override void Render(SKSurface surface, SKImageInfo info)