From ceb8f8181e781b83e9e855cd64170c989b91d77b Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 11:59:23 +0300 Subject: [PATCH 01/10] Revert "chore: remove SKCanvasElement" This reverts commit 94b01ba73c15782fec6ed54b33168983938c1b95. --- .../UITests.Shared/UITests.Shared.projitems | 8 ++ .../SKCanvasElementImpl.skia.cs | 136 ++++++++++++++++++ .../SKCanvasElement_Simple.xaml | 24 ++++ .../SKCanvasElement_Simple.xaml.cs | 18 +++ .../Given_SKCanvasElement.skia.cs | 53 +++++++ .../SKCanvasElement.SKCanvasVisual.skia.cs | 26 ++++ .../Graphics/SKCanvasElement.crossruntime.cs | 23 +++ .../UI/Xaml/Graphics/SKCanvasElement.skia.cs | 77 ++++++++++ 8 files changed, 365 insertions(+) create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml create mode 100644 src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml.cs create mode 100644 src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs create mode 100644 src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs create mode 100644 src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs create mode 100644 src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 13e2c051e4cd..368015d7831e 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -4694,6 +4694,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -6037,6 +6041,7 @@ + CloseRequestedTests.xaml @@ -8288,6 +8293,9 @@ VisualSurfaceTests.xaml + + SKCanvasElement_Simple.xaml + GLCanvasElement_Simple.xaml diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs new file mode 100644 index 000000000000..dd3c99d8c3f6 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs @@ -0,0 +1,136 @@ +#if __SKIA__ +using System; +using Windows.Foundation; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using SkiaSharp; + +namespace UITests.Shared.Windows_UI_Composition; + +public class SKCanvasElementImpl : SKCanvasElement +{ + public static int SampleCount => 3; + + public static DependencyProperty SampleProperty { get; } = DependencyProperty.Register( + nameof(Sample), + typeof(int), + typeof(SKCanvasElementImpl), + new PropertyMetadata(0, (o, args) => ((SKCanvasElementImpl)o).SampleChanged((int)args.NewValue))); + + public int Sample + { + get => (int)GetValue(SampleProperty); + set => SetValue(SampleProperty, value); + } + + private void SampleChanged(int newIndex) + { + Sample = Math.Min(Math.Max(0, newIndex), SampleCount - 1); + } + + protected override void RenderOverride(SKCanvas canvas, Size area) + { + var minDim = Math.Min(area.Width, area.Height); + // rescale to fit the given area, assuming each drawing is 260x260 + canvas.Scale((float)(minDim / 260), (float)(minDim / 260)); + + switch (Sample) + { + case 0: + SkiaDrawing0(canvas); + break; + case 1: + SkiaDrawing1(canvas); + break; + case 2: + SkiaDrawing2(canvas); + break; + } + } + + // https://fiddle.skia.org/c/@shapes + private void SkiaDrawing0(SKCanvas canvas) + { + canvas.DrawColor(SKColors.White); + + var paint = new SKPaint(); + paint.Style = SKPaintStyle.Fill; + paint.IsAntialias = true; + paint.StrokeWidth = 4; + paint.Color = new SKColor(0xff4285F4); + + var rect = SKRect.Create(10, 10, 100, 160); + canvas.DrawRect(rect, paint); + + var oval = new SKPath(); + oval.AddRoundRect(rect, 20, 20); + oval.Offset(new SKPoint(40, 80)); + paint.Color = new SKColor(0xffDB4437); + canvas.DrawPath(oval, paint); + + paint.Color = new SKColor(0xff0F9D58); + canvas.DrawCircle(180, 50, 25, paint); + + rect.Offset(80, 50); + paint.Color = new SKColor(0xffF4B400); + paint.Style = SKPaintStyle.Stroke; + canvas.DrawRoundRect(rect, 10, 10, paint); + } + + // https://fiddle.skia.org/c/@bezier_curves + private void SkiaDrawing1(SKCanvas canvas) + { + canvas.DrawColor(SKColors.White); + + var paint = new SKPaint(); + paint.Style = SKPaintStyle.Stroke; + paint.StrokeWidth = 8; + paint.Color = new SKColor(0xff4285F4); + paint.IsAntialias = true; + paint.StrokeCap = SKStrokeCap.Round; + + var path = new SKPath(); + path.MoveTo(10, 10); + path.QuadTo(256, 64, 128, 128); + path.QuadTo(10, 192, 250, 250); + canvas.DrawPath(path, paint); + } + + // https://fiddle.skia.org/c/@shader + private void SkiaDrawing2(SKCanvas canvas) + { + var paint = new SKPaint(); + using var pathEffect = SKPathEffect.CreateDiscrete(10.0f, 4.0f); + paint.PathEffect = pathEffect; + SKPoint[] points = + { + new SKPoint(0.0f, 0.0f), + new SKPoint(256.0f, 256.0f) + }; + SKColor[] colors = + { + new SKColor(66, 133, 244), + new SKColor(15, 157, 88) + }; + paint.Shader = SKShader.CreateLinearGradient(points[0], points[1], colors, SKShaderTileMode.Clamp); + paint.IsAntialias = true; + canvas.Clear(SKColors.White); + var path = Star(); + canvas.DrawPath(path, paint); + + SKPath Star() + { + const float R = 60.0f, C = 128.0f; + var path = new SKPath(); + path.MoveTo(C + R, C); + for (var i = 1; i < 15; ++i) + { + var a = 0.44879895f * i; + var r = R + R * (i % 2); + path.LineTo((float)(C + r * Math.Cos(a)), (float)(C + r * Math.Sin(a))); + } + return path; + } + } +} +#endif diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml new file mode 100644 index 000000000000..904ef6496384 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml.cs new file mode 100644 index 000000000000..f6d3d39688d8 --- /dev/null +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElement_Simple.xaml.cs @@ -0,0 +1,18 @@ +using Uno.UI.Samples.Controls; +using Microsoft.UI.Xaml.Controls; + +namespace UITests.Shared.Windows_UI_Composition +{ + [Sample("Microsoft.UI.Composition")] + public sealed partial class SKCanvasElement_Simple : UserControl + { +#if __SKIA__ + public int MaxSampleIndex => SKCanvasElementImpl.SampleCount - 1; +#endif + + public SKCanvasElement_Simple() + { + this.InitializeComponent(); + } + } +} diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs new file mode 100644 index 000000000000..37a2930177d7 --- /dev/null +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs @@ -0,0 +1,53 @@ +using System.Drawing; +using System.Threading.Tasks; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Private.Infrastructure; +using SkiaSharp; +using Uno.UI.RuntimeTests.Helpers; +using Size = Windows.Foundation.Size; +namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls; + +[TestClass] +[RunsOnUIThread] +public class Given_SKCanvasElement +{ + [TestMethod] + public async Task When_Clipped_Inside_ScrollViewer() + { + var SUT = new BlueFillSKCanvasElement + { + Height = 400, + Width = 400 + }; + + var border = new Border + { + BorderBrush = Microsoft.UI.Colors.Green, + Height = 400, + Child = new ScrollViewer + { + VerticalAlignment = VerticalAlignment.Top, + Height = 100, + Background = Microsoft.UI.Colors.Red, + Content = SUT + } + }; + + await UITestHelper.Load(border); + + var bitmap = await UITestHelper.ScreenShot(border); + + ImageAssert.HasColorInRectangle(bitmap, new Rectangle(0, 0, 400, 300), Microsoft.UI.Colors.Blue); + ImageAssert.DoesNotHaveColorInRectangle(bitmap, new Rectangle(0, 101, 400, 299), Microsoft.UI.Colors.Blue); + } + + private class BlueFillSKCanvasElement : SKCanvasElement + { + protected override void RenderOverride(SKCanvas canvas, Size area) + { + canvas.DrawRect(new SKRect(0, 0, (float)area.Width, (float)area.Height), new SKPaint { Color = SKColors.Blue }); + } + } +} diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs new file mode 100644 index 000000000000..d4dc8ce40189 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs @@ -0,0 +1,26 @@ +using System.Numerics; +using Microsoft.UI.Composition; +using SkiaSharp; + +namespace Microsoft.UI.Xaml.Controls; + +/// +/// A that exposes the ability to draw directly on the window using SkiaSharp. +/// +/// This is only available on skia-based targets. +public abstract partial class SKCanvasElement +{ + private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : Visual(compositor) + { + internal override void Paint(in PaintingSession session) + { + session.Canvas.Save(); + // clipping here guards against a naked canvas.Clear() call which would wipe out the entire window. + session.Canvas.ClipRect(new SKRect(0, 0, Size.X, Size.Y)); + owner.RenderOverride(session.Canvas, Size.ToSize()); + session.Canvas.Restore(); + } + + public void Invalidate() => Compositor.InvalidateRender(this); + } +} diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs new file mode 100644 index 000000000000..67247f69d27c --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs @@ -0,0 +1,23 @@ +#if !__SKIA__ +using System; +using Windows.Foundation; +using SkiaSharp; + +namespace Microsoft.UI.Xaml.Controls; + +public abstract partial class SKCanvasElement : FrameworkElement +{ + protected SKCanvasElement() + { + throw new PlatformNotSupportedException($"${nameof(SKCanvasElement)} is only available on skia targets."); + } + + public void Invalidate() { } + + protected abstract void RenderOverride(SKCanvas canvas, Size area); + + protected override Size MeasureOverride(Size availableSize) => availableSize; + + protected override Size ArrangeOverride(Size finalSize) => finalSize; +} +#endif diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs new file mode 100644 index 000000000000..b24f67adab39 --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs @@ -0,0 +1,77 @@ +using System; +using System.Numerics; +using Windows.Foundation; +using Microsoft.UI.Xaml.Hosting; +using SkiaSharp; + +namespace Microsoft.UI.Xaml.Controls; + +/// +/// A that exposes the ability to draw directly on the window using SkiaSharp. +/// +/// This is only available on skia-based targets. +public abstract partial class SKCanvasElement : FrameworkElement +{ + private readonly SKCanvasVisual _skiaVisual; + + protected SKCanvasElement() + { + _skiaVisual = new SKCanvasVisual(this, ElementCompositionPreview.GetElementVisual(this).Compositor); + Visual.Children.InsertAtTop(_skiaVisual); + + SizeChanged += OnSizeChanged; + } + + /// + /// Queue a rendering cycle that will call . + /// + public void Invalidate() => _skiaVisual.Invalidate(); + + /// + /// The SkiaSharp drawing logic goes here. + /// + /// The SKCanvas that should be drawn on. + /// The dimensions of the clipping area. + /// + /// When called, the is already set up such that the origin (0,0) is at the top-left of the clipping area. + /// Drawing outside this area (i.e. outside the (0, 0, area.Width, area.Height rectangle) will be clipped out. + /// + protected abstract void RenderOverride(SKCanvas canvas, Size area); + + /// + /// By default, uses all the given. Subclasses of + /// should override this method if they need something different. + /// + /// An exception will be thrown if availableSize is infinite (e.g. if inside a StackPanel). + protected override Size MeasureOverride(Size availableSize) + { + if (availableSize.Width == Double.PositiveInfinity || + availableSize.Height == Double.PositiveInfinity || + double.IsNaN(availableSize.Width) || + double.IsNaN(availableSize.Height)) + { + throw new ArgumentException($"{nameof(SKCanvasElement)} cannot be measured with infinite or NaN values, but received availableSize={availableSize}."); + } + + return availableSize; + } + + /// + /// By default, uses all the given. Subclasses of + /// should override this method if they need something different. + /// + /// An exception will be thrown if is infinite (e.g. if inside a StackPanel). + protected override Size ArrangeOverride(Size finalSize) + { + if (finalSize.Width == double.PositiveInfinity || + finalSize.Height == double.PositiveInfinity || + double.IsNaN(finalSize.Width) || + double.IsNaN(finalSize.Height)) + { + throw new ArgumentException($"{nameof(SKCanvasElement)} cannot be arranged with infinite or NaN values, but received finalSize={finalSize}."); + } + return finalSize; + } + + private void OnSizeChanged(object sender, SizeChangedEventArgs args) => _skiaVisual.Size = args.NewSize.ToVector2(); +} From 14d5199debf7b30475552cffa3ccf1e1f7a20c04 Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 12:40:14 +0300 Subject: [PATCH 02/10] chore: minimize SKCanvasElement's API surface --- .../SKCanvasElement.SKCanvasVisual.skia.cs | 4 +- .../Graphics/SKCanvasElement.crossruntime.cs | 6 --- .../UI/Xaml/Graphics/SKCanvasElement.skia.cs | 52 ++----------------- 3 files changed, 4 insertions(+), 58 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs index d4dc8ce40189..10a486173268 100644 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs @@ -10,7 +10,7 @@ namespace Microsoft.UI.Xaml.Controls; /// This is only available on skia-based targets. public abstract partial class SKCanvasElement { - private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : Visual(compositor) + private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : ShapeVisual(compositor) { internal override void Paint(in PaintingSession session) { @@ -20,7 +20,5 @@ internal override void Paint(in PaintingSession session) owner.RenderOverride(session.Canvas, Size.ToSize()); session.Canvas.Restore(); } - - public void Invalidate() => Compositor.InvalidateRender(this); } } diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs index 67247f69d27c..ec629c9f832a 100644 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs @@ -12,12 +12,6 @@ protected SKCanvasElement() throw new PlatformNotSupportedException($"${nameof(SKCanvasElement)} is only available on skia targets."); } - public void Invalidate() { } - protected abstract void RenderOverride(SKCanvas canvas, Size area); - - protected override Size MeasureOverride(Size availableSize) => availableSize; - - protected override Size ArrangeOverride(Size finalSize) => finalSize; } #endif diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs index b24f67adab39..9e2e562db2c7 100644 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs @@ -1,6 +1,5 @@ -using System; -using System.Numerics; using Windows.Foundation; +using Microsoft.UI.Composition; using Microsoft.UI.Xaml.Hosting; using SkiaSharp; @@ -12,20 +11,12 @@ namespace Microsoft.UI.Xaml.Controls; /// This is only available on skia-based targets. public abstract partial class SKCanvasElement : FrameworkElement { - private readonly SKCanvasVisual _skiaVisual; - - protected SKCanvasElement() - { - _skiaVisual = new SKCanvasVisual(this, ElementCompositionPreview.GetElementVisual(this).Compositor); - Visual.Children.InsertAtTop(_skiaVisual); - - SizeChanged += OnSizeChanged; - } + private protected override ShapeVisual CreateElementVisual() => new SKCanvasVisual(this, Compositor.GetSharedCompositor()); /// /// Queue a rendering cycle that will call . /// - public void Invalidate() => _skiaVisual.Invalidate(); + public void Invalidate() => _visual.Compositor.InvalidateRender(_visual); /// /// The SkiaSharp drawing logic goes here. @@ -37,41 +28,4 @@ protected SKCanvasElement() /// Drawing outside this area (i.e. outside the (0, 0, area.Width, area.Height rectangle) will be clipped out. /// protected abstract void RenderOverride(SKCanvas canvas, Size area); - - /// - /// By default, uses all the given. Subclasses of - /// should override this method if they need something different. - /// - /// An exception will be thrown if availableSize is infinite (e.g. if inside a StackPanel). - protected override Size MeasureOverride(Size availableSize) - { - if (availableSize.Width == Double.PositiveInfinity || - availableSize.Height == Double.PositiveInfinity || - double.IsNaN(availableSize.Width) || - double.IsNaN(availableSize.Height)) - { - throw new ArgumentException($"{nameof(SKCanvasElement)} cannot be measured with infinite or NaN values, but received availableSize={availableSize}."); - } - - return availableSize; - } - - /// - /// By default, uses all the given. Subclasses of - /// should override this method if they need something different. - /// - /// An exception will be thrown if is infinite (e.g. if inside a StackPanel). - protected override Size ArrangeOverride(Size finalSize) - { - if (finalSize.Width == double.PositiveInfinity || - finalSize.Height == double.PositiveInfinity || - double.IsNaN(finalSize.Width) || - double.IsNaN(finalSize.Height)) - { - throw new ArgumentException($"{nameof(SKCanvasElement)} cannot be arranged with infinite or NaN values, but received finalSize={finalSize}."); - } - return finalSize; - } - - private void OnSizeChanged(object sender, SizeChangedEventArgs args) => _skiaVisual.Size = args.NewSize.ToVector2(); } From 68491df8494c0b6811824393c1d5efb87dd2b5f6 Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 12:40:52 +0300 Subject: [PATCH 03/10] chore: remove canvas.Clear from SKCanvasElementImpl --- .../Windows_UI_Composition/SKCanvasElementImpl.skia.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs index dd3c99d8c3f6..914586de10b9 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs @@ -51,8 +51,6 @@ protected override void RenderOverride(SKCanvas canvas, Size area) // https://fiddle.skia.org/c/@shapes private void SkiaDrawing0(SKCanvas canvas) { - canvas.DrawColor(SKColors.White); - var paint = new SKPaint(); paint.Style = SKPaintStyle.Fill; paint.IsAntialias = true; @@ -80,8 +78,6 @@ private void SkiaDrawing0(SKCanvas canvas) // https://fiddle.skia.org/c/@bezier_curves private void SkiaDrawing1(SKCanvas canvas) { - canvas.DrawColor(SKColors.White); - var paint = new SKPaint(); paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 8; @@ -114,7 +110,6 @@ private void SkiaDrawing2(SKCanvas canvas) }; paint.Shader = SKShader.CreateLinearGradient(points[0], points[1], colors, SKShaderTileMode.Clamp); paint.IsAntialias = true; - canvas.Clear(SKColors.White); var path = Star(); canvas.DrawPath(path, paint); From ebe774f45913478d3e950e237f3c3e388756dc8b Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 12:57:22 +0300 Subject: [PATCH 04/10] chore: fix docs --- doc/articles/controls/SKCanvasElement.md | 45 +++++++++++-------- .../SKCanvasElement.SKCanvasVisual.skia.cs | 3 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/doc/articles/controls/SKCanvasElement.md b/doc/articles/controls/SKCanvasElement.md index 0c732c21e404..a3934f5f2800 100644 --- a/doc/articles/controls/SKCanvasElement.md +++ b/doc/articles/controls/SKCanvasElement.md @@ -6,10 +6,10 @@ uid: Uno.Controls.SKCanvasElement When creating an Uno Platform application, developers might want to create elaborate 2D graphics using a library such as [Skia](https://skia.org) or [Cairo](https://www.cairographics.org), rather than using, for example, a simple [Canvas](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.canvas). To support this use case, SkiaSharp comes with an [SKXamlCanvas](https://learn.microsoft.com/dotnet/api/skiasharp.views.windows.skxamlcanvas) element that allows for drawing in an area using SkiaSharp. -On Uno Platform Skia Desktop targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. This way, a lot of Skia functionally can be acquired "for free". For example, unlike `SKXamlCanvas` which doesn't support hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. +On Uno Platform Skia Desktop targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. Unlike `SKXamlCanvas` which doesn't support hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. > [!IMPORTANT] -> This functionality is only available on Skia Desktop (`netX.0-desktop`) targets. +> This functionality is only available on Skia targets. ## SKCanvasElement @@ -19,13 +19,11 @@ On Uno Platform Skia Desktop targets, we can utilize the pre-existing internal S protected abstract void RenderOverride(SKCanvas canvas, Size area); ``` -When adding your drawing logic in `RenderOverride` on the provided canvas, you can assume that the origin is already translated so that `0,0` is the origin of the visual, not the entire window. Drawing outside this area will be clipped. +When adding your drawing logic in `RenderOverride` on the provided canvas, you can assume that the origin is already translated so that `0,0` is the origin of the element, not the entire window. Drawing outside this area will be clipped. -Additionally, `SKCanvasElement` has an `Invalidate` method that can be used at any time to tell the Uno Platform runtime to redraw the visual, calling `RenderOverride` in the process. +Additionally, `SKCanvasElement` has an `Invalidate` method that can be used at any time to tell the Uno Platform runtime to redraw the window, calling `RenderOverride` in the process. -By default, an `SKCanvasElement` takes all the available space given to it in the `Measure` cycle. If you want to customize how much space the element takes, you can override its `MeasureOverride` and `ArrangeOverride` methods. - -Note that since an `SKCanvasElement` takes as much space as it can, it's not allowed to place an `SKCanvasElement` inside a `StackPanel`, a `Grid` with `Auto` sizing, or any other element that provides its child(ren) with infinite space. To work around this, you can explicitly set the `Width` and/or `Height` of the `SKCanvasElement`. +Since `SKCanvasElement` is just a FrameworkElement, controlling the dimensions of the drawing area is done by manipulating the layout of the element, e.g. by overriding MeasureOverride and ArrangeOverride. ## Full example @@ -43,7 +41,7 @@ XAML: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:skia="http://uno.ui/skia" xmlns:not_skia="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - mc:Ignorable="d skia not_skia" + mc:Ignorable="d skia" d:DesignHeight="300" d:DesignWidth="400"> @@ -65,21 +63,35 @@ Code-behind: ```csharp // SKCanvasElementExample.xaml.cs -public partial class SKCanvasElementExample : UserControl +using Uno.UI.Samples.Controls; +using Microsoft.UI.Xaml.Controls; + +namespace BlankApp { + public sealed partial class SKCanvasElement_Simple : UserControl + { #if __SKIA__ - public int MaxSampleIndex => SKCanvasElementImpl.SampleCount - 1; + public int MaxSampleIndex => SKCanvasElementImpl.SampleCount - 1; #endif - public SKCanvasElementExample() - { - this.InitializeComponent(); + public SKCanvasElement_Simple() + { + this.InitializeComponent(); + } } } ``` ```csharp // SKCanvasElementImpl.skia.cs <-- NOTICE the `.skia` +using System; +using Windows.Foundation; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using SkiaSharp; + +namespace BlankApp; + public class SKCanvasElementImpl : SKCanvasElement { public static int SampleCount => 3; @@ -104,7 +116,7 @@ public class SKCanvasElementImpl : SKCanvasElement protected override void RenderOverride(SKCanvas canvas, Size area) { var minDim = Math.Min(area.Width, area.Height); - // rescale to fit the given area, assuming each drawing takes is 260x260 + // rescale to fit the given area, assuming each drawing is 260x260 canvas.Scale((float)(minDim / 260), (float)(minDim / 260)); switch (Sample) @@ -124,8 +136,6 @@ public class SKCanvasElementImpl : SKCanvasElement // https://fiddle.skia.org/c/@shapes private void SkiaDrawing0(SKCanvas canvas) { - canvas.DrawColor(SKColors.White); - var paint = new SKPaint(); paint.Style = SKPaintStyle.Fill; paint.IsAntialias = true; @@ -153,8 +163,6 @@ public class SKCanvasElementImpl : SKCanvasElement // https://fiddle.skia.org/c/@bezier_curves private void SkiaDrawing1(SKCanvas canvas) { - canvas.DrawColor(SKColors.White); - var paint = new SKPaint(); paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 8; @@ -187,7 +195,6 @@ public class SKCanvasElementImpl : SKCanvasElement }; paint.Shader = SKShader.CreateLinearGradient(points[0], points[1], colors, SKShaderTileMode.Clamp); paint.IsAntialias = true; - canvas.Clear(SKColors.White); var path = Star(); canvas.DrawPath(path, paint); diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs index 10a486173268..1d8a7d572ca6 100644 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs +++ b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs @@ -14,8 +14,9 @@ private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : Sha { internal override void Paint(in PaintingSession session) { + // We save and restore the canvas state ourselves so that the inheritor doesn't accidentally forget to. session.Canvas.Save(); - // clipping here guards against a naked canvas.Clear() call which would wipe out the entire window. + // clipping here guarantees that drawing doesn't get outside the intended area session.Canvas.ClipRect(new SKRect(0, 0, Size.X, Size.Y)); owner.RenderOverride(session.Canvas, Size.ToSize()); session.Canvas.Restore(); From f20285698487fa6476082cd2b815638a02cd302b Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 15:41:35 +0300 Subject: [PATCH 05/10] chore: packaging --- .../SKCanvasElement.cs} | 22 +++++++-- .../Uno.WinUI.Graphics2DSK.csproj | 29 ++++++++++++ .../Uno.WinUI.Graphics3DGL.csproj | 12 +---- .../SamplesApp.Skia/SamplesApp.Skia.csproj | 1 + .../SKCanvasElementImpl.skia.cs | 2 +- .../UnoIslandsSamplesApp.Skia.csproj | 1 + src/Uno.UI-Skia-only.slnf | 5 ++- src/Uno.UI.Composition/AssemblyInfo.cs | 2 + .../Given_SKCanvasElement.skia.cs | 3 +- .../Uno.UI.RuntimeTests.Skia.csproj | 4 ++ src/Uno.UI.sln | 45 +++++++++++++++++++ src/Uno.UI/AssemblyInfo.cs | 1 + .../SKCanvasElement.SKCanvasVisual.skia.cs | 25 ----------- .../Graphics/SKCanvasElement.crossruntime.cs | 17 ------- 14 files changed, 107 insertions(+), 62 deletions(-) rename src/{Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs => AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs} (60%) create mode 100644 src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj delete mode 100644 src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs delete mode 100644 src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs b/src/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs similarity index 60% rename from src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs rename to src/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs index 9e2e562db2c7..c3ec10315a0c 100644 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.skia.cs +++ b/src/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs @@ -1,15 +1,16 @@ +using System.Numerics; using Windows.Foundation; using Microsoft.UI.Composition; -using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml; using SkiaSharp; -namespace Microsoft.UI.Xaml.Controls; +namespace Uno.WinUI.Graphics2DSK; /// /// A that exposes the ability to draw directly on the window using SkiaSharp. /// /// This is only available on skia-based targets. -public abstract partial class SKCanvasElement : FrameworkElement +public abstract class SKCanvasElement : FrameworkElement { private protected override ShapeVisual CreateElementVisual() => new SKCanvasVisual(this, Compositor.GetSharedCompositor()); @@ -25,7 +26,20 @@ public abstract partial class SKCanvasElement : FrameworkElement /// The dimensions of the clipping area. /// /// When called, the is already set up such that the origin (0,0) is at the top-left of the clipping area. - /// Drawing outside this area (i.e. outside the (0, 0, area.Width, area.Height rectangle) will be clipped out. + /// Drawing outside this area (i.e. outside the (0, 0, area.Width, area.Height) rectangle will be clipped out. /// protected abstract void RenderOverride(SKCanvas canvas, Size area); + + private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : ShapeVisual(compositor) + { + internal override void Paint(in PaintingSession session) + { + // We save and restore the canvas state ourselves so that the inheritor doesn't accidentally forget to. + session.Canvas.Save(); + // clipping here guarantees that drawing doesn't get outside the intended area + session.Canvas.ClipRect(new SKRect(0, 0, Size.X, Size.Y)); + owner.RenderOverride(session.Canvas, Size.ToSize()); + session.Canvas.Restore(); + } + } } diff --git a/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj new file mode 100644 index 000000000000..80205b3bc426 --- /dev/null +++ b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj @@ -0,0 +1,29 @@ + + + $(NetSkiaPreviousAndCurrent) + + Uno.WinUI.Graphics2DSK + true + + enable + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AddIns/Uno.WinUI.Graphics3DGL/Uno.WinUI.Graphics3DGL.csproj b/src/AddIns/Uno.WinUI.Graphics3DGL/Uno.WinUI.Graphics3DGL.csproj index 6e7ede31d5c4..a7eeb6a451e1 100644 --- a/src/AddIns/Uno.WinUI.Graphics3DGL/Uno.WinUI.Graphics3DGL.csproj +++ b/src/AddIns/Uno.WinUI.Graphics3DGL/Uno.WinUI.Graphics3DGL.csproj @@ -3,7 +3,7 @@ $(NetSkiaPreviousAndCurrent) $(NetUWPOrWinUI) - Uno.WinUI.GLCanvasElement + Uno.WinUI.Graphics3DGL true enable @@ -51,14 +51,4 @@ - - - - - - - diff --git a/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj b/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj index bbc751c691f6..9e6ac5947771 100644 --- a/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj +++ b/src/SamplesApp/SamplesApp.Skia/SamplesApp.Skia.csproj @@ -33,6 +33,7 @@ + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs index 914586de10b9..5f03f386190f 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs @@ -2,7 +2,7 @@ using System; using Windows.Foundation; using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; +using Uno.WinUI.Graphics2DSK; using SkiaSharp; namespace UITests.Shared.Windows_UI_Composition; diff --git a/src/SamplesApp/UnoIslandsSamplesApp.Skia/UnoIslandsSamplesApp.Skia.csproj b/src/SamplesApp/UnoIslandsSamplesApp.Skia/UnoIslandsSamplesApp.Skia.csproj index 726435ce45e0..8ac3b125cf63 100644 --- a/src/SamplesApp/UnoIslandsSamplesApp.Skia/UnoIslandsSamplesApp.Skia.csproj +++ b/src/SamplesApp/UnoIslandsSamplesApp.Skia/UnoIslandsSamplesApp.Skia.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Uno.UI-Skia-only.slnf b/src/Uno.UI-Skia-only.slnf index 1180c2e0875a..9f8b8e555e01 100644 --- a/src/Uno.UI-Skia-only.slnf +++ b/src/Uno.UI-Skia-only.slnf @@ -3,9 +3,10 @@ "path": "Uno.UI.sln", "projects": [ "AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.Skia.csproj", - "AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Skia.csproj", "AddIns\\Uno.UI.MediaPlayer.Skia.Gtk\\Uno.UI.MediaPlayer.Skia.Gtk.csproj", + "AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Skia.csproj", "AddIns\\Uno.UI.Svg\\Uno.UI.Svg.Skia.csproj", + "AddIns\\Uno.WinUI.Graphics2DSK\\Uno.WinUI.Graphics2DSK.csproj", "AddIns\\Uno.WinUI.Graphics3DGL\\Uno.WinUI.Graphics3DGL.csproj", "SamplesApp\\Benchmarks.Shared\\SamplesApp.Benchmarks.shproj", "SamplesApp\\SamplesApp.Shared\\SamplesApp.Shared.shproj", @@ -59,4 +60,4 @@ "Uno.UWP\\Uno.Skia.csproj" ] } -} +} \ No newline at end of file diff --git a/src/Uno.UI.Composition/AssemblyInfo.cs b/src/Uno.UI.Composition/AssemblyInfo.cs index 8d471f7539da..296280348d7e 100644 --- a/src/Uno.UI.Composition/AssemblyInfo.cs +++ b/src/Uno.UI.Composition/AssemblyInfo.cs @@ -16,6 +16,8 @@ [assembly: InternalsVisibleTo("SamplesApp.Wasm")] [assembly: InternalsVisibleTo("SamplesApp.Skia")] +[assembly: InternalsVisibleTo("Uno.WinUI.Graphics2DSK")] + [assembly: InternalsVisibleTo("UnoIslandsSamplesApp")] [assembly: InternalsVisibleTo("UnoIslandsSamplesApp.Skia")] [assembly: System.Reflection.AssemblyMetadata("IsTrimmable", "True")] diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs index 37a2930177d7..d3734ead1021 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs @@ -1,11 +1,10 @@ using System.Drawing; using System.Threading.Tasks; -using Microsoft.UI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using Private.Infrastructure; using SkiaSharp; using Uno.UI.RuntimeTests.Helpers; +using Uno.WinUI.Graphics2DSK; using Size = Windows.Foundation.Size; namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls; diff --git a/src/Uno.UI.RuntimeTests/Uno.UI.RuntimeTests.Skia.csproj b/src/Uno.UI.RuntimeTests/Uno.UI.RuntimeTests.Skia.csproj index 258eb3ecf2e6..13dc8958b49b 100644 --- a/src/Uno.UI.RuntimeTests/Uno.UI.RuntimeTests.Skia.csproj +++ b/src/Uno.UI.RuntimeTests/Uno.UI.RuntimeTests.Skia.csproj @@ -68,6 +68,10 @@ + + + + $(MSBuildThisFileDirectory)..\SourceGenerators\Uno.UI.Tasks\bin\$(Configuration)_Shadow diff --git a/src/Uno.UI.sln b/src/Uno.UI.sln index 599d35388e04..af5f896c8217 100644 --- a/src/Uno.UI.sln +++ b/src/Uno.UI.sln @@ -317,6 +317,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.UI.Runtime.Skia.X11", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.WinUI.Graphics3DGL", "AddIns\Uno.WinUI.Graphics3DGL\Uno.WinUI.Graphics3DGL.csproj", "{0F62DA75-6AD9-4F58-B69C-D63CA9053E34}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.WinUI.Graphics2DSK", "AddIns\Uno.WinUI.Graphics2DSK\Uno.WinUI.Graphics2DSK.csproj", "{B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -5772,6 +5774,48 @@ Global {0F62DA75-6AD9-4F58-B69C-D63CA9053E34}.Release|x64.Build.0 = Release|Any CPU {0F62DA75-6AD9-4F58-B69C-D63CA9053E34}.Release|x86.ActiveCfg = Release|Any CPU {0F62DA75-6AD9-4F58-B69C-D63CA9053E34}.Release|x86.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|ARM.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|ARM64.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|iPhone.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|x64.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Debug|x86.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|Any CPU.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|Any CPU.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|ARM.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|ARM.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|ARM64.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|ARM64.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|iPhone.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|iPhone.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|iPhoneSimulator.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|x64.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|x64.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|x86.ActiveCfg = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release_NoSamples|x86.Build.0 = Debug|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|Any CPU.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|ARM.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|ARM.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|ARM64.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|ARM64.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|iPhone.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|iPhone.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|x64.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|x64.Build.0 = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|x86.ActiveCfg = Release|Any CPU + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5916,6 +5960,7 @@ Global {93E6D033-52E2-4C2B-A715-A9FC1F609E3D} = {995C1054-AB61-42EE-9A17-C32155BD6D13} {37441DE3-088B-4B63-A1E2-E70E39BF4222} = {416684CF-A4E3-4079-B380-3FF0B00E433C} {0F62DA75-6AD9-4F58-B69C-D63CA9053E34} = {E872CD33-A455-4DC4-8A87-5FEEC1C39A44} + {B1A17B3C-8B9D-40C8-8DBF-37CAAC7EB55E} = {E872CD33-A455-4DC4-8A87-5FEEC1C39A44} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9B2608F4-D82B-4B72-B399-33E822DF01D0} diff --git a/src/Uno.UI/AssemblyInfo.cs b/src/Uno.UI/AssemblyInfo.cs index 8b4faf31a0d1..0c1a21251bd8 100644 --- a/src/Uno.UI/AssemblyInfo.cs +++ b/src/Uno.UI/AssemblyInfo.cs @@ -30,6 +30,7 @@ [assembly: InternalsVisibleTo("Uno.UI.MediaPlayer.WebAssembly")] [assembly: InternalsVisibleTo("Uno.WinUI.Graphics3DGL")] +[assembly: InternalsVisibleTo("Uno.WinUI.Graphics2DSK")] [assembly: AssemblyMetadata("IsTrimmable", "True")] diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs deleted file mode 100644 index 1d8a7d572ca6..000000000000 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.SKCanvasVisual.skia.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Numerics; -using Microsoft.UI.Composition; -using SkiaSharp; - -namespace Microsoft.UI.Xaml.Controls; - -/// -/// A that exposes the ability to draw directly on the window using SkiaSharp. -/// -/// This is only available on skia-based targets. -public abstract partial class SKCanvasElement -{ - private class SKCanvasVisual(SKCanvasElement owner, Compositor compositor) : ShapeVisual(compositor) - { - internal override void Paint(in PaintingSession session) - { - // We save and restore the canvas state ourselves so that the inheritor doesn't accidentally forget to. - session.Canvas.Save(); - // clipping here guarantees that drawing doesn't get outside the intended area - session.Canvas.ClipRect(new SKRect(0, 0, Size.X, Size.Y)); - owner.RenderOverride(session.Canvas, Size.ToSize()); - session.Canvas.Restore(); - } - } -} diff --git a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs b/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs deleted file mode 100644 index ec629c9f832a..000000000000 --- a/src/Uno.UI/UI/Xaml/Graphics/SKCanvasElement.crossruntime.cs +++ /dev/null @@ -1,17 +0,0 @@ -#if !__SKIA__ -using System; -using Windows.Foundation; -using SkiaSharp; - -namespace Microsoft.UI.Xaml.Controls; - -public abstract partial class SKCanvasElement : FrameworkElement -{ - protected SKCanvasElement() - { - throw new PlatformNotSupportedException($"${nameof(SKCanvasElement)} is only available on skia targets."); - } - - protected abstract void RenderOverride(SKCanvas canvas, Size area); -} -#endif From 5ae9445454b81d5f745c798d1d1fa04897b22528 Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 11 Sep 2024 16:00:23 +0300 Subject: [PATCH 06/10] chore: Uno.Sdk and CI --- build/filters/Uno.UI-packages-skia.slnf | 1 + src/Uno.Sdk/packages.json | 3 ++- .../Uno.Implicit.Packages.ProjectSystem.Desktop.targets | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build/filters/Uno.UI-packages-skia.slnf b/build/filters/Uno.UI-packages-skia.slnf index acc27dca11a9..d464ce5a38ab 100644 --- a/build/filters/Uno.UI-packages-skia.slnf +++ b/build/filters/Uno.UI-packages-skia.slnf @@ -7,6 +7,7 @@ "AddIns\\Uno.UI.MediaPlayer.Skia.Gtk\\Uno.UI.MediaPlayer.Skia.Gtk.csproj", "AddIns\\Uno.UI.Svg\\Uno.UI.Svg.Skia.csproj", "AddIns\\Uno.WinUI.Graphics3DGL\\Uno.WinUI.Graphics3DGL.csproj", + "AddIns\\Uno.WinUI.Graphics2DSK\\Uno.WinUI.Graphics2DSK.csproj", "SourceGenerators\\System.Xaml\\Uno.Xaml.csproj", "SourceGenerators\\Uno.UI.SourceGenerators.Internal\\Uno.UI.SourceGenerators.Internal.csproj", "SourceGenerators\\Uno.UI.SourceGenerators\\Uno.UI.SourceGenerators.csproj", diff --git a/src/Uno.Sdk/packages.json b/src/Uno.Sdk/packages.json index 1e9d1d097af6..78a86564265e 100644 --- a/src/Uno.Sdk/packages.json +++ b/src/Uno.Sdk/packages.json @@ -20,7 +20,8 @@ "Uno.WinUI.WebAssembly", "Uno.WinUI.Runtime.WebAssembly", "Uno.WinUI.MediaPlayer.WebAssembly", - "Uno.WinUI.Graphics3DGL" + "Uno.WinUI.Graphics3DGL", + "Uno.WinUI.Graphics2DSK" ] }, { diff --git a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Desktop.targets b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Desktop.targets index 81fe145ac34d..189ee90959c0 100644 --- a/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Desktop.targets +++ b/src/Uno.Sdk/targets/Uno.Implicit.Packages.ProjectSystem.Desktop.targets @@ -10,6 +10,10 @@ <_UnoProjectSystemPackageReference Include="Uno.WinUI.Graphics3DGL" ProjectSystem="true" /> + + <_UnoProjectSystemPackageReference Include="Uno.WinUI.Graphics2DSK" ProjectSystem="true" /> + + <_UnoProjectSystemPackageReference Include="Uno.WinUI.Skia.Linux.FrameBuffer" ProjectSystem="true" /> <_UnoProjectSystemPackageReference Include="Uno.WinUI.Skia.MacOS" ProjectSystem="true" /> From c9ad15ca5a5e1e40f55e4a6bae4491ac20fb0309 Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 18 Sep 2024 13:37:26 +0300 Subject: [PATCH 07/10] chore: fix csproj --- .../Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj index 80205b3bc426..5a08e6e74f7b 100644 --- a/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj +++ b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj @@ -16,14 +16,9 @@ - - - - - From 0e7c55107e7591f2b5ca9565c9af876e3462a10e Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Wed, 18 Sep 2024 13:38:20 +0300 Subject: [PATCH 08/10] chore: minor doc touches --- doc/articles/controls/SKCanvasElement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/controls/SKCanvasElement.md b/doc/articles/controls/SKCanvasElement.md index a3934f5f2800..bf9e6712a96d 100644 --- a/doc/articles/controls/SKCanvasElement.md +++ b/doc/articles/controls/SKCanvasElement.md @@ -6,7 +6,7 @@ uid: Uno.Controls.SKCanvasElement When creating an Uno Platform application, developers might want to create elaborate 2D graphics using a library such as [Skia](https://skia.org) or [Cairo](https://www.cairographics.org), rather than using, for example, a simple [Canvas](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.canvas). To support this use case, SkiaSharp comes with an [SKXamlCanvas](https://learn.microsoft.com/dotnet/api/skiasharp.views.windows.skxamlcanvas) element that allows for drawing in an area using SkiaSharp. -On Uno Platform Skia Desktop targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. Unlike `SKXamlCanvas` which doesn't support hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. +On Uno Platform Skia targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. Unlike `SKXamlCanvas` which doesn't support hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. > [!IMPORTANT] > This functionality is only available on Skia targets. From 6a5cf14844f4c27ee73dbd8fbf0cf8371622fb7e Mon Sep 17 00:00:00 2001 From: Ramez Ragaa Date: Thu, 19 Sep 2024 12:52:16 +0300 Subject: [PATCH 09/10] chore: don't build Graphics2DSK on uwp --- src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj index 5a08e6e74f7b..147757aeb0a2 100644 --- a/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj +++ b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj @@ -3,7 +3,7 @@ $(NetSkiaPreviousAndCurrent) Uno.WinUI.Graphics2DSK - true + true enable From a769608510c9832e43ef9743624ea554e1992bfa Mon Sep 17 00:00:00 2001 From: Ramez Ragaa <66218781+ramezgerges@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:43:47 +0300 Subject: [PATCH 10/10] chore: doc/articles/controls/SKCanvasElement.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jérôme Laban --- doc/articles/controls/SKCanvasElement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/controls/SKCanvasElement.md b/doc/articles/controls/SKCanvasElement.md index bf9e6712a96d..548d9c8abe9d 100644 --- a/doc/articles/controls/SKCanvasElement.md +++ b/doc/articles/controls/SKCanvasElement.md @@ -6,7 +6,7 @@ uid: Uno.Controls.SKCanvasElement When creating an Uno Platform application, developers might want to create elaborate 2D graphics using a library such as [Skia](https://skia.org) or [Cairo](https://www.cairographics.org), rather than using, for example, a simple [Canvas](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.canvas). To support this use case, SkiaSharp comes with an [SKXamlCanvas](https://learn.microsoft.com/dotnet/api/skiasharp.views.windows.skxamlcanvas) element that allows for drawing in an area using SkiaSharp. -On Uno Platform Skia targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. Unlike `SKXamlCanvas` which doesn't support hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. +On Uno Platform Skia targets, we can utilize the pre-existing internal Skia canvas used to render the application window instead of creating additional Skia surfaces. Unlike `SKXamlCanvas` which doesn't support yet hardware acceleration on Skia targets, hardware acceleration comes out of the box if the Uno application is already using OpenGL to render. Moreover, `SKXamlCanvas` has to make additional buffer copying, which can be skipped with this implementation. > [!IMPORTANT] > This functionality is only available on Skia targets.