Browse Source

SkiaSharp updates, SkiaSharp 3.0 render tests enabled (#15255)

* Use 2.88.8-preview.1.1 skiasharp

* Use SKImageFilter directly, as we don't need compat anymore

* Add SkiaSharp 3 render tests

* Enable SkiaSharp 3 tests on CI

* Add IncludeLinuxSkia in Skia tests

* Update Skia version, remove SkiaCompat

* Remove Skia3 test project, reuse single Skia render test project, so they won't conflict
Max Katz 1 year ago
parent
commit
8c7754ff17

+ 3 - 3
build/HarfBuzzSharp.props

@@ -1,7 +1,7 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <PackageReference Include="HarfBuzzSharp" Version="7.3.0" />
-    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0" />
-    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="7.3.0" />
+    <PackageReference Include="HarfBuzzSharp" Version="7.3.0.2" />
+    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.2" />
+    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="7.3.0.2" />
   </ItemGroup>
 </Project>

+ 9 - 4
build/SkiaSharp.props

@@ -1,7 +1,12 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <PackageReference Include="SkiaSharp" Version="2.88.7" />
-    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
-    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.7" />
+  <ItemGroup Condition="'$(AvsIncludeSkiaSharp3)' != 'true'">
+    <PackageReference Include="SkiaSharp" Version="2.88.8" />
+    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
+    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.8" />
+  </ItemGroup>
+  <ItemGroup Condition="'$(AvsIncludeSkiaSharp3)' == 'true'">
+    <PackageReference Include="SkiaSharp" Version="3.0.0-preview.3.1" />
+    <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="3.0.0-preview.3.1" />
+    <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="3.0.0-preview.3.1" />
   </ItemGroup>
 </Project>

+ 1 - 0
src/Avalonia.Base/Avalonia.Base.csproj

@@ -49,6 +49,7 @@
     <InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />

+ 1 - 0
src/Avalonia.Controls/Avalonia.Controls.csproj

@@ -14,6 +14,7 @@
     <InternalsVisibleTo Include="Avalonia.Controls.ItemsRepeater, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Markup.UnitTests, PublicKey=$(AvaloniaPublicKey)" />

+ 1 - 0
src/Skia/Avalonia.Skia/Avalonia.Skia.csproj

@@ -20,6 +20,7 @@
 
   <ItemGroup Label="InternalsVisibleTo">
     <InternalsVisibleTo Include="Avalonia.Skia.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.Skia3.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
   </ItemGroup>

+ 0 - 34
src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKCanvas.cs

@@ -1,34 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using SkiaSharp;
-
-namespace Avalonia.Skia;
-
-internal static partial class SkiaCompat
-{
-    public static void SetMatrix(SKCanvas canvas, in SKMatrix matrix)
-    {
-        if (s_isSkiaSharp3)
-        {
-#if NET8_0_OR_GREATER
-            NewCanvasSetMatrix(canvas, matrix);
-#else
-            throw UnsupportedException();
-#endif
-        }
-        else
-        {
-            LegacyCall(canvas, matrix);
-
-            static void LegacyCall(SKCanvas canvas, in SKMatrix matrix)
-            {
-                canvas.SetMatrix(matrix);
-            }
-        }
-    }
-
-#if NET8_0_OR_GREATER
-    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetMatrix")]
-    private static extern void NewCanvasSetMatrix(SKCanvas canvas, in SKMatrix matrix);
-#endif
-}

+ 0 - 57
src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKImageFilter.cs

@@ -1,57 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using SkiaSharp;
-
-namespace Avalonia.Skia;
-
-internal static partial class SkiaCompat
-{
-    public static SKImageFilter CreateBlur(float sigmaX, float sigmaY)
-    {
-        if (s_isSkiaSharp3)
-        {
-#if NET8_0_OR_GREATER
-            return NewSKImageFilterCreateBlur(null, sigmaX, sigmaY);
-#else
-            throw UnsupportedException();
-#endif
-        }
-        else
-        {
-            return LegacyBlurCall(sigmaX, sigmaY);
-
-            static SKImageFilter LegacyBlurCall(float sigmaX, float sigmaY) =>
-                SKImageFilter.CreateBlur(sigmaX, sigmaY);
-        }
-    }
-
-    public static SKImageFilter CreateDropShadow(float dropOffsetX, float dropOffsetY, float sigma, float f,
-        SKColor color)
-    {
-        if (s_isSkiaSharp3)
-        {
-#if NET8_0_OR_GREATER
-            return NewSKImageFilterCreateDropShadow(null!, dropOffsetX, dropOffsetY, sigma, f, color);
-#else
-            throw UnsupportedException();
-#endif
-        }
-        else
-        {
-            return LegacyDropShadowCall(dropOffsetX, dropOffsetY, sigma, f, color);
-
-            static SKImageFilter LegacyDropShadowCall(float dropOffsetX, float dropOffsetY, float sigma, float f, SKColor color) =>
-                SKImageFilter.CreateDropShadow(dropOffsetX, dropOffsetY, sigma, f, color);
-        }
-    }
-
-#if NET8_0_OR_GREATER
-    // See https://github.com/dotnet/runtime/issues/90081 why we need `SKImageFilter _`
-    [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CreateBlur")]
-    private static extern SKImageFilter NewSKImageFilterCreateBlur(SKImageFilter? _, float sigmaX, float sigmaY);
-
-    [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CreateDropShadow")]
-    private static extern SKImageFilter NewSKImageFilterCreateDropShadow(SKImageFilter? _, float dropOffsetX,
-        float dropOffsetY, float sigma, float f, SKColor color);
-#endif
-}

+ 0 - 32
src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKPath.cs

@@ -1,32 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using SkiaSharp;
-
-namespace Avalonia.Skia;
-
-internal static partial class SkiaCompat
-{
-    public static void Transform(SKPath path, in SKMatrix matrix)
-    {
-        if (s_isSkiaSharp3)
-        {
-#if NET8_0_OR_GREATER
-            NewPathTransform(path, matrix);
-#else
-            throw UnsupportedException();
-#endif
-        }
-        else
-        {
-            LegacyCall(path, matrix);
-
-            static void LegacyCall(SKPath path, in SKMatrix matrix) =>
-                path.Transform(matrix);
-        }
-    }
-
-#if NET8_0_OR_GREATER
-    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Transform")]
-    private static extern void NewPathTransform(SKPath path, in SKMatrix matrix);
-#endif
-}

+ 0 - 16
src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.cs

@@ -1,16 +0,0 @@
-using System;
-using SkiaSharp;
-
-namespace Avalonia.Skia;
-
-internal static partial class SkiaCompat
-{
-    private static readonly bool s_isSkiaSharp3 = typeof(SKPaint).Assembly.GetName().Version?.Major == 3;
-
-#if !NET8_0_OR_GREATER
-    private static Exception UnsupportedException()
-    {
-        return new InvalidOperationException("Avalonia doesn't support SkiaSharp 3.0 on .NET 7 and older. Please upgrade to .NET 8.");
-    }
-#endif
-}

+ 3 - 3
src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs

@@ -30,7 +30,7 @@ partial class DrawingContextImpl
             if (blur.Radius <= 0)
                 return null;
             var sigma = SkBlurRadiusToSigma(blur.Radius);
-            return SkiaCompat.CreateBlur(sigma, sigma);
+            return SKImageFilter.CreateBlur(sigma, sigma);
         }
 
         if (effect is IDropShadowEffect drop)
@@ -41,10 +41,10 @@ partial class DrawingContextImpl
                 alpha *= _currentOpacity;
             var color = new SKColor(drop.Color.R, drop.Color.G, drop.Color.B, (byte)Math.Max(0, Math.Min(255, alpha)));
 
-            return SkiaCompat.CreateDropShadow((float)drop.OffsetX, (float)drop.OffsetY, sigma, sigma, color);
+            return SKImageFilter.CreateDropShadow((float)drop.OffsetX, (float)drop.OffsetY, sigma, sigma, color);
         }
 
         return null;
     }
     
