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
release/11.1.0-beta2
Max Katz 2 years ago
parent
commit
f95ac8ffd5
  1. 6
      build/HarfBuzzSharp.props
  2. 13
      build/SkiaSharp.props
  3. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  4. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  5. 1
      src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
  6. 34
      src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKCanvas.cs
  7. 57
      src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKImageFilter.cs
  8. 32
      src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.SKPath.cs
  9. 16
      src/Skia/Avalonia.Skia/Compatibility/SkiaCompat.cs
  10. 6
      src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs
  11. 6
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  12. 2
      src/Skia/Avalonia.Skia/GlyphRunImpl.cs
  13. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  14. 4
      src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
  15. 12
      tests/Avalonia.RenderTests/TestBase.cs
  16. 9
      tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj

6
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>

13
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
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
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
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>

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
}

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
}

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
}

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
}

6
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;
}
}
}

6
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();
@ -859,7 +859,7 @@ namespace Avalonia.Skia
transform *= _postTransform.Value;
}
SkiaCompat.SetMatrix(Canvas, transform.ToSKMatrix());
Canvas.SetMatrix(transform.ToSKMatrix());
}
}

2
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);

2
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);
}
}

4
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);
}
}

12
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);
}

9
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" />

Loading…
Cancel
Save