Some of the colorspaces created are just references (#922)

* Some of the colorspaces created are just references
  - SRGB and SRGB Linear colorspaces are static and should never be disposed
* Fix concurrent disposal and creation
  - In some cases, a new native object is created with the same memory location while the 
    managed instances is still being disposed. In this case, we cannot remove the managed
    instance from the dictionary, since it is not the current object being disposed anymore.
  - SKColorSpace has some members that should not be disposed, since they are static on the
    native side. Just, there is no docs for this, so calling dispose will crash everything.
  - Added the logic to make sure that all static objects are created before anything, so we can 
    get a handle on them and make sure they are static managed instances.
* Make sure the objects are not collected when using spans
This commit is contained in:
Matthew Leibowitz 2019-09-24 01:34:44 +02:00 коммит произвёл GitHub
Родитель d5aa34a787
Коммит c10c7394a2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
33 изменённых файлов: 585 добавлений и 144 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -3,6 +3,7 @@
# Repository-specific files
output/
output-temp/
tools/
*.VC.db
**/Resources/Resource.designer.cs

Просмотреть файл

@ -4,8 +4,8 @@ mdoc.targets release 5.7.4.9
mdoc release 5.7.4.9
harfbuzz release 2.5.3
skia release m68
xunit release 2.4.0
xunit.runner.console release 2.4.0
xunit release 2.4.1
xunit.runner.console release 2.4.1
Xamarin.Forms release 4.0.0.540366
Tizen.NET release 4.0.0
OpenTK release 3.0.1

Просмотреть файл

@ -392,7 +392,13 @@ namespace SkiaSharp
// no-op due to unsupperted action
}
public byte[] Bytes => GetPixelSpan ().ToArray ();
public byte[] Bytes {
get {
var array = GetPixelSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public SKColor[] Pixels {
get {

Просмотреть файл

@ -178,6 +178,21 @@ namespace SkiaSharp
public class SKColorSpace : SKObject, ISKReferenceCounted
{
private static readonly SKColorSpace srgb;
private static readonly SKColorSpace srgbLinear;
static SKColorSpace ()
{
srgb = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb ());
srgbLinear = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb_linear ());
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve]
internal SKColorSpace (IntPtr handle, bool owns)
: base (handle, owns)
@ -208,11 +223,9 @@ namespace SkiaSharp
return SkiaApi.sk_colorspace_equals (left.Handle, right.Handle);
}
public static SKColorSpace CreateSrgb () =>
GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_srgb ());
public static SKColorSpace CreateSrgb () => srgb;
public static SKColorSpace CreateSrgbLinear () =>
GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_srgb_linear ());
public static SKColorSpace CreateSrgbLinear () => srgbLinear;
public static SKColorSpace CreateIcc (IntPtr input, long length)
{
@ -299,5 +312,19 @@ namespace SkiaSharp
public SKMatrix44 FromXyzD50 () =>
GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_from_xyzd50 (Handle), false);
private sealed class SKColorSpaceStatic : SKColorSpace
{
internal SKColorSpaceStatic (IntPtr x)
: base (x, false)
{
IgnorePublicDispose = true;
}
protected override void Dispose (bool disposing)
{
// do not dispose
}
}
}
}

Просмотреть файл

