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/doc/articles/controls/SKCanvasElement.md b/doc/articles/controls/SKCanvasElement.md
index 0c732c21e404..548d9c8abe9d 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 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 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/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs b/src/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs
new file mode 100644
index 000000000000..c3ec10315a0c
--- /dev/null
+++ b/src/AddIns/Uno.WinUI.Graphics2DSK/SKCanvasElement.cs
@@ -0,0 +1,45 @@
+using System.Numerics;
+using Windows.Foundation;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml;
+using SkiaSharp;
+
+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 class SKCanvasElement : FrameworkElement
+{
+ private protected override ShapeVisual CreateElementVisual() => new SKCanvasVisual(this, Compositor.GetSharedCompositor());
+
+ ///
+ /// Queue a rendering cycle that will call .
+ ///
+ public void Invalidate() => _visual.Compositor.InvalidateRender(_visual);
+
+ ///
+ /// 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);
+
+ 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..147757aeb0a2
--- /dev/null
+++ b/src/AddIns/Uno.WinUI.Graphics2DSK/Uno.WinUI.Graphics2DSK.csproj
@@ -0,0 +1,24 @@
+
+
+ $(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/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..5f03f386190f
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/SKCanvasElementImpl.skia.cs
@@ -0,0 +1,131 @@
+#if __SKIA__
+using System;
+using Windows.Foundation;
+using Microsoft.UI.Xaml;
+using Uno.WinUI.Graphics2DSK;
+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)
+ {
+ 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)
+ {
+ 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;
+ 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/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.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" />
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
new file mode 100644
index 000000000000..d3734ead1021
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_SKCanvasElement.skia.cs
@@ -0,0 +1,52 @@
+using System.Drawing;
+using System.Threading.Tasks;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+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;
+
+[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.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")]