-}
+}

+ 3 - 3
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -139,7 +139,7 @@ namespace Avalonia.Skia
                 {
                     if (!_isDisposed)
                     {
-                        SkiaCompat.SetMatrix(_context.Canvas, _revertTransform);
+                        _context.Canvas.SetMatrix(_revertTransform);
                         _context._leased = false;
                         _isDisposed = true;
                     }
@@ -328,7 +328,7 @@ namespace Avalonia.Skia
             {
                 var ac = shadow.Color;
 
-                var filter = SkiaCompat.CreateBlur(SkBlurRadiusToSigma(shadow.Blur), SkBlurRadiusToSigma(shadow.Blur));
+                var filter = SKImageFilter.CreateBlur(SkBlurRadiusToSigma(shadow.Blur), SkBlurRadiusToSigma(shadow.Blur));
                 var color = new SKColor(ac.R, ac.G, ac.B, (byte)(ac.A * opacity));
 
                 paint.Reset();
@@ -860,7 +860,7 @@ namespace Avalonia.Skia
                     transform *= _postTransform.Value;
                 }
 
-                SkiaCompat.SetMatrix(Canvas, transform.ToSKMatrix());
+                Canvas.SetMatrix(transform.ToSKMatrix());
             }
         }
 

+ 1 - 1
src/Skia/Avalonia.Skia/GlyphRunImpl.cs