@ -13,8 +13,18 @@ namespace SkiaSharp
// improvement in Copy performance.
internal const int CopyBufferSize = 81920;
private static readonly Lazy<SKData> empty =
new Lazy<SKData> (() => new SKDataStatic (SkiaApi.sk_data_new_empty ()));
private static readonly Lazy<SKData> empty;
static SKData()
{
empty = new Lazy<SKData> (() => new SKDataStatic (SkiaApi.sk_data_new_empty ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve]
internal SKData (IntPtr x, bool owns)
@ -174,7 +184,12 @@ namespace SkiaSharp
return GetObject<SKData> (SkiaApi.sk_data_new_subset (Handle, (IntPtr) offset, (IntPtr) length));
}
public byte[] ToArray () => AsSpan ().ToArray ();
public byte[] ToArray ()
{
var array = AsSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
public bool IsEmpty => Size == 0;

Просмотреть файл

@ -7,8 +7,18 @@ namespace SkiaSharp
{
public class SKFontManager : SKObject, ISKReferenceCounted
{
private static readonly Lazy<SKFontManager> defaultManager =
new Lazy<SKFontManager> (() => new SKFontManagerStatic (SkiaApi.sk_fontmgr_ref_default ()));
private static readonly Lazy<SKFontManager> defaultManager;
static SKFontManager()
{
defaultManager = new Lazy<SKFontManager> (() => new SKFontManagerStatic (SkiaApi.sk_fontmgr_ref_default ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve]
internal SKFontManager (IntPtr handle, bool owns)

Просмотреть файл

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
namespace SkiaSharp
{
@ -13,11 +14,22 @@ namespace SkiaSharp
internal static readonly ConcurrentBag<Exception> exceptions = new ConcurrentBag<Exception> ();
#endif
internal static readonly ConcurrentDictionary<Type, ConstructorInfo> constructors = new ConcurrentDictionary<Type, ConstructorInfo> ();
internal static readonly ConcurrentDictionary<IntPtr, WeakReference> instances = new ConcurrentDictionary<IntPtr, WeakReference> ();
internal static readonly ConcurrentDictionary<Type, ConstructorInfo> constructors;
internal static readonly ConcurrentDictionary<IntPtr, WeakReference> instances;
internal readonly ConcurrentDictionary<IntPtr, SKObject> ownedObjects = new ConcurrentDictionary<IntPtr, SKObject> ();
static SKObject ()
{
constructors = new ConcurrentDictionary<Type, ConstructorInfo> ();
instances = new ConcurrentDictionary<IntPtr, WeakReference> ();
SKColorSpace.EnsureStaticInstanceAreInitialized ();
SKData.EnsureStaticInstanceAreInitialized ();
SKFontManager.EnsureStaticInstanceAreInitialized ();
SKTypeface.EnsureStaticInstanceAreInitialized ();
}
[Preserve]
internal SKObject (IntPtr handle, bool owns)
: base (handle, owns)
@ -108,7 +120,7 @@ namespace SkiaSharp
WeakReference Update (IntPtr key, WeakReference oldValue)
{
if (oldValue.Target is SKObject obj) {
if (oldValue.Target is SKObject obj && !obj.IsDisposed) {
#if THROW_OBJECT_EXCEPTIONS
if (obj.OwnsHandle)
throw new InvalidOperationException (
@ -128,14 +140,28 @@ namespace SkiaSharp
if (handle == IntPtr.Zero)
return;
var removed = instances.TryRemove (handle, out _);
var nonExistent = new WeakReference (null);
var weak = instances.AddOrUpdate (handle, nonExistent, (ptr, old) => {
if (old.Target is SKObject obj && !obj.IsDisposed)
return old;
return new WeakReference (null);
});
#if THROW_OBJECT_EXCEPTIONS
if (!removed) {
var ex = new InvalidOperationException (
InvalidOperationException ex = null;
if (weak == nonExistent) {
// the dummy instance was added instead of being removed
ex = new InvalidOperationException (
$"A managed object did not exist for the specified native object. " +
$"H: {handle.ToString ("x")} Type: {instance.GetType ()}");
ex.Data.Add ("Handle", handle);
}
if (weak.Target is SKObject o && o != instance && !instance.IsDisposed) {
// there was a new living object there, but we are still alive
ex = new InvalidOperationException (
$"Trying to remove a different object with the same native handle. " +
$"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {instance.GetType ()})");
}
if (ex != null) {
if (instance.fromFinalizer)
exceptions.Add (ex);
else
@ -148,18 +174,26 @@ namespace SkiaSharp
where TSkiaObject : SKObject
{
if (instances.TryGetValue (handle, out var weak)) {
if (weak.Target is TSkiaObject match) {
if (!match.IsDisposed) {
instance = match;
return true;
}
#if THROW_OBJECT_EXCEPTIONS
if (weak.Target is object existing && !(weak.Target is TSkiaObject))
} else if (weak.Target is SKObject obj) {
if (!obj.IsDisposed) {
throw new InvalidOperationException (
$"A managed object exists for the handle, but is not the expected type. " +
$"H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {typeof (TSkiaObject)})");
}
} else if (weak.Target is object o) {
throw new InvalidOperationException (
$"A managed object exists for the handle, but is not the expected type. " +
$"H: {handle.ToString ("x")} Type: ({existing.GetType ()}, {typeof (TSkiaObject)})");
$"An unknown object exists for the handle when trying to fetch an instance. " +
$"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {typeof (TSkiaObject)})");
#endif
if (weak.Target is TSkiaObject obj && obj.Handle != IntPtr.Zero) {
instance = obj;
return true;
}
}
instance = null;
return false;
}
@ -225,7 +259,7 @@ namespace SkiaSharp
internal bool fromFinalizer = false;
#endif
private bool isDisposed = false;
private int isDisposed = 0;
internal SKNativeObject (IntPtr handle)
: this (handle, true)
@ -253,6 +287,8 @@ namespace SkiaSharp
protected internal bool IgnorePublicDispose { get; protected set; }
protected internal bool IsDisposed => isDisposed == 1;
protected virtual void DisposeManaged ()
{
// dispose of any managed resources
@ -265,9 +301,8 @@ namespace SkiaSharp
protected virtual void Dispose (bool disposing)
{
if (isDisposed)
if (Interlocked.CompareExchange (ref isDisposed, 1, 0) != 0)
return;
isDisposed = true;
if (disposing) {
DisposeManaged ();

Просмотреть файл

@ -15,8 +15,18 @@ namespace SkiaSharp
public class SKTypeface : SKObject, ISKReferenceCounted
{
private static readonly Lazy<SKTypeface> defaultTypeface =
new Lazy<SKTypeface> (() => new SKTypefaceStatic (SkiaApi.sk_typeface_ref_default ()));
private static readonly Lazy<SKTypeface> defaultTypeface;
static SKTypeface ()
{
defaultTypeface = new Lazy<SKTypeface> (() => new SKTypefaceStatic (SkiaApi.sk_typeface_ref_default ()));
}
internal static void EnsureStaticInstanceAreInitialized ()
{
// IMPORTANT: do not remove to ensure that the static instances
// are initialized before any access is made to them
}
[Preserve]
internal SKTypeface (IntPtr handle, bool owns)

Просмотреть файл

@ -69,9 +69,21 @@ namespace HarfBuzzSharp
set => HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle);
}
public GlyphInfo[] GlyphInfos => GetGlyphInfoSpan ().ToArray ();
public GlyphInfo[] GlyphInfos {
get {
var array = GetGlyphInfoSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public GlyphPosition[] GlyphPositions => GetGlyphPositionSpan ().ToArray ();
public GlyphPosition[] GlyphPositions {
get {
var array = GetGlyphPositionSpan ().ToArray ();
GC.KeepAlive (this);
return array;
}
}
public void Add (uint codepoint, uint cluster)
{

Просмотреть файл

@ -6,7 +6,7 @@
#addin nuget:?package=Xamarin.Nuget.Validator&version=1.1.1
#tool nuget:?package=mdoc&version=5.7.4.9
#tool nuget:?package=xunit.runner.console&version=2.4.0
#tool nuget:?package=xunit.runner.console&version=2.4.1
#tool nuget:?package=vswhere&version=2.5.2
using System.Linq;
@ -24,7 +24,7 @@ using NuGet.Versioning;
#load "cake/Utils.cake"
var TARGET = Argument ("t", Argument ("target", Argument ("Target", "Default")));
var VERBOSITY = (Verbosity) Enum.Parse (typeof(Verbosity), Argument ("v", Argument ("verbosity", Argument ("Verbosity", "Normal"))), true);
var VERBOSITY = Argument ("v", Argument ("verbosity", Argument ("Verbosity", Verbosity.Normal)));
var SKIP_EXTERNALS = Argument ("skipexternals", Argument ("SkipExternals", "")).ToLower ().Split (',');
var PACK_ALL_PLATFORMS = Argument ("packall", Argument ("PackAll", Argument ("PackAllPlatforms", TARGET.ToLower() == "ci" || TARGET.ToLower() == "nuget-only")));
var PRINT_ALL_ENV_VARS = Argument ("printAllEnvVars", false);
@ -137,16 +137,6 @@ Task ("tests-only")
.Does (() =>
{
var RunDesktopTest = new Action<string> (arch => {
var platform = "";
if (IsRunningOnWindows ()) {
platform = "windows";
} else if (IsRunningOnMac ()) {
platform = "mac";
} else if (IsRunningOnLinux ()) {
platform = "linux";
}
EnsureDirectoryExists ($"./output/tests/{platform}/{arch}");
RunMSBuild ("./tests/SkiaSharp.Desktop.Tests/SkiaSharp.Desktop.Tests.sln", platform: arch == "AnyCPU" ? "Any CPU" : arch);
RunTests ($"./tests/SkiaSharp.Desktop.Tests/bin/{arch}/{CONFIGURATION}/SkiaSharp.Tests.dll", arch == "x86");
});
@ -165,7 +155,6 @@ Task ("tests-only")
}
// .NET Core
EnsureDirectoryExists ("./output/tests/netcore");
RunMSBuild ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.sln");
RunNetCoreTests ("./tests/SkiaSharp.NetCore.Tests/SkiaSharp.NetCore.Tests.csproj");
});

Просмотреть файл

@ -71,6 +71,8 @@ void RunTests (FilePath testAssembly, bool is32)
ReportName = "TestResults",
XmlReport = true,
UseX86 = is32,
NoAppDomain = true,
Parallelism = ParallelismOption.All,
OutputDirectory = dir,
WorkingDirectory = dir,
ArgumentCustomization = args => args.Append ("-verbose"),

Просмотреть файл

@ -1,5 +1,5 @@
{
"msbuild-sdks": {
"MSBuild.Sdk.Extras": "1.6.65"
"sdk": {
"version": "2.2.108.1"
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -17,7 +17,7 @@
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint>
<PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">

Просмотреть файл

@ -18,7 +18,7 @@
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint>
<PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">

Просмотреть файл

@ -17,7 +17,7 @@
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint>
<PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">

Просмотреть файл

@ -18,7 +18,7 @@
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>..\..\..\AppStoreCertificates\SkiaSharpSample_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8DD3DBEC438E69AF67465BF733A17DBA8397D719</PackageCertificateThumbprint>
<PackageCertificateThumbprint>D3578B78019FED6AEE572B23C1723D44951E0BF9</PackageCertificateThumbprint>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">

Просмотреть файл

@ -2,6 +2,10 @@ trigger:
- master
- development
pr:
- master
- development
variables:
FEATURE_NAME_PREFIX: 'feature/'
VERBOSITY: normal
@ -11,14 +15,20 @@ variables:
FEATURE_NAME: ''
PREVIEW_LABEL: 'rc'
NATIVE_LINUX_PACKAGES: curl mono-complete msbuild python git libfontconfig1-dev clang-3.8 make
TIZEN_LINUX_PACKAGES: libxcb-xfixes0 libxcb-render-util0 libwebkitgtk-1.0-0 libxcb-image0 acl libsdl1.2debian libv4l-0 libxcb-randr0 libxcb-shape0 libxcb-icccm4 libsm6 gettext rpm2cpio cpio bridge-utils openvpn
MANAGED_LINUX_PACKAGES: ttf-ancient-fonts
MONO_VERSION: 5_18_1
XCODE_VERSION: 10.2.1
# CONFIGURATION: 'Release'
VM_IMAGE_WINDOWS: vs2017-win2016
VM_IMAGE_MAC: macos-10.14
VM_IMAGE_LINUX: ubuntu-16.04
TIZEN_LINUX_PACKAGES: mono-complete msbuild libxcb-xfixes0 libxcb-render-util0 libwebkitgtk-1.0-0 libxcb-image0 acl libsdl1.2debian libv4l-0 libxcb-randr0 libxcb-shape0 libxcb-icccm4 libsm6 gettext rpm2cpio cpio bridge-utils openvpn
MANAGED_LINUX_PACKAGES: mono-complete msbuild ttf-ancient-fonts
MONO_VERSION_MACOS: 5_18_3
MONO_VERSION_LINUX: stable-xenial/snapshots/5.20.1.34
XCODE_VERSION: 10.3
DOTNET_VERSION: 2.2.108
CONFIGURATION: 'Release'
VM_IMAGE_WINDOWS: Hosted VS2017
VM_IMAGE_MAC: Hosted macOS
VM_IMAGE_LINUX: Hosted Ubuntu 1604
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
# # To speed up builds when debugging DevOps
# DOWNLOAD_EXTERNALS: ''
resources:
repositories:
@ -38,12 +48,14 @@ stages:
displayName: Build Native Android (Windows)
vmImage: $(VM_IMAGE_WINDOWS)
target: externals-android
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Tizen (Windows)
parameters:
name: native_tizen_windows
displayName: Build Native Tizen (Windows)
vmImage: $(VM_IMAGE_WINDOWS)
target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
condition: false # TODO: TIZEN INSTALL BUGS
- template: azure-templates-bootstrapper.yml # Build Native UWP (Windows)
parameters:
@ -51,12 +63,14 @@ stages:
displayName: Build Native UWP (Windows)
vmImage: $(VM_IMAGE_WINDOWS)
target: externals-uwp
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Win32 (Windows)
parameters:
name: native_win32_windows
displayName: Build Native Win32 (Windows)
vmImage: $(VM_IMAGE_WINDOWS)
target: externals-windows
buildExternals: $(DOWNLOAD_EXTERNALS)
# NATIVE JOBS - MAC
- template: azure-templates-bootstrapper.yml # Build Native Android (macOS)
parameters:
@ -64,24 +78,28 @@ stages:
displayName: Build Native Android (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-android
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native iOS (macOS)
parameters:
name: native_ios_macos
displayName: Build Native iOS (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-ios
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native macOS (macOS)
parameters:
name: native_macos_macos
displayName: Build Native macOS (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-macos
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native Tizen (macOS)
parameters:
name: native_tizen_macos
displayName: Build Native Tizen (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
condition: false # TODO: TIZEN INSTALL BUGS
- template: azure-templates-bootstrapper.yml # Build Native tvOS (macOS)
parameters:
@ -89,25 +107,31 @@ stages:
displayName: Build Native tvOS (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-tvos
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-bootstrapper.yml # Build Native watchOS (macOS)
parameters:
name: native_watchos_macos
displayName: Build Native watchOS (macOS)
vmImage: $(VM_IMAGE_MAC)
target: externals-watchos
buildExternals: $(DOWNLOAD_EXTERNALS)
# NATIVE JOBS - LINUX
- template: azure-templates-native-linux.yml # Build Native Linux (Linux)
parameters:
name: native_linux_linux
displayName: Build Native Linux (Linux)
vmImage: $(VM_IMAGE_LINUX)
packages: $(NATIVE_LINUX_PACKAGES)
target: externals-linux
buildExternals: $(DOWNLOAD_EXTERNALS)
- template: azure-templates-native-linux.yml # Build Native Linux [No Dependencies] (Linux)
parameters:
name: native_linux_nodependencies_linux
displayName: Build Native Linux [No Dependencies] (Linux)
vmImage: $(VM_IMAGE_LINUX)
packages: $(NATIVE_LINUX_PACKAGES)
target: externals-linux
buildExternals: $(DOWNLOAD_EXTERNALS)
additionalArgs: --additionalGnArgs="skia_use_fontconfig=false"
- template: azure-templates-bootstrapper.yml # Build Native Tizen (Linux)
parameters:
@ -116,6 +140,7 @@ stages:
vmImage: $(VM_IMAGE_LINUX)
packages: $(TIZEN_LINUX_PACKAGES)
target: externals-tizen
buildExternals: $(DOWNLOAD_EXTERNALS)
- stage: managed
displayName: Build Managed
@ -238,7 +263,7 @@ stages:
- package_nodependencies_windows
- package_windows
pool:
vmImage: $(VM_IMAGE_LINUX)
name: $(VM_IMAGE_LINUX)
steps:
- task: DownloadBuildArtifacts@0
displayName: Download the nuget-nodependencies artifacts
@ -270,23 +295,9 @@ stages:
dependsOn: package
condition: eq(variables['System.TeamProject'], 'devdiv')
jobs:
- job: signing # Sign NuGets
displayName: Sign NuGets
pool:
name: VSEng-XamarinCustom
demands:
- corpnet
condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['Build.SourceBranch'], 'refs/heads/master')))
steps:
- checkout: none
- template: sign-artifacts.yml@xamarin-templates
parameters:
targetFolder: '$(Build.ArtifactStagingDirectory)/signed'
- task: PublishBuildArtifacts@1
displayName: Publish the nuget-signed artifacts
inputs:
artifactName: nuget-signed
pathToPublish: '$(Build.ArtifactStagingDirectory)/signed'
- template: sign-artifacts/jobs/v1.yml@xamarin-templates
parameters:
additionalConditions: eq(variables['Build.SourceBranch'], 'refs/heads/master')
- stage: tests
displayName: Run Tests
@ -305,11 +316,19 @@ stages:
- native_win32_windows
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the test results
displayName: Publish the .NET Framework test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml'
testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Framework Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests (macOS)
parameters:
name: tests_macos
@ -323,11 +342,19 @@ stages:
- native_macos_macos
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the test results
displayName: Publish the Mono test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml'
testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'macOS Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'macOS .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests (Linux)
parameters:
name: tests_linux
@ -342,11 +369,19 @@ stages:
- native_linux_linux
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the test results
displayName: Publish the Mono test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml'
testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Linux Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Linux .NET Core Tests'
- template: azure-templates-bootstrapper.yml # Tests [No Dependencies] (Linux)
parameters:
name: tests_nodependencies_linux
@ -361,11 +396,19 @@ stages:
- native_linux_nodependencies_linux
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the test results
displayName: Publish the Mono test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/**/TestResults.xml'
testResultsFiles: 'tests/SkiaSharp.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Linux [No Dependencies] Mono Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp.NetCore.Tests/**/TestResults.xml'
testRunTitle: 'Linux [No Dependencies] .NET Core Tests'
- stage: samples
displayName: Build Samples

Просмотреть файл

@ -1,25 +1,38 @@
parameters:
name: '' # in the form type_platform_host
displayName: '' # the human name
vmImage: '' # the VM image
packages: '' # any additional packages
target: '' # the bootstrapper target
dependsOn: [] # the dependiencies
requiredArtifacts: [] # the artifacts that this build needs to download
demands: [] # the demands
preBuildSteps: [] # any steps to run before the build
postBuildSteps: [] # any additional steps to run after the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper
condition: succeeded() # whether or not to run this template
shouldPublish: true # whether or not to publish the artifacts
name: '' # in the form type_platform_host
displayName: '' # the human name
vmImage: '' # the VM image
packages: '' # any additional packages
target: '' # the bootstrapper target
dependsOn: [] # the dependiencies
requiredArtifacts: [] # the artifacts that this build needs to download
demands: [] # the demands
preBuildSteps: [] # any steps to run before the build
postBuildSteps: [] # any additional steps to run after the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper
condition: succeeded() # whether or not to run this template
shouldPublish: true # whether or not to publish the artifacts
configuration: '$(CONFIGURATION)' # the build configuration
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
verbosity: '$(VERBOSITY)' # the level of verbosity to use when building
jobs:
# - ${{ if and(parameters.buildExternals, startsWith(parameters.name, 'native_')) }}:
# - template: azure-templates-download.yml
# parameters:
# name: ${{ parameters.name }}
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# condition: ${{ parameters.condition }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(not(parameters.buildExternals), not(startsWith(parameters.name, 'native_'))) }}:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
timeoutInMinutes: 120
pool:
vmImage: ${{ parameters.vmImage }}
name: ${{ parameters.vmImage }}
demands: ${{ parameters.demands }}
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
@ -35,6 +48,11 @@ jobs:
# install any packages on linux
- ${{ if endsWith(parameters.name, '_linux') }}:
- bash: |
sudo apt remove -y mono-complete msbuild
sudo apt autoremove -y
sudo rm /etc/mono/config
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu $(MONO_VERSION_LINUX) main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
sudo apt install -y ${{ parameters.packages }}
displayName: Install any package dependencies
@ -56,9 +74,17 @@ jobs:
- ${{ if contains(parameters.name, '_android_') }}:
- powershell: .\scripts\install-android-ndk.ps1
displayName: Install the Android NDK
# install extra bits for the manged builds
- ${{ if not(startsWith(parameters.name, 'native_')) }}:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_VERSION)
performMultiLevelLookup: true
displayName: Install the correct version of .NET Core
# switch to the correct mono version on mac
- ${{ if endsWith(parameters.name, '_macos') }}:
- bash: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(MONO_VERSION)
- bash: sudo $(Agent.HomeDirectory)/scripts/select-xamarin-sdk.sh $(MONO_VERSION_MACOS)
displayName: Switch to the latest Xamarin SDK
- bash: echo '##vso[task.setvariable variable=MD_APPLE_SDK_ROOT;]'/Applications/Xcode_$(XCODE_VERSION).app;sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer
displayName: Switch to the latest Xcode
@ -78,10 +104,14 @@ jobs:
- ${{ parameters.preBuildSteps }}
# build
- ${{ if endsWith(parameters.name, '_windows') }}:
- powershell: .\scripts\retry-command.ps1 -RetryCount ${{ parameters.retryCount }} { .\bootstrapper.ps1 -t ${{ parameters.target }} -v $env:VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }} }
- powershell: .\scripts\retry-command.ps1 -RetryCount ${{ parameters.retryCount }} { .\bootstrapper.ps1 -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }} }
env:
JavaSdkDirectory: $(JAVA_HOME)
displayName: Run the bootstrapper for ${{ parameters.target }}
- ${{ if not(endsWith(parameters.name, '_windows')) }}:
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v $VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }}
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }}
env:
JavaSdkDirectory: $(JAVA_HOME)
displayName: Run the bootstrapper for ${{ parameters.target }}
# post-build steps
- ${{ parameters.postBuildSteps }}

Просмотреть файл

@ -0,0 +1,36 @@
parameters:
name: '' # in the form type_platform_host
displayName: '' # the human name
vmImage: '' # the VM image
condition: succeeded() # whether or not to run this template
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
jobs:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
pool:
name: ${{ parameters.vmImage }}
condition: ${{ parameters.condition }}
steps:
- checkout: none
- task: DownloadBuildArtifacts@0
displayName: Download the pre-built ${{ parameters.name }} artifacts
inputs:
buildType: 'specific'
project: '$(System.TeamProjectId)'
pipeline: '$(System.DefinitionId)'
buildVersionToDownload: 'specific'
buildId: '${{ parameters.buildExternals }}'
downloadType: 'single'
artifactName: ${{ parameters.name }}
downloadPath: 'download-temp'
- powershell: |
New-Item '.\output\' -Type Directory -Force | Out-Null
Get-ChildItem '.\download-temp\${{ parameters.name }}\' | Copy-Item -Destination '.\output\' -Recurse -Force
Remove-Item '.\download-temp\${{ parameters.name }}\' -Recurse -Force
displayName: Move the ${{ parameters.name }} artifacts to the output directory
- task: PublishBuildArtifacts@1
displayName: Publish the ${{ parameters.name }} artifacts
inputs:
artifactName: ${{ parameters.name }}
pathToPublish: 'output'

Просмотреть файл

@ -1,19 +1,32 @@
parameters:
name: '' # in the form type_platform_host
displayName: '' # the human name
packages: '' # any additional packages
target: '' # the bootstrapper target
preBuildSteps: [] # any steps to run before the build
postBuildSteps: [] # any additional steps to run after the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper
name: '' # in the form type_platform_host
displayName: '' # the human name
vmImage: '' # the VM image
packages: '' # any additional packages
target: '' # the bootstrapper target
preBuildSteps: [] # any steps to run before the build
postBuildSteps: [] # any additional steps to run after the build
additionalArgs: '' # any additional arguments to pass to the bootstrapper
retryCount: 1 # the number of times to retry the bootstrapper
configuration: '$(CONFIGURATION)' # the build configuration
buildExternals: '$(DOWNLOAD_EXTERNALS)' # the build number to download externals from
verbosity: '$(VERBOSITY)' # the level of verbosity to use when building
jobs:
# - ${{ if and(parameters.buildExternals, startsWith(parameters.name, 'native_')) }}:
# - template: azure-templates-download.yml
# parameters:
# name: ${{ parameters.name }}
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(not(parameters.buildExternals), not(startsWith(parameters.name, 'native_'))) }}:
- job: ${{ parameters.name }}
displayName: ${{ parameters.displayName }}
timeoutInMinutes: 120
pool:
vmImage: $(VM_IMAGE_LINUX)
name: ${{ parameters.vmImage }}
container: ubuntu:14.04
steps:
- checkout: self
@ -30,7 +43,7 @@ jobs:
# pre-build steps
- ${{ parameters.preBuildSteps }}
# build
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v $VERBOSITY -c ${{ coalesce(variables.CONFIGURATION, 'Release') }} ${{ parameters.additionalArgs }}
- bash: ./scripts/retry-command.sh ${{ parameters.retryCount }} ./bootstrapper.sh -t ${{ parameters.target }} -v ${{ parameters.verbosity }} -c ${{ coalesce(parameters.configuration, 'Release') }} ${{ parameters.additionalArgs }}
displayName: Run the bootstrapper for ${{ parameters.target }}
env:
CC: clang-3.8

Просмотреть файл

@ -0,0 +1,30 @@
param (
[string] $BuildNumber,
[string] $Artifact
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.IO.Compression.FileSystem
$url = "https://dev.azure.com/xamarin/public/_apis/build/builds/$BuildNumber/artifacts?artifactName=$Artifact&api-version=5.1&%24format=zip"
$outputTemp = "./output-temp"
$dest = "$outputTemp/$Artifact.zip"
$destTemp = "$outputTemp/$Artifact"
$output = "./output"
# dowload the artifact
New-Item -Type Directory -Path $outputTemp -Force | Out-Null
Invoke-WebRequest -Uri $url -OutFile $dest
# extract it
if (Test-Path -Path $destTemp) {
Remove-Item -Path $destTemp/** -Force -Recurse
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($dest, $outputTemp)
# create the output folder
New-Item -Type Directory -Path $output -Force | Out-Null
# move the items into the output folder
Get-ChildItem $destTemp | Copy-Item -Destination $output -Force -Recurse

44
scripts/download-file.ps1 Normal file
Просмотреть файл

@ -0,0 +1,44 @@
# This was modified from StackOverflow
# https://stackoverflow.com/questions/45574479/powershell-determine-new-url-of-a-permanently-moved-redirected-resource
Param (
[Parameter(Mandatory, ValueFromPipeline)] [Uri] $Uri,
[string] $OutFile,
[int] $MaxRedirections = 50 # Use same default as [System.Net.HttpWebRequest]
)
process {
$nextUri = $Uri
$ultimateFound = $false
foreach($i in 1..$($MaxRedirections+1)) {
Write-Verbose "Examining: $nextUri"
$request = [System.Net.HttpWebRequest]::Create($nextUri)
$request.AllowAutoRedirect = $False
try {
$response = $request.GetResponse()
$nextUriStr = $response.Headers['Location']
$response.Close()
if (-not $nextUriStr) {
$ultimateFound = $true
break
}
} catch [System.Net.WebException] {
$nextUriStr = try { $_.Exception.Response.Headers['Location'] } catch {}
if (-not $nextUriStr) { Throw }
}
Write-Verbose "Raw target: $nextUriStr"
if ($nextUriStr -match '^https?:') {
$nextUri = $prevUri = [Uri] $nextUriStr
} else {
$nextUri = $prevUri = [Uri] ($prevUri.Scheme + '://' + $prevUri.Authority + $nextUriStr)
}
if ($i -ge $MaxRedirections) {
break
}
}
if (-not $ultimateFound) {
Throw "Enumeration of $Uri redirections ended before reaching the ultimate target."
}
Invoke-WebRequest -Uri $nextUri -OutFile $OutFile
}

Просмотреть файл

@ -20,7 +20,7 @@ $ndk = "$HOME/android-ndk"
if ($InstallDestination) {
$ndk = $InstallDestination
}
Write-Host "Install destination is '$ts'..."
Write-Host "Install destination is '$ndk'..."
$ndkTemp = "$HOME/android-ndk-temp"
$install = "$ndkTemp/android-ndk.zip"

Просмотреть файл

@ -16,7 +16,7 @@ $jdk = Join-Path "$HOME" "openjdk"
if ($InstallDestination) {
$jdk = $InstallDestination
}
Write-Host "Install destination is '$ts'..."
Write-Host "Install destination is '$jdk'..."
$jdkTemp = Join-Path "$HOME" "openjdk-temp"
$archive = Join-Path "$jdkTemp" "openjdk.tar.gz"

Просмотреть файл

@ -81,8 +81,8 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="xunit.assemblyfixture" Version="2.0.3" />
<PackageReference Include="xunit.skippablefact" Version="1.3.12" />
</ItemGroup>

Просмотреть файл

@ -14,8 +14,8 @@
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="XunitXml.TestLogger" Version="2.1.26" />
<PackageReference Include="xunit.assemblyfixture" Version="2.0.3" />
<PackageReference Include="xunit.skippablefact" Version="1.3.12" />

Просмотреть файл

@ -7,20 +7,21 @@ namespace SkiaSharp.Tests
{
public class GarbageCleanupFixture : IDisposable
{
#if THROW_OBJECT_EXCEPTIONS
internal static readonly ConcurrentBag<IntPtr> ignoredExceptions = new ConcurrentBag<IntPtr>();
#endif
private static readonly string[] StaticTypes = new[] {
"SkiaSharp.SKData+SKDataStatic",
"SkiaSharp.SKFontManager+SKFontManagerStatic",
"SkiaSharp.SKTypeface+SKTypefaceStatic",
"SkiaSharp.SKColorSpace+SKColorSpaceStatic",
};
public GarbageCleanupFixture()
{
Assert.Empty(SKObject.constructors);
Assert.Empty(SKObject.instances);
var aliveObjects = SKObject.instances.Values
.Select(o => o.Target)
.Where(IsExpectedToBeDead)
.ToList();
Assert.Empty(aliveObjects);
}
public void Dispose()
@ -37,21 +38,9 @@ namespace SkiaSharp.Tests
#if THROW_OBJECT_EXCEPTIONS
// make sure all the exceptions are accounted for
var ignored = ignoredExceptions
.ToList();
var exceptions = SKObject.exceptions
.ToList();
var keep = exceptions
.Where(ex => !ignored.Contains((IntPtr)ex.Data["Handle"]))
.ToList();
Assert.Empty(keep);
foreach (var ignore in ignored)
{
var e = exceptions
.Where(ex => ex.Data["Handle"] is IntPtr)
.ToList();
Assert.NotEmpty(e);
}
Assert.Empty(exceptions);
// make sure all the GCHandles are freed
var gcHandles = GCHandleProxy.allocatedHandles.Values
@ -63,6 +52,9 @@ namespace SkiaSharp.Tests
private bool IsExpectedToBeDead(object instance)
{
if (instance == null)
return false;
var skobject = Assert.IsAssignableFrom<SKObject>(instance);
if (StaticTypes.Contains(skobject.GetType().FullName))

Просмотреть файл

@ -180,7 +180,7 @@ namespace SkiaSharp.Tests
if (index > 0) {
decodeInfo = info.WithAlphaType (frameInfos [index].AlphaType);
}
bm.TryAllocPixels (decodeInfo);
Assert.True (bm.TryAllocPixels (decodeInfo));
if (cachedIndex != -1) {
Assert.True (cachedFrames [cachedIndex].CopyTo (bm));
}
@ -199,7 +199,9 @@ namespace SkiaSharp.Tests
var uncachedFrame = new SKBitmap ();
decode (uncachedFrame, frameInfos [i].RequiredFrame, i);
Assert.Equal (cachedFrame.Bytes, uncachedFrame.Bytes);
var cachedBytes = cachedFrame.Bytes;
var uncachedBytes = uncachedFrame.Bytes;
Assert.Equal (cachedBytes, uncachedBytes);
}
}
}

Просмотреть файл

@ -16,6 +16,21 @@ namespace SkiaSharp.Tests
Assert.True(SKColorSpace.Equal(colorspace, colorspace));
}
[SkippableFact]
public void StaticSrgbIsReturnedAsTheStaticInstance()
{
var handle = SkiaApi.sk_colorspace_new_srgb();
try
{
var cs = SKObject.GetObject<SKColorSpace>(handle, unrefExisting: false);
Assert.Equal("SKColorSpaceStatic", cs.GetType().Name);
}
finally
{
SkiaApi.sk_refcnt_safe_unref(handle);
}
}
[SkippableFact]
public void ImageInfoHasColorSpace()
{
@ -226,6 +241,58 @@ namespace SkiaSharp.Tests
Assert.True(colorspace.GammaIsCloseToSrgb);
}
[SkippableFact]
public void ColorSpaceCorrectlyReferencesSrgbSingleton()
{
var handle1 = SkiaApi.sk_colorspace_new_srgb();
var colorspace1 = SKColorSpace.CreateSrgb();
Assert.Equal(colorspace1.Handle, handle1);
var colorspace2 = SKColorSpace.CreateSrgb();
Assert.Same(colorspace1, colorspace2);
Assert.Equal(handle1, colorspace2.Handle);
colorspace2.Dispose();
Assert.False(colorspace2.IsDisposed);
Assert.False(colorspace1.IsDisposed);
SkiaApi.sk_refcnt_safe_unref(handle1);
}
[SkippableFact]
public void SameColorSpaceCreatedDifferentWaysAreTheSameObject()
{
var colorspace1 = SKColorSpace.CreateSrgbLinear();
Assert.Equal("SkiaSharp.SKColorSpace+SKColorSpaceStatic", colorspace1.GetType().FullName);
Assert.Equal(2, colorspace1.GetReferenceCount());
var colorspace2 = SKColorSpace.CreateRgb(SKNamedGamma.Linear, SKColorSpaceGamut.Srgb);
Assert.Equal("SkiaSharp.SKColorSpace+SKColorSpaceStatic", colorspace2.GetType().FullName);
Assert.Equal(2, colorspace2.GetReferenceCount());
Assert.Same(colorspace1, colorspace2);
var colorspace3 = SKColorSpace.CreateRgb(
new SKColorSpaceTransferFn { A = 0.6f, B = 0.5f, C = 0.4f, D = 0.3f, E = 0.2f, F = 0.1f },
SKMatrix44.CreateIdentity());
Assert.NotSame(colorspace1, colorspace3);
colorspace3.Dispose();
Assert.True(colorspace3.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
colorspace2.Dispose();
Assert.False(colorspace2.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
colorspace1.Dispose();
Assert.False(colorspace1.IsDisposed);
Assert.Equal(2, colorspace1.GetReferenceCount());
}
private static void AssertMatrix(float[] expected, SKMatrix44 actual)
{
var actualArray = actual

Просмотреть файл

@ -171,6 +171,8 @@ namespace SkiaSharp.Tests
[SkippableFact]
public void StreamIsNotCollectedPrematurely()
{
VerifyImmediateFinalizers();
DoWork(out var handle);
CollectGarbage();

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;
namespace SkiaSharp.Tests
@ -131,9 +133,8 @@ namespace SkiaSharp.Tests
var inst1 = new LifecycleObject(handle, true) { Value = 1 };
#if THROW_OBJECT_EXCEPTIONS
Assert.Throws<InvalidOperationException>(() => new LifecycleObject(handle, true) { Value = 2 });
GarbageCleanupFixture.ignoredExceptions.Add(handle);
var ex = Assert.Throws<InvalidOperationException>(() => new LifecycleObject(handle, true) { Value = 2 });
Assert.Contains("H: " + handle.ToString("x") + " ", ex.Message);
#else
var inst2 = new LifecycleObject(handle, true) { Value = 2 };
Assert.True(inst1.DestroyedNative);
@ -230,5 +231,79 @@ namespace SkiaSharp.Tests
throw new Exception("BREAK!");
}
}
[SkippableTheory]
[InlineData(1)]
[InlineData(1000)]
public async Task EnsureMultithreadingDoesNotThrow(int iterations)
{
var imagePath = Path.Combine(PathToImages, "baboon.jpg");
var tasks = new Task[iterations];
for (var i = 0; i < iterations; i++)
{
var task = new Task(() =>
{
using (var stream = File.OpenRead(imagePath))
using (var data = SKData.Create(stream))
using (var codec = SKCodec.Create(data))
{
var info = new SKImageInfo(codec.Info.Width, codec.Info.Height);
using (var image = SKBitmap.Decode(codec, info))
{
var img = new byte[image.Height, image.Width];
}
}
});
tasks[i] = task;
task.Start();
}
await Task.WhenAll(tasks);
}
[SkippableFact]
public void EnsureConcurrencyResultsInCorrectDeregistration()
{
var handle = (IntPtr)446;
var obj = new ImmediateRecreationObject(handle, true);
Assert.Null(obj.NewInstance);
Assert.Equal(obj, SKObject.instances[handle]?.Target);
obj.Dispose();
Assert.True(SKObject.GetInstance<ImmediateRecreationObject>(handle, out _));
var newObj = obj.NewInstance;
Assert.NotEqual(obj, SKObject.instances[handle]?.Target);
Assert.Equal(newObj, SKObject.instances[handle]?.Target);
newObj.Dispose();
Assert.False(SKObject.GetInstance<ImmediateRecreationObject>(handle, out _));
}
private class ImmediateRecreationObject : SKObject
{
public ImmediateRecreationObject(IntPtr handle, bool shouldRecreate)
: base(handle, true)
{
ShouldRecreate = shouldRecreate;
}
public bool ShouldRecreate { get; }
public ImmediateRecreationObject NewInstance { get; private set; }
protected override void DisposeNative()
{
base.DisposeNative();
if (ShouldRecreate)
NewInstance = new ImmediateRecreationObject(Handle, false);
}
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.33.0" />
<package id="Cake" version="0.34.1" />
</packages>