diff --git a/binding/Binding/GRContext.cs b/binding/Binding/GRContext.cs index 311d1d8f61..56b44f2586 100644 --- a/binding/Binding/GRContext.cs +++ b/binding/Binding/GRContext.cs @@ -128,6 +128,8 @@ public static GRContext CreateMetal (GRMtlBackendContext backendContext, GRConte public GRBackend Backend => SkiaApi.gr_direct_context_get_backend (Handle).FromNative (); + public bool IsAbandoned => SkiaApi.gr_direct_context_is_abandoned (Handle); + public void AbandonContext (bool releaseResources = false) { if (releaseResources) diff --git a/binding/Binding/SkiaApi.generated.cs b/binding/Binding/SkiaApi.generated.cs index 8f24fc7b03..72996a490d 100644 --- a/binding/Binding/SkiaApi.generated.cs +++ b/binding/Binding/SkiaApi.generated.cs @@ -510,6 +510,22 @@ private partial class Delegates { (gr_direct_context_get_resource_cache_usage_delegate ??= GetSymbol ("gr_direct_context_get_resource_cache_usage")).Invoke (context, maxResources, maxResourceBytes); #endif + // bool gr_direct_context_is_abandoned(gr_direct_context_t* context) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal static extern bool gr_direct_context_is_abandoned (gr_direct_context_t context); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal delegate bool gr_direct_context_is_abandoned (gr_direct_context_t context); + } + private static Delegates.gr_direct_context_is_abandoned gr_direct_context_is_abandoned_delegate; + internal static bool gr_direct_context_is_abandoned (gr_direct_context_t context) => + (gr_direct_context_is_abandoned_delegate ??= GetSymbol ("gr_direct_context_is_abandoned")).Invoke (context); + #endif + // gr_direct_context_t* gr_direct_context_make_gl(const gr_glinterface_t* glInterface) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] diff --git a/build.cake b/build.cake index 29fd58dbe2..cfc33ecb2e 100644 --- a/build.cake +++ b/build.cake @@ -3,7 +3,7 @@ #addin nuget:?package=Cake.FileHelpers&version=3.2.1 #addin nuget:?package=Cake.Json&version=4.0.0 #addin nuget:?package=SharpCompress&version=0.24.0 -#addin nuget:?package=Mono.ApiTools.NuGetDiff&version=1.3.0&loaddependencies=true +#addin nuget:?package=Mono.ApiTools.NuGetDiff&version=1.3.2&loaddependencies=true #addin nuget:?package=Xamarin.Nuget.Validator&version=1.1.1 #tool nuget:?package=mdoc&version=5.7.4.10 @@ -29,6 +29,7 @@ DirectoryPath ROOT_PATH = MakeAbsolute(Directory(".")); var SKIP_EXTERNALS = Argument ("skipexternals", "") .ToLower ().Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); +var SKIP_BUILD = Argument ("skipbuild", false); var PACK_ALL_PLATFORMS = Argument ("packall", Argument ("PackAllPlatforms", false)); var BUILD_ALL_PLATFORMS = Argument ("buildall", Argument ("BuildAllPlatforms", false)); var PRINT_ALL_ENV_VARS = Argument ("printAllEnvVars", false); @@ -42,7 +43,6 @@ var PLATFORM_SUPPORTS_VULKAN_TESTS = (IsRunningOnWindows () || IsRunningOnLinux var SUPPORT_VULKAN_VAR = Argument ("supportVulkan", EnvironmentVariable ("SUPPORT_VULKAN") ?? PLATFORM_SUPPORTS_VULKAN_TESTS); var SUPPORT_VULKAN = SUPPORT_VULKAN_VAR == "1" || SUPPORT_VULKAN_VAR.ToLower () == "true"; -var NuGetToolPath = Context.Tools.Resolve ("nuget.exe"); var CakeToolPath = Context.Tools.Resolve ("Cake.exe"); var MDocPath = Context.Tools.Resolve ("mdoc.exe"); @@ -54,7 +54,7 @@ var BUILD_NUMBER = EnvironmentVariable ("BUILD_NUMBER") ?? "0"; var GIT_SHA = Argument ("gitSha", EnvironmentVariable ("GIT_SHA") ?? ""); var GIT_BRANCH_NAME = Argument ("gitBranch", EnvironmentVariable ("GIT_BRANCH_NAME") ?? ""); -var PREVIEW_FEED_URL = "https://nugetized.blob.core.windows.net/skiasharp-eap/flatcontainer/{0}/{1}/{0}.{1}.nupkg"; // 0=id, 1=version +var PREVIEW_FEED_URL = "https://pkgs.dev.azure.com/xamarin/public/_packaging/SkiaSharp/nuget/v3/index.json"; var TRACKED_NUGETS = new Dictionary { { "SkiaSharp", new Version (1, 57, 0) }, @@ -108,6 +108,7 @@ Task ("externals") Task ("libs") .Description ("Build all managed assemblies.") + .WithCriteria(!SKIP_BUILD) .IsDependentOn ("externals") .Does (() => { @@ -485,6 +486,11 @@ Task ("samples") //////////////////////////////////////////////////////////////////////////////////////////////////// Task ("nuget") + .Description ("Pack all NuGets.") + .IsDependentOn ("nuget-normal") + .IsDependentOn ("nuget-special"); + +Task ("nuget-normal") .Description ("Pack all NuGets (build all required dependencies).") .IsDependentOn ("libs") .Does (() => @@ -641,42 +647,91 @@ Task ("nuget") Information ("Metadata validation passed for: {0}", nupkgFile.GetFilename ()); } } +}); - // special case for all the native assets - if (PACK_ALL_PLATFORMS) - { - EnsureDirectoryExists ($"{OUTPUT_SPECIAL_NUGETS_PATH}"); - DeleteFiles ($"{OUTPUT_SPECIAL_NUGETS_PATH}/*.nupkg"); - var specials = new Dictionary { - { "_NativeAssets", "native" }, - { "_NuGets", "nugets" }, - }; - foreach (var pair in specials) { - DeleteFiles ($"./output/{pair.Value}/*.nuspec"); +Task ("nuget-special") + .Description ("Pack all special NuGets.") + .IsDependentOn ("nuget-normal") + .Does (() => +{ + EnsureDirectoryExists ($"{OUTPUT_SPECIAL_NUGETS_PATH}"); + DeleteFiles ($"{OUTPUT_SPECIAL_NUGETS_PATH}/*.nupkg"); + + // get a list of all the version number variants + var versions = new List (); + if (!string.IsNullOrEmpty (PREVIEW_LABEL) && PREVIEW_LABEL.StartsWith ("pr.")) { + var v = $"0.0.0-{PREVIEW_LABEL}"; + if (!string.IsNullOrEmpty (BUILD_NUMBER)) + v += $".{BUILD_NUMBER}"; + versions.Add (v); + } else { + if (!string.IsNullOrEmpty (GIT_SHA)) { + var v = $"0.0.0-commit.{GIT_SHA}"; + if (!string.IsNullOrEmpty (BUILD_NUMBER)) + v += $".{BUILD_NUMBER}"; + versions.Add (v); + } + if (!string.IsNullOrEmpty (GIT_BRANCH_NAME)) { + var v = $"0.0.0-branch.{GIT_BRANCH_NAME.Replace ("/", ".")}"; + if (!string.IsNullOrEmpty (BUILD_NUMBER)) + v += $".{BUILD_NUMBER}"; + versions.Add (v); + } + } - var nuspec = $"./output/{pair.Value}/{pair.Key}.nuspec"; + // get a list of all the nuspecs to pack + var specials = new Dictionary (); - // update the version - var xdoc = XDocument.Load ($"./nuget/{pair.Key}.nuspec"); - var metadata = xdoc.Root.Element ("metadata"); - var version = metadata.Element ("version"); + var nativePlatforms = GetDirectories ("./output/native/*") + .Select (d => d.GetDirectoryName ()) + .ToArray (); + if (nativePlatforms.Length > 0) { + specials[$"_NativeAssets"] = $"native"; + foreach (var platform in nativePlatforms) { + specials[$"_NativeAssets.{platform}"] = $"native/{platform}"; + } + } + if (GetFiles ("./output/nugets/*.nupkg").Count > 0) { + specials[$"_NuGets"] = $"nugets"; + } - if (!string.IsNullOrEmpty (PREVIEW_LABEL) && PREVIEW_LABEL.StartsWith ("pr.")) { - version.Value = "0.0.0-" + PREVIEW_LABEL; - xdoc.Save (nuspec); - PackageNuGet (nuspec, OUTPUT_SPECIAL_NUGETS_PATH, true); - } else { - version.Value = "0.0.0-commit." + GIT_SHA; - xdoc.Save (nuspec); - PackageNuGet (nuspec, OUTPUT_SPECIAL_NUGETS_PATH, true); + foreach (var pair in specials) { + var id = pair.Key; + var path = pair.Value; + var nuspec = $"./output/{path}/{id}.nuspec"; + + DeleteFiles ($"./output/{path}/*.nuspec"); - version.Value = "0.0.0-branch." + GIT_BRANCH_NAME.Replace ("/", "."); - xdoc.Save (nuspec); - PackageNuGet (nuspec, OUTPUT_SPECIAL_NUGETS_PATH, true); + foreach (var packageVersion in versions) { + // update the version + var fn = id.StartsWith ("_NativeAssets.") ? "_NativeAssets" : id; + var xdoc = XDocument.Load ($"./nuget/{fn}.nuspec"); + var metadata = xdoc.Root.Element ("metadata"); + metadata.Element ("version").Value = packageVersion; + metadata.Element ("id").Value = id; + + if (id == "_NativeAssets") { + // handle the root package + var dependencies = metadata.Element ("dependencies"); + foreach (var platform in nativePlatforms) { + dependencies.Add (new XElement ("dependency", + new XAttribute ("id", $"_NativeAssets.{platform}"), + new XAttribute ("version", packageVersion))); + } + } else if (id.StartsWith ("_NativeAssets.")) { + // handle the dependencies + var platform = id.Substring (id.IndexOf (".") + 1); + var files = xdoc.Root.Element ("files"); + files.Add (new XElement ("file", + new XAttribute ("src", $"*/**"), + new XAttribute ("target", $"tools/{platform}"))); } - DeleteFiles ($"./output/{pair.Value}/*.nuspec"); + xdoc.Save (nuspec); + PackageNuGet (nuspec, OUTPUT_SPECIAL_NUGETS_PATH, true); } + + DeleteFiles ($"./output/{path}/*.nuspec"); } }); diff --git a/cake/UpdateDocs.cake b/cake/UpdateDocs.cake index 55a396678b..b44fe1d161 100644 --- a/cake/UpdateDocs.cake +++ b/cake/UpdateDocs.cake @@ -38,19 +38,12 @@ void CopyChangelogs (DirectoryPath diffRoot, string id, string version) } Task ("docs-download-output") - .Does (() => + .Does (async () => { EnsureDirectoryExists ("./output"); CleanDirectories ("./output"); - EnsureDirectoryExists ("./output/temp"); - - var url = GetDownloadUrl ("_nugets"); - DownloadFile (url, "./output/temp/nugets.nupkg"); - - Unzip ("./output/temp/nugets.nupkg", "./output/temp"); - MoveDirectory ("./output/temp/tools", OUTPUT_NUGETS_PATH); - DeleteDirectory("./output/temp", new DeleteDirectorySettings { Recursive = true, Force = true }); + await DownloadPackageAsync ("_nugets", OUTPUT_NUGETS_PATH); foreach (var id in TRACKED_NUGETS.Keys) { var version = GetVersion (id); diff --git a/cake/UtilsManaged.cake b/cake/UtilsManaged.cake index 5d974e499d..caa9e043e7 100644 --- a/cake/UtilsManaged.cake +++ b/cake/UtilsManaged.cake @@ -4,7 +4,7 @@ void PackageNuGet(FilePath nuspecPath, DirectoryPath outputPath, bool allowDefau var settings = new NuGetPackSettings { OutputDirectory = MakeAbsolute(outputPath), BasePath = nuspecPath.GetDirectory(), - ToolPath = NuGetToolPath, + ToolPath = NUGET_EXE, Properties = new Dictionary { // NU5048: The 'PackageIconUrl'/'iconUrl' element is deprecated. Consider using the 'PackageIcon'/'icon' element instead. // NU5105: The package version 'xxx' uses SemVer 2.0.0 or components of SemVer 1.0.0 that are not supported on legacy clients. @@ -18,25 +18,6 @@ void PackageNuGet(FilePath nuspecPath, DirectoryPath outputPath, bool allowDefau NuGetPack(nuspecPath, settings); } -void RunNuGetRestorePackagesConfig(FilePath sln) -{ - var dir = sln.GetDirectory(); - - var nugetSources = new [] { OUTPUT_NUGETS_PATH.FullPath, "https://api.nuget.org/v3/index.json" }; - - EnsureDirectoryExists(OUTPUT_NUGETS_PATH); - - var settings = new NuGetRestoreSettings { - ToolPath = NuGetToolPath, - Source = nugetSources, - NoCache = true, - PackagesDirectory = dir.Combine("packages"), - }; - - foreach (var config in GetFiles(dir + "/**/packages.config")) - NuGetRestore(config, settings); -} - void RunTests(FilePath testAssembly, bool is32) { var dir = testAssembly.GetDirectory(); @@ -104,7 +85,7 @@ void RunNetCorePublish(FilePath testProject, DirectoryPath output) void RunCodeCoverage(string testResultsGlob, DirectoryPath output) { try { - RunProcess ("reportgenerator", new ProcessSettings { + RunProcess("reportgenerator", new ProcessSettings { Arguments = $"-reports:{testResultsGlob} " + $"-targetdir:{output} " + @@ -112,13 +93,13 @@ void RunCodeCoverage(string testResultsGlob, DirectoryPath output) $"-assemblyfilters:-*.Tests" }); } catch (Exception ex) { - Error ("Make sure to install the 'dotnet-reportgenerator-globaltool' .NET Core global tool."); - Error (ex); + Error("Make sure to install the 'dotnet-reportgenerator-globaltool' .NET Core global tool."); + Error(ex); throw; } var xml = $"{output}/Cobertura.xml"; - var root = FindRegexMatchGroupsInFile (xml, @"(.*)<\/source>", 0)[1].Value; - ReplaceTextInFiles (xml, root, ""); + var root = FindRegexMatchGroupsInFile(xml, @"(.*)<\/source>", 0)[1].Value; + ReplaceTextInFiles(xml, root, ""); } IEnumerable<(string Name, string Value)> CreateTraitsDictionary(string args) @@ -260,17 +241,59 @@ async Task CreateNuGetDiffAsync() } } -string GetDownloadUrl(string id) +async Task DownloadPackageAsync(string id, DirectoryPath outputDirectory) { var version = "0.0.0-"; - if (!string.IsNullOrEmpty (PREVIEW_LABEL) && PREVIEW_LABEL.StartsWith ("pr.")) - version += PREVIEW_LABEL.ToLower (); - else if (!string.IsNullOrEmpty (GIT_SHA)) - version += "commit." + GIT_SHA.ToLower (); - else if (!string.IsNullOrEmpty (GIT_BRANCH_NAME)) - version += "branch." + GIT_BRANCH_NAME.Replace ("/", ".").ToLower (); + if (!string.IsNullOrEmpty(PREVIEW_LABEL) && PREVIEW_LABEL.StartsWith("pr.")) + version += PREVIEW_LABEL.ToLower(); + else if (!string.IsNullOrEmpty(GIT_SHA)) + version += "commit." + GIT_SHA.ToLower(); + else if (!string.IsNullOrEmpty(GIT_BRANCH_NAME)) + version += "branch." + GIT_BRANCH_NAME.Replace("/", ".").ToLower(); else version += "branch.main"; + version += ".*"; + + var filter = new NuGetVersions.Filter { + IncludePrerelease = true, + SourceUrl = PREVIEW_FEED_URL, + VersionRange = VersionRange.Parse(version), + }; + + var latestVersion = await NuGetVersions.GetLatestAsync(id, filter); - return string.Format (PREVIEW_FEED_URL, id.ToLower(), version); + var comparer = new NuGetDiff(PREVIEW_FEED_URL); + comparer.PackageCache = PACKAGE_CACHE_PATH.FullPath; + + await Download(id, latestVersion); + + async Task Download(string currentId, NuGetVersion currentVersion) + { + currentId = currentId.ToLower(); + + Information($"Downloading: {currentId}..."); + + var root = await comparer.ExtractCachedPackageAsync(currentId, currentVersion); + var toolsDir = $"{root}/tools/"; + if (DirectoryExists(toolsDir)) { + var allFiles = GetFiles(toolsDir + "**/*"); + foreach (var file in allFiles) { + var relative = MakeAbsolute(Directory(toolsDir)).GetRelativePath(file); + var dir = $"{outputDirectory}/{relative.GetDirectory()}"; + EnsureDirectoryExists(dir); + CopyFileToDirectory(file, dir); + } + } + + var nuspec = $"{root}/{currentId}.nuspec"; + var xdoc = XDocument.Load(nuspec); + var xmlns = xdoc.Root.Name.Namespace; + var dependencies = xdoc.Root.Descendants(xmlns + "dependency").ToArray(); + + foreach (var dep in dependencies) { + var depId = dep.Attribute("id").Value; + var depVersion = dep.Attribute("version").Value; + await Download(depId, NuGetVersion.Parse(depVersion)); + } + } } diff --git a/cake/externals.cake b/cake/externals.cake index bea85eaa39..b0f8429f8b 100644 --- a/cake/externals.cake +++ b/cake/externals.cake @@ -12,6 +12,7 @@ foreach (var cake in GetFiles("native/*/build.cake")) var task = Task($"externals-{native}") .WithCriteria(should) + .WithCriteria(!SKIP_BUILD) .Does(() => RunCake(localCake, "Default")); externalsTask.IsDependentOn(task); @@ -25,19 +26,12 @@ Task("externals-osx") //////////////////////////////////////////////////////////////////////////////////////////////////// Task("externals-download") - .Does(() => + .Does(async () => { EnsureDirectoryExists ("./output"); CleanDirectories ("./output"); - EnsureDirectoryExists ("./output/temp"); - - var url = GetDownloadUrl ("_nativeassets"); - DownloadFile (url, "./output/temp/nativeassets.nupkg"); - - Unzip ("./output/temp/nativeassets.nupkg", "./output/temp"); - MoveDirectory ("./output/temp/tools", "./output/native"); - DeleteDirectory("./output/temp", new DeleteDirectorySettings { Recursive = true, Force = true }); + await DownloadPackageAsync("_nativeassets", "./output/native"); }); //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/cake/msbuild.cake b/cake/msbuild.cake index 6caf4252a8..8e99484450 100644 --- a/cake/msbuild.cake +++ b/cake/msbuild.cake @@ -2,6 +2,29 @@ DirectoryPath PACKAGE_CACHE_PATH = MakeAbsolute(ROOT_PATH.Combine("externals/pac DirectoryPath OUTPUT_NUGETS_PATH = MakeAbsolute(ROOT_PATH.Combine("output/nugets")); DirectoryPath OUTPUT_SPECIAL_NUGETS_PATH = MakeAbsolute(ROOT_PATH.Combine("output/special-nugets")); +var NUGETS_SOURCES = new [] { + OUTPUT_NUGETS_PATH.FullPath, + "https://api.nuget.org/v3/index.json", + "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" +}; + +void RunNuGetRestorePackagesConfig(FilePath sln) +{ + var dir = sln.GetDirectory(); + + EnsureDirectoryExists(OUTPUT_NUGETS_PATH); + + var settings = new NuGetRestoreSettings { + ToolPath = NUGET_EXE, + Source = NUGETS_SOURCES, + NoCache = true, + PackagesDirectory = dir.Combine("packages"), + }; + + foreach (var config in GetFiles(dir + "/**/packages.config")) + NuGetRestore(config, settings); +} + void RunMSBuild( FilePath solution, string platform = "Any CPU", @@ -12,12 +35,6 @@ void RunMSBuild( string configuration = null, Dictionary properties = null) { - var nugetSources = new [] { - OUTPUT_NUGETS_PATH.FullPath, - "https://api.nuget.org/v3/index.json", - "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" - }; - EnsureDirectoryExists(OUTPUT_NUGETS_PATH); MSBuild(solution, c => { @@ -68,8 +85,8 @@ void RunMSBuild( c.Properties [prop.Key] = new [] { prop.Value }; } } - // c.Properties ["RestoreSources"] = nugetSources; + // c.Properties ["RestoreSources"] = NUGETS_SOURCES; var sep = IsRunningOnWindows() ? ";" : "%3B"; - c.ArgumentCustomization = args => args.Append($"/p:RestoreSources=\"{string.Join(sep, nugetSources)}\""); + c.ArgumentCustomization = args => args.Append($"/p:RestoreSources=\"{string.Join(sep, NUGETS_SOURCES)}\""); }); } diff --git a/cake/shared.cake b/cake/shared.cake index a346cf49b6..6b633401ff 100644 --- a/cake/shared.cake +++ b/cake/shared.cake @@ -7,6 +7,7 @@ var CONFIGURATION = Argument("c", Argument("configuration", "Release")); var VS_INSTALL = Argument("vsinstall", EnvironmentVariable("VS_INSTALL")); var MSBUILD_EXE = Argument("msbuild", EnvironmentVariable("MSBUILD_EXE")); +var NUGET_EXE = Argument("nuget", EnvironmentVariable("NUGET_EXE") ?? Context.Tools.Resolve ("nuget.exe")); var CAKE_ARGUMENTS = (IReadOnlyDictionary)Context.Arguments .GetType() diff --git a/externals/skia b/externals/skia index cecf0b0ccf..457fddd486 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit cecf0b0ccf451b67e67695ec2c5031eb3ea52028 +Subproject commit 457fddd486d12d91e63d490d28707e3bb9e9f991 diff --git a/nuget/_NativeAssets.nuspec b/nuget/_NativeAssets.nuspec index c29cf6e657..f135ed9b5e 100644 --- a/nuget/_NativeAssets.nuspec +++ b/nuget/_NativeAssets.nuspec @@ -5,14 +5,18 @@ _NativeAssets Build Native Assets 1.0.0 - All the native assets from the build - All the native assets from the build + All the native assets from the build. + All the native assets from the build. Microsoft + + + + - + \ No newline at end of file diff --git a/nuget/_NuGets.nuspec b/nuget/_NuGets.nuspec index 974104a7bb..1c6ac6cd23 100644 --- a/nuget/_NuGets.nuspec +++ b/nuget/_NuGets.nuspec @@ -5,8 +5,8 @@ _NuGets Build NuGets 1.0.0 - All the NuGet packages from the build - All the NuGet packages from the build + All the stable NuGet packages from the build. + All the stable NuGet packages from the build. Microsoft diff --git a/scripts/azure-pipelines.yml b/scripts/azure-pipelines.yml index 1c9f05ec37..38e1cc8c4c 100644 --- a/scripts/azure-pipelines.yml +++ b/scripts/azure-pipelines.yml @@ -311,7 +311,7 @@ stages: displayName: Managed (Windows) vmImage: $(VM_IMAGE_WINDOWS) target: libs - additionalArgs: --exclusive + additionalArgs: --skipExternals="all" requiredArtifacts: - native_android_x86_windows - native_android_x64_windows @@ -337,7 +337,7 @@ stages: displayName: Managed (macOS) vmImage: $(VM_IMAGE_MAC) target: libs - additionalArgs: --exclusive + additionalArgs: --skipExternals="all" requiredArtifacts: - native_android_x86_macos - native_android_x64_macos @@ -357,7 +357,7 @@ stages: vmImage: $(VM_IMAGE_LINUX) packages: $(MANAGED_LINUX_PACKAGES) target: libs - additionalArgs: --exclusive + additionalArgs: --skipExternals="all" requiredArtifacts: - native_linux_x64_linux - native_linux_arm_linux @@ -381,7 +381,7 @@ stages: displayName: Package NuGets vmImage: $(VM_IMAGE_WINDOWS) target: nuget - additionalArgs: --packall=true --exclusive + additionalArgs: --packall=true --skipbuild=true installWindowsSdk: false shouldPublish: true requiredArtifacts: @@ -567,7 +567,10 @@ stages: --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json ` --version "1.0.0-prerelease*" displayName: Install the xharness .NET Core tool - - bash: sh -c "echo \"y\" | $ANDROID_HOME/tools/bin/sdkmanager \"system-images;android-30;google_apis_playstore;x86\"" + - bash: | + sh -c "echo \"y\" | $ANDROID_HOME/tools/bin/sdkmanager \"emulator\" \"system-images;android-30;google_apis_playstore;x86\"" + echo "##vso[task.prependpath]$ANDROID_HOME/tools/bin" + echo "##vso[task.prependpath]$ANDROID_HOME/emulator" displayName: Install the Android emulator postBuildSteps: - task: PublishTestResults@2 diff --git a/source/SkiaSharp.Views/SkiaSharp.Views.Android/AndroidExtensions.cs b/source/SkiaSharp.Views/SkiaSharp.Views.Android/AndroidExtensions.cs index b110d6827e..dc93136a4c 100644 --- a/source/SkiaSharp.Views/SkiaSharp.Views.Android/AndroidExtensions.cs +++ b/source/SkiaSharp.Views/SkiaSharp.Views.Android/AndroidExtensions.cs @@ -139,9 +139,9 @@ public static Bitmap ToBitmap(this SKBitmap skiaBitmap) } } - public static Bitmap ToBitmap(this SKPixmap skiaPixmap) + public static Bitmap ToBitmap(this SKImage skiaImage) { - var info = skiaPixmap.Info; + var info = skiaImage.Info; // destination values var config = Bitmap.Config.Argb8888; @@ -172,7 +172,7 @@ public static Bitmap ToBitmap(this SKPixmap skiaPixmap) var ptr = bmp.LockPixels(); // copy - var success = skiaPixmap.ReadPixels(dstInfo, ptr, dstInfo.RowBytes); + var success = skiaImage.ReadPixels(dstInfo, ptr, dstInfo.RowBytes); // confirm bmp.UnlockPixels(); @@ -183,15 +183,15 @@ public static Bitmap ToBitmap(this SKPixmap skiaPixmap) bmp = null; } + GC.KeepAlive(skiaImage); return bmp; } - public static Bitmap ToBitmap(this SKImage skiaImage) + public static Bitmap ToBitmap(this SKPixmap skiaPixamp) { - using (var pixmap = skiaImage.PeekPixels()) + using (var image = SKImage.FromPixels(skiaPixamp)) { - var bmp = pixmap.ToBitmap(); - GC.KeepAlive(skiaImage); + var bmp = image.ToBitmap(); return bmp; } } diff --git a/tests/SkiaSharp.Android.Tests.sln b/tests/SkiaSharp.Android.Tests.sln index 711a23cb5b..ba713466a8 100644 --- a/tests/SkiaSharp.Android.Tests.sln +++ b/tests/SkiaSharp.Android.Tests.sln @@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Android", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Forms.Android", "..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Android\SkiaSharp.Views.Forms.Android.csproj", "{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp", "..\binding\HarfBuzzSharp\HarfBuzzSharp.csproj", "{814F4C3B-0767-4A6C-B274-71EAD305D2BF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp", "..\binding\SkiaSharp\SkiaSharp.csproj", "{B9C1E6E5-D56B-4B10-946C-6493AE1C57FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +51,14 @@ Global {F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Release|Any CPU.Build.0 = Release|Any CPU + {814F4C3B-0767-4A6C-B274-71EAD305D2BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {814F4C3B-0767-4A6C-B274-71EAD305D2BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {814F4C3B-0767-4A6C-B274-71EAD305D2BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {814F4C3B-0767-4A6C-B274-71EAD305D2BF}.Release|Any CPU.Build.0 = Release|Any CPU + {B9C1E6E5-D56B-4B10-946C-6493AE1C57FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9C1E6E5-D56B-4B10-946C-6493AE1C57FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9C1E6E5-D56B-4B10-946C-6493AE1C57FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9C1E6E5-D56B-4B10-946C-6493AE1C57FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/SkiaSharp.Android.Tests/AndroidExtensionsTests.cs b/tests/SkiaSharp.Android.Tests/AndroidExtensionsTests.cs new file mode 100644 index 0000000000..f39f35729c --- /dev/null +++ b/tests/SkiaSharp.Android.Tests/AndroidExtensionsTests.cs @@ -0,0 +1,75 @@ +using Xunit; + +namespace SkiaSharp.Views.Android.Tests +{ + public class AndroidExtensionsTests : AndroidTests + { + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void PixelBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var image = SKImage.FromBitmap(bitmap); + + using var androidBitmap = image.ToBitmap(); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void BitmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + + using var androidBitmap = bitmap.ToBitmap(); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void PixmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var pixmap = bitmap.PeekPixels(); + + using var androidBitmap = pixmap.ToBitmap(); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void EncodedDataBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); + using var image = SKImage.FromEncodedData(data); + + using var androidBitmap = image.ToBitmap(); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + } +} diff --git a/tests/SkiaSharp.Android.Tests/AndroidTests.cs b/tests/SkiaSharp.Android.Tests/AndroidTests.cs new file mode 100644 index 0000000000..68c30f30ed --- /dev/null +++ b/tests/SkiaSharp.Android.Tests/AndroidTests.cs @@ -0,0 +1,26 @@ +using Android.Graphics; +using SkiaSharp.Tests; +using Xunit; + +namespace SkiaSharp.Views.Android.Tests +{ + public abstract class AndroidTests : SKTest + { + protected static void ValidateTestBitmap(Bitmap bmp, byte alpha = 255) + { + Assert.NotNull(bmp); + Assert.Equal(40, bmp.Width); + Assert.Equal(40, bmp.Height); + + Assert.Equal(Get(SKColors.Red), (uint)bmp.GetPixel(10, 10)); + Assert.Equal(Get(SKColors.Green), (uint)bmp.GetPixel(30, 10)); + Assert.Equal(Get(SKColors.Blue), (uint)bmp.GetPixel(10, 30)); + Assert.Equal(Get(SKColors.Yellow), (uint)bmp.GetPixel(30, 30)); + + SKColor Get(SKColor color) => + alpha == 0 + ? SKColor.Empty + : color.WithAlpha(alpha); + } + } +} diff --git a/tests/SkiaSharp.Android.Tests/FormsExtensionsTests.cs b/tests/SkiaSharp.Android.Tests/FormsExtensionsTests.cs new file mode 100644 index 0000000000..887a67f413 --- /dev/null +++ b/tests/SkiaSharp.Android.Tests/FormsExtensionsTests.cs @@ -0,0 +1,93 @@ +using System.Threading.Tasks; +using Android.App; +using SkiaSharp.Views.Android.Tests; +using Xunit; + +namespace SkiaSharp.Views.Forms.Tests +{ + public class SKImageSourceHandlerTests : AndroidTests + { + private readonly SKImageSourceHandler handler; + + public SKImageSourceHandlerTests() + { + handler = new SKImageSourceHandler(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task PixelBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var image = SKImage.FromBitmap(bitmap); + + var source = (SKImageImageSource)image; + + using var androidBitmap = await handler.LoadImageAsync(source, Application.Context); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task BitmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + + var source = (SKBitmapImageSource)bitmap; + + using var androidBitmap = await handler.LoadImageAsync(source, Application.Context); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task PixmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var pixmap = bitmap.PeekPixels(); + + var source = (SKPixmapImageSource)pixmap; + + using var androidBitmap = await handler.LoadImageAsync(source, Application.Context); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task EncodedDataBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); + using var image = SKImage.FromEncodedData(data); + + var source = (SKImageImageSource)image; + + using var androidBitmap = await handler.LoadImageAsync(source, Application.Context); + + ValidateTestBitmap(androidBitmap, alpha); + + androidBitmap.Recycle(); + } + } +} diff --git a/tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj b/tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj index 0f8c393c52..748555788a 100644 --- a/tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj +++ b/tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj @@ -94,6 +94,9 @@ + + + diff --git a/tests/SkiaSharp.iOS.Tests.sln b/tests/SkiaSharp.iOS.Tests.sln index 0e5e067e67..3f0330ab20 100644 --- a/tests/SkiaSharp.iOS.Tests.sln +++ b/tests/SkiaSharp.iOS.Tests.sln @@ -3,100 +3,128 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.808.5 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.iOS", "..\binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj", "{A4146A87-DB60-4A17-A179-0E2E4255A08E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.iOS", "..\binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj", "{A4146A87-DB60-4A17-A179-0E2E4255A08E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HarfBuzzSharp.iOS", "..\binding\HarfBuzzSharp.iOS\HarfBuzzSharp.iOS.csproj", "{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp.iOS", "..\binding\HarfBuzzSharp.iOS\HarfBuzzSharp.iOS.csproj", "{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.HarfBuzz", "..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.HarfBuzz", "..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.iOS", "..\source\SkiaSharp.Views\SkiaSharp.Views.iOS\SkiaSharp.Views.iOS.csproj", "{549F8E22-A756-4E99-A84A-C4E74832DA95}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.iOS", "..\source\SkiaSharp.Views\SkiaSharp.Views.iOS\SkiaSharp.Views.iOS.csproj", "{549F8E22-A756-4E99-A84A-C4E74832DA95}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.Forms.iOS", "..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.iOS\SkiaSharp.Views.Forms.iOS.csproj", "{0254162B-6B4A-459E-BD96-3A42A104C144}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Forms.iOS", "..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.iOS\SkiaSharp.Views.Forms.iOS.csproj", "{0254162B-6B4A-459E-BD96-3A42A104C144}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.iOS.Tests", "SkiaSharp.iOS.Tests\SkiaSharp.iOS.Tests.csproj", "{B73EB308-70BE-49FD-91A7-1D1495663D6D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp", "..\binding\HarfBuzzSharp\HarfBuzzSharp.csproj", "{5D79739D-98C6-48A4-965E-064DB4C59955}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp", "..\binding\SkiaSharp\SkiaSharp.csproj", "{A694283C-FE40-4049-88AB-2CAE678FC087}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - Debug|iPhoneSimulator = Debug|iPhoneSimulator - Release|iPhoneSimulator = Release|iPhoneSimulator Debug|iPhone = Debug|iPhone + Debug|iPhoneSimulator = Debug|iPhoneSimulator + Release|Any CPU = Release|Any CPU Release|iPhone = Release|iPhone + Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.Build.0 = Release|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhone.ActiveCfg = Debug|Any CPU {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhone.Build.0 = Debug|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.Build.0 = Release|Any CPU {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhone.ActiveCfg = Release|Any CPU {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhone.Build.0 = Release|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.Build.0 = Release|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhone.ActiveCfg = Debug|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhone.Build.0 = Debug|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.Build.0 = Release|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhone.ActiveCfg = Release|Any CPU {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhone.Build.0 = Release|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.Build.0 = Release|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhone.ActiveCfg = Debug|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhone.Build.0 = Debug|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.Build.0 = Release|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhone.ActiveCfg = Release|Any CPU {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhone.Build.0 = Release|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.Build.0 = Release|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhone.ActiveCfg = Debug|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhone.Build.0 = Debug|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.Build.0 = Release|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhone.ActiveCfg = Release|Any CPU {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhone.Build.0 = Release|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.Build.0 = Release|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhone.ActiveCfg = Debug|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhone.Build.0 = Debug|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.Build.0 = Release|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhone.ActiveCfg = Release|Any CPU {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhone.Build.0 = Release|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhone.ActiveCfg = Debug|iPhone {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhone.Build.0 = Debug|iPhone + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhone.ActiveCfg = Release|iPhone {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhone.Build.0 = Release|iPhone + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator + {B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|iPhone.Build.0 = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|Any CPU.Build.0 = Release|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|iPhone.ActiveCfg = Release|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|iPhone.Build.0 = Release|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {5D79739D-98C6-48A4-965E-064DB4C59955}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|iPhone.Build.0 = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|Any CPU.Build.0 = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|iPhone.ActiveCfg = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|iPhone.Build.0 = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {A694283C-FE40-4049-88AB-2CAE678FC087}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/SkiaSharp.iOS.Tests/FormsExtensionsTests.cs b/tests/SkiaSharp.iOS.Tests/FormsExtensionsTests.cs new file mode 100644 index 0000000000..db22bc2841 --- /dev/null +++ b/tests/SkiaSharp.iOS.Tests/FormsExtensionsTests.cs @@ -0,0 +1,84 @@ +using System.Threading.Tasks; +using SkiaSharp.Views.iOS.Tests; +using Xunit; + +namespace SkiaSharp.Views.Forms.Tests +{ + public class SKImageSourceHandlerTests : iOSTests + { + private readonly SKImageSourceHandler handler; + + public SKImageSourceHandlerTests() + { + handler = new SKImageSourceHandler(); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task PixelBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var image = SKImage.FromBitmap(bitmap); + + var source = (SKImageImageSource)image; + + using var uiImage = await handler.LoadImageAsync(source); + + ValidateTestBitmap(uiImage, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task BitmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + + var source = (SKBitmapImageSource)bitmap; + + using var uiImage = await handler.LoadImageAsync(source); + + ValidateTestBitmap(uiImage, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task PixmapToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var pixmap = bitmap.PeekPixels(); + + var source = (SKPixmapImageSource)pixmap; + + using var uiImage = await handler.LoadImageAsync(source); + + ValidateTestBitmap(uiImage, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public async Task EncodedDataBackedImageToBitmap(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); + using var image = SKImage.FromEncodedData(data); + + var source = (SKImageImageSource)image; + + using var uiImage = await handler.LoadImageAsync(source); + + ValidateTestBitmap(uiImage, alpha); + } + } +} diff --git a/tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj b/tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj index 15fe8b87d1..33ac37130b 100644 --- a/tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj +++ b/tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj @@ -94,9 +94,13 @@ + + + + diff --git a/tests/SkiaSharp.iOS.Tests/TestExtensions.cs b/tests/SkiaSharp.iOS.Tests/TestExtensions.cs new file mode 100644 index 0000000000..283cd0d1eb --- /dev/null +++ b/tests/SkiaSharp.iOS.Tests/TestExtensions.cs @@ -0,0 +1,26 @@ +using CoreGraphics; + +namespace SkiaSharp.Views.iOS.Tests +{ + public static class TestExtensions + { + public static SKColor GetPixel(this CGImage cgImage, int x, int y) + { + var data = cgImage.DataProvider.CopyData(); + + var bytesPerPixel = cgImage.BitsPerPixel / cgImage.BitsPerComponent; + + var offset = (y * cgImage.BytesPerRow) + (x * bytesPerPixel); + + var a = data[offset + 3]; + var r = data[offset + 0]; + var g = data[offset + 1]; + var b = data[offset + 2]; + + if (a == 0) + return SKColor.Empty; + + return (SKColor)new SKColorF((float)r / a, (float)g / a, (float)b / a, a / 255f); + } + } +} diff --git a/tests/SkiaSharp.iOS.Tests/iOSExtensionsTests.cs b/tests/SkiaSharp.iOS.Tests/iOSExtensionsTests.cs new file mode 100644 index 0000000000..247d91e654 --- /dev/null +++ b/tests/SkiaSharp.iOS.Tests/iOSExtensionsTests.cs @@ -0,0 +1,67 @@ +using Xunit; + +namespace SkiaSharp.Views.iOS.Tests +{ + public class iOSExtensionsTests : iOSTests + { + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void PixelBackedImageToUIImage(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var image = SKImage.FromBitmap(bitmap); + + using var iosBitmap = image.ToUIImage(); + + ValidateTestBitmap(iosBitmap, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void BitmapToUIImage(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + + using var uiImage = bitmap.ToUIImage(); + + ValidateTestBitmap(uiImage, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void PixmapToUIImage(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var pixmap = bitmap.PeekPixels(); + + using var uiImage = pixmap.ToUIImage(); + + ValidateTestBitmap(uiImage, alpha); + } + + [SkippableTheory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + [InlineData(255)] + public void EncodedDataBackedImageToUIImage(byte alpha) + { + using var bitmap = CreateTestBitmap(alpha); + using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); + using var image = SKImage.FromEncodedData(data); + + using var uiImage = image.ToUIImage(); + + ValidateTestBitmap(uiImage, alpha); + } + } +} diff --git a/tests/SkiaSharp.iOS.Tests/iOSTests.cs b/tests/SkiaSharp.iOS.Tests/iOSTests.cs new file mode 100644 index 0000000000..726652cbe5 --- /dev/null +++ b/tests/SkiaSharp.iOS.Tests/iOSTests.cs @@ -0,0 +1,34 @@ +using CoreGraphics; +using SkiaSharp.Tests; +using UIKit; +using Xunit; + +namespace SkiaSharp.Views.iOS.Tests +{ + public abstract class iOSTests : SKTest + { + protected static void ValidateTestBitmap(UIImage uiImage, byte alpha = 255) + { + var cgImage = uiImage.CGImage; + + ValidateTestBitmap(cgImage, alpha); + } + + protected static void ValidateTestBitmap(CGImage cgImage, byte alpha = 255) + { + Assert.NotNull(cgImage); + Assert.Equal(40, cgImage.Width); + Assert.Equal(40, cgImage.Height); + + Assert.Equal(Get(SKColors.Red), cgImage.GetPixel(10, 10)); + Assert.Equal(Get(SKColors.Green), cgImage.GetPixel(30, 10)); + Assert.Equal(Get(SKColors.Blue), cgImage.GetPixel(10, 30)); + Assert.Equal(Get(SKColors.Yellow), cgImage.GetPixel(30, 30)); + + SKColor Get(SKColor color) => + alpha == 0 + ? SKColor.Empty + : color.WithAlpha(alpha); + } + } +} diff --git a/tests/Tests/GRContextTest.cs b/tests/Tests/GRContextTest.cs index 64504299cd..dfc5d11ca8 100644 --- a/tests/Tests/GRContextTest.cs +++ b/tests/Tests/GRContextTest.cs @@ -19,6 +19,23 @@ public void CreateDefaultContextIsValid() } } + [Trait(CategoryKey, GpuCategory)] + [SkippableFact] + public void AbandonContextIsAbandoned() + { + using (var ctx = CreateGlContext()) { + ctx.MakeCurrent(); + + var grContext = GRContext.CreateGl(); + + Assert.False(grContext.IsAbandoned); + + grContext.AbandonContext(); + + Assert.True(grContext.IsAbandoned); + } + } + [Trait(CategoryKey, GpuCategory)] [SkippableFact] public void CreateDefaultContextWithOptionsIsValid()