@@ -117,7 +117,7 @@ namespace Avalonia.Skia
                 runBuffer.SetPositions(_glyphPositions);
                 runBuffer.SetGlyphs(_glyphIndices);
 
-                var textBlob = builder.Build();
+                var textBlob = builder.Build()!;
 
                 SKTextBlobBuilderCache.Shared.Return(builder);
 

+ 1 - 1
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@@ -157,7 +157,7 @@ namespace Avalonia.Skia
                 var oldMatrix = context.Canvas.TotalMatrix;
                 context.Canvas.ResetMatrix();
                 _surface.Surface.Draw(context.Canvas, 0, 0, null);
-                SkiaCompat.SetMatrix(context.Canvas, oldMatrix);
+                context.Canvas.SetMatrix(oldMatrix);
             }
         }
 

+ 2 - 2
src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs

@@ -21,7 +21,7 @@ namespace Avalonia.Skia
 
             var transformedPath = StrokePath =  source.StrokePath.Clone();
             if (transformedPath is not null)
-                SkiaCompat.Transform(transformedPath, matrix);
+                transformedPath.Transform(matrix);
 
             Bounds = transformedPath?.TightBounds.ToAvaloniaRect() ?? default;
             
@@ -30,7 +30,7 @@ namespace Avalonia.Skia
             else if (source.FillPath != null)
             {
                 FillPath = transformedPath = source.FillPath.Clone();
-                SkiaCompat.Transform(transformedPath, matrix);
+                transformedPath.Transform(matrix);
             }
         }
 

+ 10 - 2
tests/Avalonia.RenderTests/TestBase.cs

@@ -42,6 +42,14 @@ namespace Avalonia.Direct2D1.RenderTests
 #endif
         public static FontFamily TestFontFamily = new FontFamily(s_fontUri);
 
+#if AVALONIA_SKIA3
+        // TODO: investigate why output is different.
+        // Most likely we need to use new SKSamplingOptions API, as old filters are broken with SKBitmap.
+        private const double AllowedError = 0.15;
+#else
+        private const double AllowedError = 0.022;
+#endif
+
         public TestBase(string outputPath)
         {
             outputPath = outputPath.Replace('\\', Path.DirectorySeparatorChar);
@@ -89,7 +97,7 @@ namespace Avalonia.Direct2D1.RenderTests
                 if (!skipImmediate)
                 {
                     var immediateError = TestRenderHelper.CompareImages(immediate!, expected);
-                    if (immediateError > 0.022)
+                    if (immediateError > AllowedError)
                     {
                         Assert.True(false, immediatePath + ": Error = " + immediateError);
                     }
@@ -98,7 +106,7 @@ namespace Avalonia.Direct2D1.RenderTests
                 if (!skipCompositor)
                 {
                     var compositedError = TestRenderHelper.CompareImages(composited!, expected);
-                    if (compositedError > 0.022)
+                    if (compositedError > AllowedError)
                     {
                         Assert.True(false, compositedPath + ": Error = " + compositedError);
                     }

+ 8 - 1
tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj

@@ -1,8 +1,15 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
-    <DefineConstants>AVALONIA_SKIA;AVALONIA_SKIA_SKIP_FAIL</DefineConstants>
+    <DefineConstants>$(DefineConstants);AVALONIA_SKIA</DefineConstants>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <IncludeLinuxSkia>true</IncludeLinuxSkia>
+    <IncludeWasmSkia>true</IncludeWasmSkia>
+
+<!--    <AvaSkiaRenderTestsIncludeSkiaSharp3>true</AvaSkiaRenderTestsIncludeSkiaSharp3>-->
+    <AvsIncludeSkiaSharp3 Condition="'$(AvsIncludeSkiaSharp3)' == '' AND '$(AvaSkiaRenderTestsIncludeSkiaSharp3)' == 'true'">true</AvsIncludeSkiaSharp3>
+    <DefineConstants Condition="'$(AvsIncludeSkiaSharp3)' == 'true'">$(DefineConstants);AVALONIA_SKIA3</DefineConstants>
+    <DefineConstants Condition="'$(AvsIncludeSkiaSharp3)' != 'true'">$(DefineConstants);AVALONIA_SKIA2</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="..\Avalonia.RenderTests\**\*.cs" />