Merge branch 'main' into develop

This commit is contained in:
Matthew Leibowitz 2021-03-12 02:48:53 +02:00
Родитель 5c539d553b ec4f45adc3
Коммит 120b63c466
53 изменённых файлов: 1698 добавлений и 293 удалений

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

@ -306,6 +306,8 @@ namespace SkiaSharp
{
if (stream == null)
throw new ArgumentNullException (nameof (stream));
if (stream is SKFileStream filestream && !filestream.IsValid)
throw new ArgumentException ("File stream was not valid.", nameof(stream));
fixed (SKCodecResult* r = &result) {
var codec = GetObject (SkiaApi.sk_codec_new_from_stream (stream.Handle, r));

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

@ -511,6 +511,9 @@ namespace SkiaSharp
public SKData EncodedData =>
SKData.GetObject (SkiaApi.sk_image_ref_encoded (Handle));
public SKImageInfo Info =>
new SKImageInfo (Width, Height, ColorType, AlphaType, ColorSpace);
// ToShader
public SKShader ToShader () =>

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

@ -137,6 +137,13 @@ Task ("libs")
Task ("tests")
.Description ("Run all tests.")
.IsDependentOn ("tests-netfx")
.IsDependentOn ("tests-netcore")
.IsDependentOn ("tests-android")
.IsDependentOn ("tests-ios");
Task ("tests-netfx")
.Description ("Run all Full .NET Framework tests.")
.IsDependentOn ("externals")
.Does (() =>
{
@ -166,7 +173,6 @@ Task ("tests")
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
// Full .NET Framework
if (IsRunningOnWindows ()) {
RunDesktopTest ("x86");
RunDesktopTest ("x64");
@ -176,7 +182,23 @@ Task ("tests")
RunDesktopTest ("x64");
}
// .NET Core
if (failedTests > 0) {
if (THROW_ON_TEST_FAILURE)
throw new Exception ($"There were {failedTests} failed tests.");
else
Warning ($"There were {failedTests} failed tests.");
}
});
Task ("tests-netcore")
.Description ("Run all .NET Core tests.")
.IsDependentOn ("externals")
.Does (() =>
{
var failedTests = 0;
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
// SkiaSharp.NetCore.Tests.csproj
RunMSBuild ("./tests/SkiaSharp.NetCore.Tests.sln");
@ -195,25 +217,94 @@ Task ("tests")
}
}
if (failedTests > 0)
if (failedTests > 0) {
if (THROW_ON_TEST_FAILURE)
throw new Exception ($"There were {failedTests} failed tests.");
else
Warning ($"There were {failedTests} failed tests.");
}
if (COVERAGE) {
try {
RunProcess ("reportgenerator", new ProcessSettings {
Arguments = "-reports:./tests/**/Coverage/**/*.xml -targetdir:./output/coverage -reporttypes:HtmlInline_AzurePipelines;Cobertura"
});
} catch (Exception ex) {
Error ("Make sure to install the 'dotnet-reportgenerator-globaltool' .NET Core global tool.");
Error (ex);
throw;
}
var xml = "./output/coverage/Cobertura.xml";
var root = FindRegexMatchGroupsInFile (xml, @"<source>(.*)<\/source>", 0)[1].Value;
ReplaceTextInFiles (xml, root, "");
RunCodeCoverage ("./tests/**/Coverage/**/*.xml", "./output/coverage");
}
});
Task ("tests-android")
.Description ("Run all Android tests.")
.IsDependentOn ("externals")
.Does (() =>
{
var failedTests = 0;
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
// SkiaSharp.Android.Tests.csproj
try {
// build the solution to copy all the files
RunMSBuild ("./tests/SkiaSharp.Android.Tests.sln", configuration: "Debug");
// package the app
FilePath csproj = "./tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj";
RunMSBuild (csproj,
targets: new [] { "SignAndroidPackage" },
platform: "AnyCPU",
configuration: "Debug");
// run the tests
DirectoryPath results = "./output/testlogs/SkiaSharp.Android.Tests";
RunCake ("./cake/xharness-android.cake", "Default", new Dictionary<string, string> {
{ "project", MakeAbsolute(csproj).FullPath },
{ "configuration", "Debug" },
{ "exclusive", "true" },
{ "results", MakeAbsolute(results).FullPath },
});
} catch {
failedTests++;
}
if (failedTests > 0) {
if (THROW_ON_TEST_FAILURE)
throw new Exception ($"There were {failedTests} failed tests.");
else
Warning ($"There were {failedTests} failed tests.");
}
});
Task ("tests-ios")
.Description ("Run all iOS tests.")
.IsDependentOn ("externals")
.Does (() =>
{
var failedTests = 0;
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
// SkiaSharp.iOS.Tests.csproj
try {
// build the solution to copy all the files
RunMSBuild ("./tests/SkiaSharp.iOS.Tests.sln", configuration: "Debug");
// package the app
FilePath csproj = "./tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj";
RunMSBuild (csproj,
properties: new Dictionary<string, string> { { "BuildIpa", "true" } },
platform: "iPhoneSimulator",
configuration: "Debug");
// run the tests
DirectoryPath results = "./output/testlogs/SkiaSharp.iOS.Tests";
RunCake ("./cake/xharness-ios.cake", "Default", new Dictionary<string, string> {
{ "project", MakeAbsolute(csproj).FullPath },
{ "configuration", "Debug" },
{ "exclusive", "true" },
{ "results", MakeAbsolute(results).FullPath },
});
} catch {
failedTests++;
}
if (failedTests > 0) {
if (THROW_ON_TEST_FAILURE)
throw new Exception ($"There were {failedTests} failed tests.");
else
Warning ($"There were {failedTests} failed tests.");
}
});
@ -241,11 +332,12 @@ Task ("tests-wasm")
serverProc?.Kill();
}
if (failedTests > 0)
if (failedTests > 0) {
if (THROW_ON_TEST_FAILURE)
throw new Exception ($"There were {failedTests} failed tests.");
else
Warning ($"There were {failedTests} failed tests.");
}
});
////////////////////////////////////////////////////////////////////////////////////////////////////

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

@ -101,6 +101,26 @@ void RunNetCorePublish(FilePath testProject, DirectoryPath output)
DotNetCorePublish(testProject.GetFilename().ToString(), settings);
}
void RunCodeCoverage(string testResultsGlob, DirectoryPath output)
{
try {
RunProcess ("reportgenerator", new ProcessSettings {
Arguments =
$"-reports:{testResultsGlob} " +
$"-targetdir:{output} " +
$"-reporttypes:HtmlInline_AzurePipelines;Cobertura " +
$"-assemblyfilters:-*.Tests"
});
} catch (Exception 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>(.*)<\/source>", 0)[1].Value;
ReplaceTextInFiles (xml, root, "");
}
IEnumerable<(string Name, string Value)> CreateTraitsDictionary(string args)
{
if (!string.IsNullOrEmpty(args)) {

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

@ -7,15 +7,21 @@ void RunMSBuild(
string platform = "Any CPU",
string platformTarget = null,
bool restore = true,
bool restoreOnly = false,
bool bl = true)
bool bl = true,
string[] targets = null,
string configuration = null,
Dictionary<string, string> properties = null)
{
var nugetSources = new [] { OUTPUT_NUGETS_PATH.FullPath, "https://api.nuget.org/v3/index.json" };
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 => {
c.Configuration = CONFIGURATION;
c.Configuration = configuration ?? CONFIGURATION;
c.Verbosity = VERBOSITY;
c.MaxCpuCount = 0;
@ -33,12 +39,13 @@ void RunMSBuild(
}
c.NoLogo = VERBOSITY == Verbosity.Minimal;
c.Restore = restore;
if (restoreOnly) {
if (targets?.Length > 0) {
c.Targets.Clear();
c.Targets.Add("Restore");
} else {
c.Restore = restore;
foreach (var target in targets) {
c.Targets.Add(target);
}
}
if (!string.IsNullOrEmpty(platformTarget)) {
@ -55,6 +62,12 @@ void RunMSBuild(
c.Properties ["RestoreNoCache"] = new [] { "true" };
c.Properties ["RestorePackagesPath"] = new [] { PACKAGE_CACHE_PATH.FullPath };
if (properties != null) {
foreach (var prop in properties) {
c.Properties [prop.Key] = new [] { prop.Value };
}
}
// c.Properties ["RestoreSources"] = nugetSources;
var sep = IsRunningOnWindows() ? ";" : "%3B";
c.ArgumentCustomization = args => args.Append($"/p:RestoreSources=\"{string.Join(sep, nugetSources)}\"");

182
cake/xharness-android.cake Normal file
Просмотреть файл

@ -0,0 +1,182 @@
#addin nuget:?package=Cake.Android.Adb&version=3.2.0
#addin nuget:?package=Cake.Android.AvdManager&version=2.2.0
DirectoryPath ROOT_PATH = MakeAbsolute(Directory(".."));
#load "shared.cake"
// required
FilePath PROJECT = Argument("project", EnvironmentVariable("ANDROID_TEST_PROJECT") ?? "");
string TEST_DEVICE = Argument("device", EnvironmentVariable("ANDROID_TEST_DEVICE") ?? "android-emulator-32_30");
string DEVICE_NAME = Argument("skin", EnvironmentVariable("ANDROID_TEST_SKIN") ?? "Nexus 5X");
// optional
var TEST_APP = Argument("app", EnvironmentVariable("ANDROID_TEST_APP") ?? "");
var TEST_APP_PACKAGE_NAME = Argument("package", EnvironmentVariable("ANDROID_TEST_APP_PACKAGE_NAME") ?? "");
var TEST_APP_INSTRUMENTATION = Argument("instrumentation", EnvironmentVariable("ANDROID_TEST_APP_INSTRUMENTATION") ?? "");
var TEST_RESULTS = Argument("results", EnvironmentVariable("ANDROID_TEST_RESULTS") ?? "");
// other
string ANDROID_AVD = "DEVICE_TESTS_EMULATOR";
string DEVICE_ID = "";
string DEVICE_ARCH = "";
// set up env
var ANDROID_SDK_ROOT = Argument("android", EnvironmentVariable("ANDROID_SDK_ROOT") ?? EnvironmentVariable("ANDROID_SDK_ROOT"));
if (string.IsNullOrEmpty(ANDROID_SDK_ROOT)) {
throw new Exception("Environment variable 'ANDROID_SDK_ROOT' must be set to the Android SDK root.");
}
System.Environment.SetEnvironmentVariable("PATH",
$"{ANDROID_SDK_ROOT}/tools/bin" + System.IO.Path.PathSeparator +
$"{ANDROID_SDK_ROOT}/platform-tools" + System.IO.Path.PathSeparator +
$"{ANDROID_SDK_ROOT}/emulator" + System.IO.Path.PathSeparator +
EnvironmentVariable("PATH"));
Information("Android SDK Root: {0}", ANDROID_SDK_ROOT);
Information("Project File: {0}", PROJECT);
Information("Build Configuration: {0}", CONFIGURATION);
var avdSettings = new AndroidAvdManagerToolSettings { SdkRoot = ANDROID_SDK_ROOT };
var adbSettings = new AdbToolSettings { SdkRoot = ANDROID_SDK_ROOT };
var emuSettings = new AndroidEmulatorToolSettings { SdkRoot = ANDROID_SDK_ROOT, ArgumentCustomization = args => args.Append("-no-window") };
AndroidEmulatorProcess emulatorProcess = null;
Setup(context =>
{
Information("Test Device: {0}", TEST_DEVICE);
// determine the device characteristics
{
var working = TEST_DEVICE.Trim().ToLower();
var emulator = true;
var api = 30;
// version
if (working.IndexOf("_") is int idx && idx > 0) {
api = int.Parse(working.Substring(idx + 1));
working = working.Substring(0, idx);
}
var parts = working.Split('-');
// os
if (parts[0] != "android")
throw new Exception("Unexpected platform (expected: android) in device: " + TEST_DEVICE);
// device/emulator
if (parts[1] == "device")
emulator = false;
else if (parts[1] != "emulator" && parts[1] != "simulator")
throw new Exception("Unexpected device type (expected: device|emulator) in device: " + TEST_DEVICE);
// arch/bits
if (parts[2] == "32") {
if (emulator)
DEVICE_ARCH = "x86";
else
DEVICE_ARCH = "armeabi-v7a";
} else if (parts[2] == "64") {
if (emulator)
DEVICE_ARCH = "x86_64";
else
DEVICE_ARCH = "arm64-v8a";
}
DEVICE_ID = $"system-images;android-{api};google_apis_playstore;{DEVICE_ARCH}";
// we are not using a virtual device, so quit
if (!emulator)
return;
}
Information("Test Device ID: {0}", DEVICE_ID);
// delete the AVD first, if it exists
Information("Deleting AVD if exists: {0}...", ANDROID_AVD);
try { AndroidAvdDelete(ANDROID_AVD, avdSettings); }
catch { }
// create the new AVD
Information("Creating AVD: {0}...", ANDROID_AVD);
AndroidAvdCreate(ANDROID_AVD, DEVICE_ID, DEVICE_NAME, force: true, settings: avdSettings);
// start the emulator
Information("Starting Emulator: {0}...", ANDROID_AVD);
emulatorProcess = AndroidEmulatorStart(ANDROID_AVD, emuSettings);
// wait for it to finish booting (10 mins)
var waited = 0;
var total = 60 * 10;
while (AdbShell("getprop sys.boot_completed", adbSettings).FirstOrDefault() != "1") {
System.Threading.Thread.Sleep(1000);
Information("Wating {0}/{1} seconds for the emulator to boot up.", waited, total);
if (waited++ > total)
break;
}
Information("Waited {0} seconds for the emulator to boot up.", waited);
});
Teardown(context =>
{
// no virtual device was used
if (emulatorProcess == null)
return;
// stop and cleanup the emulator
AdbEmuKill(adbSettings);
System.Threading.Thread.Sleep(5000);
// kill the process if it has not already exited
try { emulatorProcess.Kill(); }
catch { }
// delete the AVD
try { AndroidAvdDelete(ANDROID_AVD, avdSettings); }
catch { }
});
Task("Default")
.Does(() =>
{
if (string.IsNullOrEmpty(TEST_APP)) {
if (string.IsNullOrEmpty(PROJECT.FullPath))
throw new Exception("If no app was specified, an app must be provided.");
var binDir = PROJECT.GetDirectory().Combine("bin").Combine(CONFIGURATION).FullPath;
var apps = GetFiles(binDir + "/*-Signed.apk");
if (apps.Any()) {
TEST_APP = apps.FirstOrDefault().FullPath;
} else {
apps = GetFiles(binDir + "/*.apk");
TEST_APP = apps.First().FullPath;
}
}
if (string.IsNullOrEmpty(TEST_APP_PACKAGE_NAME)) {
var appFile = (FilePath)TEST_APP;
appFile = appFile.GetFilenameWithoutExtension();
TEST_APP_PACKAGE_NAME = appFile.FullPath.Replace("-Signed", "");
}
if (string.IsNullOrEmpty(TEST_APP_INSTRUMENTATION)) {
TEST_APP_INSTRUMENTATION = TEST_APP_PACKAGE_NAME + ".TestInstrumentation";
}
if (string.IsNullOrEmpty(TEST_RESULTS)) {
TEST_RESULTS = TEST_APP + "-results";
}
Information("Test App: {0}", TEST_APP);
Information("Test App Package Name: {0}", TEST_APP_PACKAGE_NAME);
Information("Test App Instrumentation: {0}", TEST_APP_INSTRUMENTATION);
Information("Test Results Directory: {0}", TEST_RESULTS);
CleanDirectories(TEST_RESULTS);
RunProcess("xharness", "android test " +
$"--app=\"{TEST_APP}\" " +
$"--package-name=\"{TEST_APP_PACKAGE_NAME}\" " +
$"--instrumentation=\"{TEST_APP_INSTRUMENTATION}\" " +
$"--device-arch=\"{DEVICE_ARCH}\" " +
$"--output-directory=\"{TEST_RESULTS}\" " +
$"--verbosity=\"Debug\" ");
var failed = XmlPeek($"{TEST_RESULTS}/TestResults.xml", "/assemblies/assembly[@failed > 0 or @errors > 0]/@failed");
if (!string.IsNullOrEmpty(failed)) {
throw new Exception($"At least {failed} test(s) failed.");
}
});
RunTarget(TARGET);

60
cake/xharness-ios.cake Normal file
Просмотреть файл

@ -0,0 +1,60 @@
DirectoryPath ROOT_PATH = MakeAbsolute(Directory(".."));
#load "shared.cake"
// required
FilePath PROJECT = Argument("project", EnvironmentVariable("IOS_TEST_PROJECT") ?? "");
string TEST_DEVICE = Argument("device", EnvironmentVariable("IOS_TEST_DEVICE") ?? "ios-simulator-64_14.4"); // comma separated in the form <platform>-<device|simulator>[-<32|64>][_<version>] (eg: ios-simulator-64_13.4,[...])
// optional
var TEST_APP = Argument("app", EnvironmentVariable("IOS_TEST_APP") ?? "");
var TEST_RESULTS = Argument("results", EnvironmentVariable("IOS_TEST_RESULTS") ?? "");
// other
string PLATFORM = TEST_DEVICE.ToLower().Contains("simulator") ? "iPhoneSimulator" : "iPhone";
Information("Project File: {0}", PROJECT);
Task("Default")
.Does(() =>
{
if (string.IsNullOrEmpty(TEST_APP)) {
if (string.IsNullOrEmpty(PROJECT.FullPath))
throw new Exception("If no app was specified, an app must be provided.");
var binDir = PROJECT.GetDirectory().Combine("bin").Combine(PLATFORM).Combine(CONFIGURATION).FullPath;
var apps = GetDirectories(binDir + "/*.app");
TEST_APP = apps.First().FullPath;
}
if (string.IsNullOrEmpty(TEST_RESULTS)) {
TEST_RESULTS = TEST_APP + "-results";
}
Information("Test App: {0}", TEST_APP);
Information("Test Device: {0}", TEST_DEVICE);
Information("Test App: {0}", TEST_APP);
Information("Test Results Directory: {0}", TEST_RESULTS);
CleanDirectories(TEST_RESULTS);
try {
RunProcess("xharness", "apple test " +
$"--app=\"{TEST_APP}\" " +
$"--targets=\"{TEST_DEVICE}\" " +
$"--output-directory=\"{TEST_RESULTS}\" " +
$"--verbosity=\"Debug\" ");
} finally {
// ios test result files are weirdly named, so fix it up
var resultsFile = GetFiles($"{TEST_RESULTS}/xunit-test-*.xml").FirstOrDefault();
if (FileExists(resultsFile)) {
CopyFile(resultsFile, resultsFile.GetDirectory().CombineWithFilePath("TestResults.xml"));
}
}
// this _may_ not be needed, but just in case
var failed = XmlPeek($"{TEST_RESULTS}/TestResults.xml", "/assemblies/assembly[@failed > 0 or @errors > 0]/@failed");
if (!string.IsNullOrEmpty(failed)) {
throw new Exception($"At least {failed} test(s) failed.");
}
});
RunTarget(TARGET);

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

@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>SkiaSharpSample</RootNamespace>
<AssemblyName>SkiaSharpSample</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />

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

@ -5,7 +5,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{17521786-7B50-46C5-BBCE-999FDEBB45A3}</ProjectGuid>
<OutputType>WinExe</OutputType>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<RootNamespace>SkiaSharpSample</RootNamespace>
<TargetFrameworkProfile />
<LangVersion>8.0</LangVersion>

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

@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>SkiaSharpSample</RootNamespace>
<AssemblyName>SkiaSharpSample</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>

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

@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>SkiaSharpSample.GTK</RootNamespace>
<AssemblyName>SkiaSharpSample.GTK</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<RootNamespace>SkiaSharpSample</RootNamespace>
<TargetFrameworkProfile />
<LangVersion>8.0</LangVersion>

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

@ -9,7 +9,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>SkiaSharpSample.WPF</RootNamespace>
<AssemblyName>SkiaSharpSample.WPF</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>

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

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SkiaSharpSample</RootNamespace>
<AssemblyName>SkiaSharpSample</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ApplicationManifest>app.manifest</ApplicationManifest>
<TargetFrameworkProfile />

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

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SkiaSharpSample</RootNamespace>
<AssemblyName>SkiaSharpSample</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>

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

@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>SkiaSharpSample.Platform</RootNamespace>
<AssemblyName>SkiaSharpSample.Platform</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>

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

@ -22,7 +22,7 @@ variables:
MANAGED_LINUX_PACKAGES: ttf-ancient-fonts ninja-build
MONO_VERSION_MACOS: 'Latest'
MONO_VERSION_LINUX: ''
XCODE_VERSION: 12.3
XCODE_VERSION: 12.4
DOTNET_VERSION: 3.1.405
CONFIGURATION: 'Release'
VM_IMAGE_WINDOWS: windows-2019
@ -457,12 +457,32 @@ stages:
- native_linux
- native_wasm
jobs:
- template: azure-templates-bootstrapper.yml # Tests (Windows)
- template: azure-templates-bootstrapper.yml # Tests|netfx (Windows)
parameters:
name: tests_windows
displayName: Windows
name: tests_netfx_windows
displayName: Windows (.NET Framework)
vmImage: $(VM_IMAGE_WINDOWS)
target: tests
target: tests-netfx
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
installWindowsSdk: false
shouldPublish: false
requiredArtifacts:
- native_win32_x86_windows
- native_win32_x64_windows
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the .NET Framework test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Framework Tests'
- template: azure-templates-bootstrapper.yml # Tests|netcore (Windows)
parameters:
name: tests_netcore_windows
displayName: Windows (.NET Core)
vmImage: $(VM_IMAGE_WINDOWS)
target: tests-netcore
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
installWindowsSdk: false
shouldPublish: false
@ -472,13 +492,6 @@ stages:
tools:
- dotnet-reportgenerator-globaltool
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the .NET Framework test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Windows .NET Framework Tests'
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
@ -489,20 +502,18 @@ stages:
- task: PublishBuildArtifacts@1
displayName: 'Publish the code coverage results'
inputs:
artifactName: coverage_windows
artifactName: coverage_netcore_windows
pathToPublish: 'output/coverage'
- template: azure-templates-bootstrapper.yml # Tests (macOS)
- template: azure-templates-bootstrapper.yml # Tests|netfx (macOS)
parameters:
name: tests_macos
displayName: macOS
name: tests_netfx_macos
displayName: macOS (.NET Framework)
vmImage: $(VM_IMAGE_MAC)
target: tests
target: tests-netfx
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_macos_macos
tools:
- dotnet-reportgenerator-globaltool
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the Mono test results
@ -511,6 +522,19 @@ stages:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'macOS Mono Tests'
- template: azure-templates-bootstrapper.yml # Tests|netcore (macOS)
parameters:
name: tests_netcore_macos
displayName: macOS (.NET Core)
vmImage: $(VM_IMAGE_MAC)
target: tests-netcore
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_macos_macos
tools:
- dotnet-reportgenerator-globaltool
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
@ -521,21 +545,87 @@ stages:
- task: PublishBuildArtifacts@1
displayName: 'Publish the code coverage results'
inputs:
artifactName: coverage_macos
artifactName: coverage_netcore_macos
pathToPublish: 'output/coverage'
- template: azure-templates-bootstrapper.yml # Tests (Linux)
- template: azure-templates-bootstrapper.yml # Tests|android (macOS)
parameters:
name: tests_linux
displayName: Linux
name: tests_android_macos
displayName: Android (macOS)
vmImage: $(VM_IMAGE_MAC)
target: tests-android
additionalArgs: --device=android-emulator-32_30 --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_android_x86_macos
- native_android_x64_macos
- native_android_arm_macos
- native_android_arm64_macos
preBuildSteps:
- pwsh: |
dotnet tool install Microsoft.DotNet.XHarness.CLI `
--global `
--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\""
displayName: Install the Android emulator
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the Android test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'output/testlogs/SkiaSharp.Android.Tests/**/TestResults.xml'
testRunTitle: 'Android Tests'
- task: PublishBuildArtifacts@1
displayName: Publish the test logs
condition: always()
inputs:
artifactName: testlogs_android
pathToPublish: 'output/testlogs'
- template: azure-templates-bootstrapper.yml # Tests|ios (macOS)
parameters:
name: tests_ios_macos
displayName: iOS (macOS)
vmImage: $(VM_IMAGE_MAC)
target: tests-ios
additionalArgs: --device=ios-simulator-64 --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_ios_macos
preBuildSteps:
- template: azure-templates-provisioning-profiles.yml
- pwsh: |
dotnet tool install Microsoft.DotNet.XHarness.CLI `
--global `
--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
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the iOS test results
condition: always()
inputs:
testResultsFormat: xUnit
testResultsFiles: 'output/testlogs/SkiaSharp.iOS.Tests/**/TestResults.xml'
testRunTitle: 'iOS Tests'
- task: PublishBuildArtifacts@1
displayName: Publish the test logs
condition: always()
inputs:
artifactName: testlogs_ios
pathToPublish: 'output/testlogs'
- template: azure-templates-bootstrapper.yml # Tests|netfx (Linux)
parameters:
name: tests_netfx_linux
displayName: Linux (.NET Framework)
vmImage: $(VM_IMAGE_LINUX)
packages: $(MANAGED_LINUX_PACKAGES)
target: tests
target: tests-netfx
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_linux_x64_linux
tools:
- dotnet-reportgenerator-globaltool
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the Mono test results
@ -544,6 +634,20 @@ stages:
testResultsFormat: xUnit
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
testRunTitle: 'Linux Mono Tests'
- template: azure-templates-bootstrapper.yml # Tests|netcore (Linux)
parameters:
name: tests_netcore_linux
displayName: Linux (.NET Core)
vmImage: $(VM_IMAGE_LINUX)
packages: $(MANAGED_LINUX_PACKAGES)
target: tests-netcore
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
shouldPublish: false
requiredArtifacts:
- native_linux_x64_linux
tools:
- dotnet-reportgenerator-globaltool
postBuildSteps:
- task: PublishTestResults@2
displayName: Publish the .NET Core test results
condition: always()
@ -554,7 +658,7 @@ stages:
- task: PublishBuildArtifacts@1
displayName: 'Publish the code coverage results'
inputs:
artifactName: coverage_linux
artifactName: coverage_netcore_linux
pathToPublish: 'output/coverage'
- template: azure-templates-bootstrapper.yml # Tests [WASM] (Linux)
parameters:
@ -585,26 +689,26 @@ stages:
pool:
vmImage: $(VM_IMAGE_LINUX)
dependsOn:
- tests_windows
- tests_macos
- tests_linux
- tests_netcore_windows
- tests_netcore_macos
- tests_netcore_linux
steps:
- checkout: self
- template: azure-templates-variables.yml
- task: DownloadBuildArtifacts@0
displayName: Download the coverage_windows artifact
displayName: Download the coverage_netcore_windows artifact
inputs:
artifactName: coverage_windows
artifactName: coverage_netcore_windows
downloadPath: output
- task: DownloadBuildArtifacts@0
displayName: Download the coverage_macos artifact
displayName: Download the coverage_netcore_macos artifact
inputs:
artifactName: coverage_macos
artifactName: coverage_netcore_macos
downloadPath: output
- task: DownloadBuildArtifacts@0
displayName: Download the coverage_linux artifact
displayName: Download the coverage_netcore_linux artifact
inputs:
artifactName: coverage_linux
artifactName: coverage_netcore_linux
downloadPath: output
- task: PublishCodeCoverageResults@1
displayName: 'Publish the code coverage results'
@ -639,21 +743,7 @@ stages:
requiredArtifacts:
- nuget
preBuildSteps:
- task: InstallAppleCertificate@2
inputs:
certSecureFile: 'SkiaSharp iOS Certificate.p12'
- task: InstallAppleCertificate@2
inputs:
certSecureFile: 'SkiaSharp Mac Certificate.p12'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp iOS Provisioning.mobileprovision'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp Mac Provisioning.provisionprofile'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp tvOS Provisioning.mobileprovision'
- template: azure-templates-provisioning-profiles.yml
- pwsh: |
New-Item '.\output\nugets\' -Type Directory -Force | Out-Null
Get-ChildItem '.\output\*.nupkg' | Move-Item -Destination '.\output\nugets\'

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

@ -33,6 +33,7 @@ jobs:
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# condition: ${{ parameters.condition }}
# postBuildSteps: ${{ parameters.postBuildSteps }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(eq(parameters.buildExternals, ''), not(startsWith(parameters.name, 'native_'))) }}:

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

@ -4,6 +4,7 @@ parameters:
vmImage: '' # the VM image
condition: succeeded() # whether or not to run this template
buildExternals: '' # the build number to download externals from
postBuildSteps: [] # any additional steps to run after the build
jobs:
- job: ${{ parameters.name }}
@ -33,4 +34,5 @@ jobs:
displayName: Publish the ${{ parameters.name }} artifacts
inputs:
artifactName: ${{ parameters.name }}
pathToPublish: 'output'
pathToPublish: 'output'
- ${{ parameters.postBuildSteps }}

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

@ -0,0 +1,16 @@
steps:
- task: InstallAppleCertificate@2
inputs:
certSecureFile: 'SkiaSharp iOS Certificate.p12'
- task: InstallAppleCertificate@2
inputs:
certSecureFile: 'SkiaSharp Mac Certificate.p12'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp iOS Provisioning.mobileprovision'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp Mac Provisioning.provisionprofile'
- task: InstallAppleProvisioningProfile@1
inputs:
provProfileSecureFile: 'SkiaSharp tvOS Provisioning.mobileprovision'

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

@ -1,4 +1,5 @@
using CoreVideo;
using System;
using CoreVideo;
using Xamarin.Forms;
using SKFormsView = SkiaSharp.Views.Forms.SKGLView;
@ -46,7 +47,7 @@ namespace SkiaSharp.Views.Forms
var nativeView = Control;
nativeView?.BeginInvokeOnMainThread(() =>
{
if (nativeView != null)
if (nativeView.Handle != IntPtr.Zero)
nativeView.NeedsDisplay = true;
});
return;
@ -59,6 +60,15 @@ namespace SkiaSharp.Views.Forms
var nativeView = Control;
var formsView = Element;
// stop the render loop if this was a one-shot, or the views are disposed
if (nativeView == null || formsView == null || nativeView.Handle == IntPtr.Zero || !formsView.HasRenderLoop)
{
displayLink.Stop();
displayLink.Dispose();
displayLink = null;
return CVReturn.Success;
}
// redraw the view
nativeView?.BeginInvokeOnMainThread(() =>
{
@ -66,14 +76,6 @@ namespace SkiaSharp.Views.Forms
nativeView.NeedsDisplay = true;
});
// stop the render loop if this was a one-shot, or the views are disposed
if (nativeView == null || formsView == null || !formsView.HasRenderLoop)
{
displayLink.Stop();
displayLink.Dispose();
displayLink = null;
}
return CVReturn.Success;
});
displayLink.Start();

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

@ -1,4 +1,5 @@
using CoreAnimation;
using System;
using CoreAnimation;
using Foundation;
using Xamarin.Forms;
@ -39,7 +40,7 @@ namespace SkiaSharp.Views.Forms
displayLink.Dispose();
displayLink = null;
}
base.Dispose(disposing);
}
@ -57,7 +58,11 @@ namespace SkiaSharp.Views.Forms
if (oneShot)
{
var nativeView = Control;
nativeView?.BeginInvokeOnMainThread(() => nativeView?.Display());
nativeView?.BeginInvokeOnMainThread(() =>
{
if (nativeView.Handle != IntPtr.Zero)
nativeView.Display();
});
return;
}
@ -67,16 +72,17 @@ namespace SkiaSharp.Views.Forms
var nativeView = Control;
var formsView = Element;
// redraw the view
nativeView?.Display();
// stop the render loop if this was a one-shot, or the views are disposed
if (nativeView == null || formsView == null || !formsView.HasRenderLoop)
if (nativeView == null || formsView == null || nativeView.Handle == IntPtr.Zero || !formsView.HasRenderLoop)
{
displayLink.Invalidate();
displayLink.Dispose();
displayLink = null;
return;
}
// redraw the view
nativeView.Display();
});
displayLink.AddToRunLoop(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
}

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

@ -1,5 +1,4 @@
using System;
using Android.Graphics;
using Android.Graphics;
using Windows.Graphics.Display;
using Windows.UI.Xaml;
@ -7,20 +6,20 @@ namespace SkiaSharp.Views.UWP
{
public partial class SKXamlCanvas : FrameworkElement
{
private Bitmap bitmap;
private SKImageInfo info;
private SurfaceFactory surfaceFactory;
public SKXamlCanvas()
{
surfaceFactory = new SurfaceFactory();
Initialize();
SetWillNotDraw(false);
}
partial void DoUnloaded() =>
FreeBitmap();
surfaceFactory.Dispose();
private SKSize GetCanvasSize() =>
info.Size;
surfaceFactory.Info.Size;
private void DoInvalidate()
{
@ -38,11 +37,11 @@ namespace SkiaSharp.Views.UWP
var display = DisplayInformation.GetForCurrentView();
var scale = display.LogicalDpi / 96.0f;
info = new SKImageInfo((int)(w * scale), (int)(h * scale), SKColorType.Rgba8888, SKAlphaType.Premul);
surfaceFactory.UpdateCanvasSize((int)(w * scale), (int)(h * scale));
}
else
{
info = new SKImageInfo(w, h, SKColorType.Rgba8888, SKAlphaType.Premul);
surfaceFactory.UpdateCanvasSize(w, h);
}
}
@ -53,52 +52,23 @@ namespace SkiaSharp.Views.UWP
if (designMode)
return;
if (info.Width == 0 || info.Height == 0 || Visibility != Visibility.Visible || !isVisible)
// bail out if the view is not actually visible
if (Visibility != Visibility.Visible || !isVisible)
{
FreeBitmap();
surfaceFactory.FreeBitmap();
return;
}
// create the bitmap data if we need it
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != info.Width || bitmap.Height != info.Height)
{
FreeBitmap();
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
}
// create a skia surface
var surface = surfaceFactory.CreateSurface(out var info);
if (surface == null)
return;
// create a surface
using (var surface = SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes))
{
// draw using SkiaSharp
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
// draw using SkiaSharp
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
surface.Canvas.Flush();
}
bitmap.UnlockPixels();
// draw bitmap to canvas
if (IgnorePixelScaling)
{
var src = new Rect(0, 0, info.Rect.Width, info.Rect.Height);
var dst = new RectF(0, 0, (float)Width, (float)Height);
canvas.DrawBitmap(bitmap, src, dst, null);
}
else
{
canvas.DrawBitmap(bitmap, 0, 0, null);
}
}
private void FreeBitmap()
{
if (bitmap != null)
{
// free and recycle the bitmap data
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
bitmap.Recycle();
bitmap.Dispose();
bitmap = null;
}
// draw the surface to the view
surfaceFactory.DrawSurface(surface, canvas);
}
}
}

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

@ -18,6 +18,7 @@
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\GLTextureView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureView.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureViewRenderer.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SurfaceFactory.cs" />
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.UWP\UWPExtensions.cs" />
<Compile Include="..\SkiaSharp.Views.Uno\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

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

@ -10,11 +10,9 @@ namespace SkiaSharp.Views.Android
{
public class SKCanvasView : View
{
private Bitmap bitmap;
private SKImageInfo info;
private bool ignorePixelScaling;
private bool designMode;
private SurfaceFactory surfaceFactory;
public SKCanvasView(Context context)
: base(context)
@ -43,15 +41,10 @@ namespace SkiaSharp.Views.Android
private void Initialize()
{
designMode = !Extensions.IsValidEnvironment;
if (designMode)
return;
// create the initial info
info = new SKImageInfo(0, 0, SKColorType.Rgba8888, SKAlphaType.Premul);
surfaceFactory = new SurfaceFactory();
}
public SKSize CanvasSize => info.Size;
public SKSize CanvasSize => surfaceFactory.Info.Size;
public bool IgnorePixelScaling
{
@ -71,37 +64,26 @@ namespace SkiaSharp.Views.Android
if (designMode)
return;
if (info.Width == 0 || info.Height == 0 || Visibility != ViewStates.Visible)
// bail out if the view is not actually visible
if (Visibility != ViewStates.Visible)
{
FreeBitmap();
surfaceFactory.FreeBitmap();
return;
}
// create the bitmap data if we need it
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != info.Width || bitmap.Height != info.Height)
{
FreeBitmap();
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
}
// create a skia surface
var surface = surfaceFactory.CreateSurface(out var info);
if (surface == null)
return;
// create a surface
using (var surface = SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes))
{
// draw using SkiaSharp
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
// draw using SkiaSharp
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
#pragma warning disable CS0618 // Type or member is obsolete
OnDraw(surface, info);
OnDraw(surface, info);
#pragma warning restore CS0618 // Type or member is obsolete
surface.Canvas.Flush();
}
bitmap.UnlockPixels();
// draw bitmap to canvas
if (IgnorePixelScaling)
canvas.DrawBitmap(bitmap, info.Rect.ToRect(), new RectF(0, 0, Width, Height), null);
else
canvas.DrawBitmap(bitmap, 0, 0, null);
// draw the surface to the view
surfaceFactory.DrawSurface(surface, canvas);
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
@ -112,24 +94,6 @@ namespace SkiaSharp.Views.Android
UpdateCanvasSize(w, h);
}
private void UpdateCanvasSize(int w, int h)
{
if (designMode)
return;
if (IgnorePixelScaling)
{
var scale = Resources.DisplayMetrics.Density;
info.Width = (int)(w / scale);
info.Height = (int)(h / scale);
}
else
{
info.Width = w;
info.Height = h;
}
}
public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
protected virtual void OnPaintSurface(SKPaintSurfaceEventArgs e)
@ -137,7 +101,7 @@ namespace SkiaSharp.Views.Android
PaintSurface?.Invoke(this, e);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use OnPaintSurface(SKPaintSurfaceEventArgs) instead.")]
protected virtual void OnDraw(SKSurface surface, SKImageInfo info)
{
@ -145,29 +109,27 @@ namespace SkiaSharp.Views.Android
protected override void OnDetachedFromWindow()
{
FreeBitmap();
surfaceFactory.Dispose();
base.OnDetachedFromWindow();
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
UpdateCanvasSize(Width, Height);
Invalidate();
}
protected override void Dispose(bool disposing)
{
if (disposing)
FreeBitmap();
surfaceFactory.Dispose();
base.Dispose(disposing);
}
private void FreeBitmap()
{
if (bitmap != null)
{
// free and recycle the bitmap data
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
bitmap.Recycle();
bitmap.Dispose();
bitmap = null;
}
}
private void UpdateCanvasSize(int w, int h) =>
surfaceFactory.UpdateCanvasSize(w, h, IgnorePixelScaling ? Resources.DisplayMetrics.Density : 1);
}
}

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

@ -5,34 +5,27 @@ namespace SkiaSharp.Views.Android
public class SKLockedSurface
{
private readonly Canvas canvas;
private readonly Bitmap bitmap;
private readonly SurfaceFactory surfaceFactory;
internal SKLockedSurface(Canvas canvas, Bitmap bitmap)
internal SKLockedSurface(Canvas canvas, SurfaceFactory surfaceFactory)
{
this.bitmap = bitmap;
this.canvas = canvas;
this.surfaceFactory = surfaceFactory;
// create a surface
ImageInfo = new SKImageInfo(bitmap.Width, bitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
Surface = SKSurface.Create(ImageInfo, bitmap.LockPixels(), ImageInfo.RowBytes);
Surface = surfaceFactory.CreateSurface(out var info);
ImageInfo = info;
}
public SKImageInfo ImageInfo { get; private set; }
public SKImageInfo ImageInfo { get; }
public SKSurface Surface { get; private set; }
public SKSurface Surface { get; }
public SKCanvas Canvas => Surface.Canvas;
internal Canvas Post()
{
// dispose our canvas
Surface.Canvas.Flush();
Surface.Dispose();
// unlock the bitmap data and write to canvas
bitmap.UnlockPixels();
canvas.DrawBitmap(bitmap, 0, 0, null);
surfaceFactory.DrawSurface(Surface, canvas);
return canvas;
}
}

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

@ -1,7 +1,5 @@
using System;
using Android.Content;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
using Android.Views;
@ -9,7 +7,7 @@ namespace SkiaSharp.Views.Android
{
public class SKSurfaceView : SurfaceView, ISurfaceHolderCallback
{
private Bitmap bitmap;
private SurfaceFactory surfaceFactory;
public SKSurfaceView(Context context)
: base(context)
@ -31,26 +29,28 @@ namespace SkiaSharp.Views.Android
private void Initialize()
{
surfaceFactory = new SurfaceFactory();
Holder.AddCallback(this);
}
public SKSize CanvasSize => bitmap == null ? SKSize.Empty : new SKSize(bitmap.Width, bitmap.Height);
public SKSize CanvasSize => surfaceFactory.Info.Size;
// ISurfaceHolderCallback
public virtual void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
public virtual void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
{
CreateBitmap(width, height);
surfaceFactory.UpdateCanvasSize(width, height);
}
public virtual void SurfaceCreated(ISurfaceHolder holder)
{
CreateBitmap(holder.SurfaceFrame.Width(), holder.SurfaceFrame.Height());
var surfaceFrame = Holder.SurfaceFrame.ToSKRect();
surfaceFactory.UpdateCanvasSize(surfaceFrame.Width, surfaceFrame.Height);
}
public virtual void SurfaceDestroyed(ISurfaceHolder holder)
{
FreeBitmap();
surfaceFactory.Dispose();
}
// lock / unlock the SKSurface
@ -58,7 +58,11 @@ namespace SkiaSharp.Views.Android
public SKLockedSurface LockSurface()
{
var canvas = Holder.LockCanvas();
return new SKLockedSurface(canvas, bitmap);
if (canvas == null)
return null;
surfaceFactory.UpdateCanvasSize(canvas.Width, canvas.Height);
return new SKLockedSurface(canvas, surfaceFactory);
}
public void UnlockSurfaceAndPost(SKLockedSurface surface)
@ -71,35 +75,9 @@ namespace SkiaSharp.Views.Android
protected override void Dispose(bool disposing)
{
if (disposing)
{
FreeBitmap();
}
surfaceFactory.Dispose();
base.Dispose(disposing);
}
private void CreateBitmap(int width, int height)
{
// create the bitmap data
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != width || bitmap.Height != height)
{
FreeBitmap();
bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
}
}
private void FreeBitmap()
{
if (bitmap != null)
{
// free and recycle the bitmap data
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
{
bitmap.Recycle();
}
bitmap.Dispose();
bitmap = null;
}
}
}
}

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

@ -0,0 +1,90 @@
using System;
using Android.Graphics;
#if HAS_UNO
namespace SkiaSharp.Views.UWP
#else
namespace SkiaSharp.Views.Android
#endif
{
internal class SurfaceFactory : IDisposable
{
private Bitmap bitmap;
public SKImageInfo Info { get; private set; }
public void UpdateCanvasSize(int w, int h, float density = 1f)
{
if (density != 1)
Info = CreateInfo((int)(w / density), (int)(h / density));
else
Info = CreateInfo(w, h);
// if there are no pixels, clean up
if (Info.Width == 0 || Info.Height == 0)
FreeBitmap();
}
public SKSurface CreateSurface(out SKImageInfo info)
{
// get context details
info = Info;
// if there are no pixels, clean up and return
if (info.Width == 0 || info.Height == 0)
{
Dispose();
return null;
}
// if the memory size has changed, then reset the underlying memory
if (bitmap?.Handle == IntPtr.Zero || bitmap?.Width != info.Width || bitmap?.Height != info.Height)
FreeBitmap();
// create the bitmap data if we need it
if (bitmap == null)
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
return SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes);
}
public void DrawSurface(SKSurface surface, Canvas canvas)
{
// clean up skia objects
surface.Canvas.Flush();
surface.Dispose();
// get the bitmap ready for drawing
bitmap.UnlockPixels();
// get the bounds
var src = new Rect(0, 0, Info.Width, Info.Height);
var dst = new RectF(0, 0, canvas.Width, canvas.Height);
// draw bitmap to the view canvas
canvas.DrawBitmap(bitmap, src, dst, null);
}
public void Dispose()
{
FreeBitmap();
Info = CreateInfo(0, 0);
}
public void FreeBitmap()
{
if (bitmap == null)
return;
// free and recycle the bitmap data
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
bitmap.Recycle();
bitmap.Dispose();
bitmap = null;
}
private SKImageInfo CreateInfo(int width, int height) =>
new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
}
}

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

@ -23,7 +23,6 @@ namespace SkiaSharp.Views.Mac
public SKCanvasView(CGRect frame)
: base(frame)
{
Initialize();
}

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

@ -0,0 +1,57 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31004.235
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Android.Tests", "SkiaSharp.Android.Tests\SkiaSharp.Android.Tests.csproj", "{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp.Android", "..\binding\HarfBuzzSharp.Android\HarfBuzzSharp.Android.csproj", "{7F5C82DF-976E-4973-AFDF-C2E178B19774}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Android", "..\binding\SkiaSharp.Android\SkiaSharp.Android.csproj", "{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.HarfBuzz", "..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Android", "..\source\SkiaSharp.Views\SkiaSharp.Views.Android\SkiaSharp.Views.Android.csproj", "{16922662-C540-4865-876D-94ED3211521B}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.Build.0 = Release|Any CPU
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.Deploy.0 = Release|Any CPU
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Release|Any CPU.Build.0 = Release|Any CPU
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Release|Any CPU.Build.0 = Release|Any CPU
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Release|Any CPU.Build.0 = Release|Any CPU
{16922662-C540-4865-876D-94ED3211521B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16922662-C540-4865-876D-94ED3211521B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16922662-C540-4865-876D-94ED3211521B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16922662-C540-4865-876D-94ED3211521B}.Release|Any CPU.Build.0 = Release|Any CPU
{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E393D9B5-76D6-469A-A011-3C3A4D8DF8A5}
EndGlobalSection
EndGlobal

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

@ -0,0 +1,31 @@
using System.Reflection;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Xunit.Runners.UI;
namespace SkiaSharp.Tests
{
[Activity(MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : RunnerActivity
{
protected override void OnCreate(Bundle bundle)
{
Xamarin.Essentials.Platform.Init(this, bundle);
AssetCopier.CopyAssets();
AddTestAssembly(Assembly.GetExecutingAssembly());
AddExecutionAssembly(Assembly.GetExecutingAssembly());
base.OnCreate(bundle);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}

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

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.mono.skiasharp.tests" android:installLocation="auto">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="@string/app_name" android:icon="@drawable/icon" android:theme="@style/MainTheme"></application>
</manifest>

Двоичные данные
tests/SkiaSharp.Android.Tests/Resources/drawable/icon.png Normal file

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

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

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

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Tests</string>
</resources>

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

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MainTheme" parent="MainTheme.Base">
</style>
<!-- Base theme applied no matter what API -->
<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
<item name="windowNoTitle">true</item>
<!--We will be using the toolbar so no need to show ActionBar-->
<item name="windowActionBar">false</item>
<!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette -->
<!-- colorPrimary is used for the default action bar background -->
<item name="colorPrimary">#2196F3</item>
<!-- colorPrimaryDark is used for the status bar -->
<item name="colorPrimaryDark">#1976D2</item>
<!-- colorAccent is used as the default value for colorControlActivated
which is used to tint widgets -->
<item name="colorAccent">#FF4081</item>
<!-- You can also set colorControlNormal, colorControlActivated
colorControlHighlight and colorSwitchThumbNormal. -->
<item name="windowActionModeOverlay">true</item>
<item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
</style>
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">#FF4081</item>
</style>
</resources>

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

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<RootNamespace>SkiaSharp.Tests</RootNamespace>
<AssemblyName>SkiaSharp.Tests</AssemblyName>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidApplication>True</AndroidApplication>
<AndroidUseIntermediateDesignerFile>true</AndroidUseIntermediateDesignerFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<SkipGenerateAssemblyVersionInfo>true</SkipGenerateAssemblyVersionInfo>
<SkipMDocGenerateDocs>true</SkipMDocGenerateDocs>
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
<SignAssembly>false</SignAssembly>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;USE_LIBRARY_LOADER;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<DefineConstants>USE_LIBRARY_LOADER;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidLinkMode>None</AndroidLinkMode>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.devices" Version="2.5.25" />
<PackageReference Include="xunit.skippablefact" Version="1.4.13" />
<PackageReference Include="Validation" Version="2.4.22" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.20602.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\binding\HarfBuzzSharp.Android\HarfBuzzSharp.Android.csproj">
<Project>{7f5c82df-976e-4973-afdf-c2e178b19774}</Project>
<Name>HarfBuzzSharp.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\binding\SkiaSharp.Android\SkiaSharp.Android.csproj">
<Project>{d7e86b8f-4e7d-4be9-a740-d0f5ac9392e5}</Project>
<Name>SkiaSharp.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj">
<Project>{4c5f53b5-9dfd-4cc6-8fae-98b4f40cb33a}</Project>
<Name>SkiaSharp.HarfBuzz</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Android\SkiaSharp.Views.Forms.Android.csproj">
<Project>{f962e49d-dc1f-4e93-9f6b-335e2746bcf1}</Project>
<Name>SkiaSharp.Views.Forms.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.Views\SkiaSharp.Views.Android\SkiaSharp.Views.Android.csproj">
<Project>{16922662-c540-4865-876d-94ed3211521b}</Project>
<Name>SkiaSharp.Views.Android</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="TestInstrumentation.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Tests\**\*.cs" Link="%(RecursiveDir)%(FileName)%(Extension)" Exclude="..\Tests\GlContexts\*\*;..\Tests\PlatformUtils\*\*" />
<Compile Include="..\..\binding\Binding.Shared\LibraryLoader.cs" Link="PlatformUtils\LibraryLoader.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\Content\**\*" Link="Content\%(RecursiveDir)%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" />
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\output\SkiaSharp\nuget\build\monoandroid1.0\SkiaSharp.targets" Condition="Exists('..\..\output\SkiaSharp\nuget\build\monoandroid1.0\SkiaSharp.targets')" />
<Import Project="..\..\output\HarfBuzzSharp\nuget\build\monoandroid1.0\HarfBuzzSharp.targets" Condition="Exists('..\..\output\HarfBuzzSharp\nuget\build\monoandroid1.0\HarfBuzzSharp.targets')" />
</Project>

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

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using Android.App;
using Android.OS;
using Android.Runtime;
using Microsoft.DotNet.XHarness.TestRunners.Common;
using Microsoft.DotNet.XHarness.TestRunners.Xunit;
using Xamarin.Essentials;
namespace SkiaSharp.Tests
{
[Instrumentation(Name = "com.mono.skiasharp.tests.TestInstrumentation")]
public class TestInstrumentation : Instrumentation
{
private string resultsFileName;
protected TestInstrumentation()
{
}
protected TestInstrumentation(IntPtr handle, JniHandleOwnership transfer)
: base(handle, transfer)
{
}
public override void OnCreate(Bundle arguments)
{
base.OnCreate(arguments);
AssetCopier.CopyAssets();
resultsFileName = arguments.GetString("results-file-name", "TestResults.xml");
Start();
}
public override async void OnStart()
{
base.OnStart();
var bundle = new Bundle();
var entryPoint = new TestsEntryPoint(resultsFileName);
entryPoint.TestsCompleted += (sender, results) =>
{
var message =
$"Tests run: {results.ExecutedTests} " +
$"Passed: {results.PassedTests} " +
$"Inconclusive: {results.InconclusiveTests} " +
$"Failed: {results.FailedTests} " +
$"Ignored: {results.SkippedTests}";
bundle.PutString("test-execution-summary", message);
bundle.PutLong("return-code", results.FailedTests == 0 ? 0 : 1);
};
await entryPoint.RunAsync();
if (File.Exists(entryPoint.TestsResultsFinalPath))
bundle.PutString("test-results-path", entryPoint.TestsResultsFinalPath);
if (bundle.GetLong("return-code", -1) == -1)
bundle.PutLong("return-code", 1);
Finish(Result.Ok, bundle);
}
private class TestsEntryPoint : AndroidApplicationEntryPoint
{
private readonly string resultsPath;
public TestsEntryPoint(string resultsFileName)
{
#pragma warning disable CS0618 // Type or member is obsolete
var root = DeviceInfo.Version >= new Version(11, 0)
? Android.OS.Environment.ExternalStorageDirectory.AbsolutePath
: Application.Context.GetExternalFilesDir(null)?.AbsolutePath ?? FileSystem.AppDataDirectory;
#pragma warning restore CS0618 // Type or member is obsolete
var docsDir = Path.Combine(root, "Documents");
if (!Directory.Exists(docsDir))
Directory.CreateDirectory(docsDir);
resultsPath = Path.Combine(docsDir, resultsFileName);
}
protected override bool LogExcludedTests => true;
public override TextWriter Logger => null;
public override string TestsResultsFinalPath => resultsPath;
protected override int? MaxParallelThreads => System.Environment.ProcessorCount;
protected override IDevice Device { get; } = new TestDevice();
protected override IEnumerable<TestAssemblyInfo> GetTestAssemblies()
{
yield return new TestAssemblyInfo(Assembly.GetExecutingAssembly(), Assembly.GetExecutingAssembly().Location);
}
protected override void TerminateWithSuccess()
{
}
protected override TestRunner GetTestRunner(LogWriter logWriter)
{
var testRunner = base.GetTestRunner(logWriter);
return testRunner;
}
}
private class TestDevice : IDevice
{
public string BundleIdentifier => AppInfo.PackageName;
public string UniqueIdentifier => Guid.NewGuid().ToString("N");
public string Name => DeviceInfo.Name;
public string Model => DeviceInfo.Model;
public string SystemName => DeviceInfo.Platform.ToString();
public string SystemVersion => DeviceInfo.VersionString;
public string Locale => CultureInfo.CurrentCulture.Name;
}
}
}

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

@ -0,0 +1,107 @@

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}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}"
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}"
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}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.iOS.Tests", "SkiaSharp.iOS.Tests\SkiaSharp.iOS.Tests.csproj", "{B73EB308-70BE-49FD-91A7-1D1495663D6D}"
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
Release|iPhone = Release|iPhone
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}.Release|iPhone.ActiveCfg = Release|Any CPU
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhone.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}.Release|iPhone.ActiveCfg = Release|Any CPU
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhone.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}.Release|iPhone.ActiveCfg = Release|Any CPU
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhone.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}.Release|iPhone.ActiveCfg = Release|Any CPU
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhone.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}.Release|iPhone.ActiveCfg = Release|Any CPU
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhone.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}.Release|iPhone.ActiveCfg = Release|iPhone
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhone.Build.0 = Release|iPhone
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {35C58E22-5C43-4580-AE38-8F774B6C98EC}
EndGlobalSection
EndGlobal

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

@ -0,0 +1,21 @@
using System.Reflection;
using Foundation;
using UIKit;
namespace SkiaSharp.Tests
{
[Register(nameof(AppDelegate))]
public partial class AppDelegate : Xunit.Runner.RunnerAppDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// We need this to ensure the execution assembly is part of the app bundle
AddExecutionAssembly(Assembly.GetExecutingAssembly());
// tests can be inside the main assembly
AddTestAssembly(Assembly.GetExecutingAssembly());
return base.FinishedLaunching(app, options);
}
}
}

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

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>

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

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>MinimumOSVersion</key>
<string>10.0</string>
<key>CFBundleDisplayName</key>
<string>Tests</string>
<key>CFBundleIdentifier</key>
<string>com.mono.skiasharp.tests</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>CFBundleName</key>
<string>Tests</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
</dict>
</plist>

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

@ -0,0 +1,18 @@
using System;
using UIKit;
namespace SkiaSharp.Tests
{
public class Application
{
static void Main(string[] args)
{
AssetCopier.CopyAssets();
if (args?.Length > 0 || Environment.GetEnvironmentVariable("NUNIT_AUTOEXIT")?.Length > 0) // usually means this is from xharness
UIApplication.Main(args, null, nameof(TestApplicationDelegate));
else
UIApplication.Main(args, null, nameof(AppDelegate));
}
}
}

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

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="X5k-f2-b5h">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="gAE-YM-kbH">
<objects>
<viewController id="X5k-f2-b5h" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Y8P-hJ-Z43"/>
<viewControllerLayoutGuide type="bottom" id="9ZL-r4-8FZ"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="yd7-JS-zBw">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="23" firstAttribute="centerY" secondItem="yd7-JS-zBw" secondAttribute="centerY" priority="1" id="39"/>
<constraint firstItem="23" firstAttribute="centerX" secondItem="yd7-JS-zBw" secondAttribute="centerX" priority="1" id="41"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XAI-xm-WK6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="349" y="339"/>
</scene>
</scenes>
<resources>
</resources>
</document>

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

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B73EB308-70BE-49FD-91A7-1D1495663D6D}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<AssemblyName>SkiaSharp.Tests</AssemblyName>
<RootNamespace>SkiaSharp.Tests</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<SkipGenerateAssemblyVersionInfo>true</SkipGenerateAssemblyVersionInfo>
<SkipMDocGenerateDocs>true</SkipMDocGenerateDocs>
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG;USE_LIBRARY_LOADER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>x86_64</MtouchArch>
<MtouchLink>None</MtouchLink>
<MtouchDebug>true</MtouchDebug>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<DefineConstants>USE_LIBRARY_LOADER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
<MtouchArch>x86_64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG;USE_LIBRARY_LOADER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARM64</MtouchArch>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<DefineConstants>USE_LIBRARY_LOADER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
<MtouchArch>ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.devices" Version="2.5.25" />
<PackageReference Include="xunit.skippablefact" Version="1.4.13" />
<PackageReference Include="Validation" Version="2.4.22" />
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.20602.1" />
<PackageReference Include="System.Memory" Version="4.5.3" ExcludeAssets="all" />
<PackageReference Include="System.Buffers" Version="4.5.1" ExcludeAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="TestApplicationDelegate.cs" />
<None Include="Entitlements.plist" />
<None Include="Info.plist" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Tests\**\*.cs" Link="%(RecursiveDir)%(FileName)%(Extension)" Exclude="..\Tests\GlContexts\*\*;..\Tests\PlatformUtils\*\*" />
<Compile Include="..\..\binding\Binding.Shared\LibraryLoader.cs" Link="PlatformUtils\LibraryLoader.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\Content\**\*" Link="Content\%(RecursiveDir)%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\binding\HarfBuzzSharp.iOS\HarfBuzzSharp.iOS.csproj">
<Project>{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}</Project>
<Name>HarfBuzzSharp.iOS</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj">
<Project>{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}</Project>
<Name>SkiaSharp.HarfBuzz</Name>
</ProjectReference>
<ProjectReference Include="..\..\binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj">
<Project>{A4146A87-DB60-4A17-A179-0E2E4255A08E}</Project>
<Name>SkiaSharp.iOS</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.iOS\SkiaSharp.Views.Forms.iOS.csproj">
<Project>{0254162B-6B4A-459E-BD96-3A42A104C144}</Project>
<Name>SkiaSharp.Views.Forms.iOS</Name>
</ProjectReference>
<ProjectReference Include="..\..\source\SkiaSharp.Views\SkiaSharp.Views.iOS\SkiaSharp.Views.iOS.csproj">
<Project>{549F8E22-A756-4E99-A84A-C4E74832DA95}</Project>
<Name>SkiaSharp.Views.iOS</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\..\output\SkiaSharp\nuget\build\xamarinios1.0\SkiaSharp.targets" Condition="Exists('..\..\output\SkiaSharp\nuget\build\xamarinios1.0\SkiaSharp.targets')" />
<Import Project="..\..\output\HarfBuzzSharp\nuget\build\xamarinios1.0\HarfBuzzSharp.targets" Condition="Exists('..\..\output\HarfBuzzSharp\nuget\build\xamarinios1.0\HarfBuzzSharp.targets')" />
</Project>

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

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Foundation;
using Microsoft.DotNet.XHarness.TestRunners.Common;
using Microsoft.DotNet.XHarness.TestRunners.Xunit;
using UIKit;
using Xamarin.Essentials;
namespace SkiaSharp.Tests
{
[Register(nameof(TestApplicationDelegate))]
public class TestApplicationDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds)
{
RootViewController = new ViewController()
};
Window.MakeKeyAndVisible();
return true;
}
class ViewController : UIViewController
{
public override async void ViewDidLoad()
{
base.ViewDidLoad();
var entryPoint = new TestsEntryPoint();
await entryPoint.RunAsync();
}
}
class TestsEntryPoint : iOSApplicationEntryPoint
{
protected override bool LogExcludedTests => true;
protected override int? MaxParallelThreads => Environment.ProcessorCount;
protected override IDevice Device { get; } = new TestDevice();
protected override IEnumerable<TestAssemblyInfo> GetTestAssemblies()
{
yield return new TestAssemblyInfo(Assembly.GetExecutingAssembly(), Assembly.GetExecutingAssembly().Location);
}
protected override void TerminateWithSuccess()
{
Console.WriteLine("Exiting test run with success");
var s = new ObjCRuntime.Selector("terminateWithSuccess");
UIApplication.SharedApplication.PerformSelector(s, UIApplication.SharedApplication, 0);
}
protected override TestRunner GetTestRunner(LogWriter logWriter)
{
var testRunner = base.GetTestRunner(logWriter);
return testRunner;
}
}
class TestDevice : IDevice
{
public string BundleIdentifier => AppInfo.PackageName;
public string UniqueIdentifier => Guid.NewGuid().ToString("N");
public string Name => DeviceInfo.Name;
public string Model => DeviceInfo.Model;
public string SystemName => DeviceInfo.Platform.ToString();
public string SystemVersion => DeviceInfo.VersionString;
public string Locale => CultureInfo.CurrentCulture.Name;
}
}
}

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

@ -19,19 +19,24 @@ namespace SkiaSharp.Tests
protected static readonly string[] UnicodeFontFamilies;
protected static readonly string DefaultFontFamily;
protected static readonly string PathToAssembly;
protected static readonly string PathToFonts;
protected static readonly string PathToImages;
public static readonly string PathToAssembly;
public static readonly string PathToFonts;
public static readonly string PathToImages;
static BaseTest()
{
// the the base paths
#if __ANDROID__ || __IOS__
PathToAssembly = Xamarin.Essentials.FileSystem.CacheDirectory;
#else
PathToAssembly = Directory.GetCurrentDirectory();
#endif
PathToFonts = Path.Combine(PathToAssembly, "fonts");
PathToImages = Path.Combine(PathToAssembly, "images");
// some platforms run the tests from a temporary location, so copy the native files
#if !NET_STANDARD
#if !NET_STANDARD && !__ANDROID__ && !__IOS__
var skiaRoot = Path.GetDirectoryName(typeof(SkiaSharp.SKImageInfo).Assembly.Location);
var harfRoot = Path.GetDirectoryName(typeof(HarfBuzzSharp.Buffer).Assembly.Location);
@ -56,11 +61,19 @@ namespace SkiaSharp.Tests
IsRuntimeMono = Type.GetType("Mono.Runtime") != null;
// set the test fields
#if __ANDROID__
DefaultFontFamily = "sans-serif";
UnicodeFontFamilies = new[] { "Noto Color Emoji" };
#elif __IOS__
DefaultFontFamily = "Arial";
UnicodeFontFamilies = new[] { "Apple Color Emoji" };
#else
DefaultFontFamily = IsLinux ? "DejaVu Sans" : "Arial";
UnicodeFontFamilies =
IsLinux ? new[] { "Symbola" } :
IsMac ? new[] { "Apple Color Emoji" } :
new[] { "Segoe UI Emoji", "Segoe UI Symbol" };
#endif
}
public static void CollectGarbage()

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

@ -20,11 +20,6 @@ namespace SkiaSharp.Tests
public GarbageCleanupFixture()
{
var aliveObjects = HandleDictionary.instances.Values
.Select(o => o.Target)
.Where(o => IsExpectedToBeDead(o, null))
.ToList();
Assert.Empty(aliveObjects);
}
public void Dispose()
@ -34,7 +29,7 @@ namespace SkiaSharp.Tests
var staticObjects = HandleDictionary.instances.Values
.Select(o => o.Target)
.Where(o => !IsExpectedToBeDead(o, null))
.Where(o => !IsExpectedToBeDead(o))
.Cast<SKObject>()
.ToList();
var staticChildren = staticObjects
@ -44,7 +39,7 @@ namespace SkiaSharp.Tests
// make sure nothing is alive
var aliveObjects = HandleDictionary.instances.Values
.Select(o => o.Target)
.Where(o => IsExpectedToBeDead(o, staticChildren))
.Where(o => IsExpectedToBeDead(o))
.Cast<SKObject>()
.ToList();
foreach (var o in staticChildren)
@ -65,7 +60,7 @@ namespace SkiaSharp.Tests
#endif
}
private bool IsExpectedToBeDead(object instance, IEnumerable<SKObject> exceptions)
private bool IsExpectedToBeDead(object instance)
{
if (instance == null)
return false;

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

@ -0,0 +1,47 @@
using System.IO;
namespace SkiaSharp.Tests
{
internal static class AssetCopier
{
public static void CopyAssets()
{
var fontsRoot = BaseTest.PathToFonts;
var imagesRoot = BaseTest.PathToImages;
var assembly = typeof(AssetCopier).Assembly;
var prefix = assembly.GetName().Name + ".Content.";
var fontsPrefix = "fonts.";
var imagesPrefix = "images.";
var names = assembly.GetManifestResourceNames();
foreach (var name in names)
{
if (!name.StartsWith(prefix))
continue;
var filename = name.Substring(prefix.Length);
var root = "";
if (filename.StartsWith(fontsPrefix))
{
filename = filename.Substring(fontsPrefix.Length);
root = fontsRoot;
}
else if (filename.StartsWith(imagesPrefix))
{
filename = filename.Substring(imagesPrefix.Length);
root = imagesRoot;
}
if (!Directory.Exists(root))
Directory.CreateDirectory(root);
using var stream = assembly.GetManifestResourceStream(name);
using var dest = File.Create(Path.Combine(root, filename));
stream.CopyTo(dest);
}
}
}
}

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

@ -501,7 +501,11 @@ namespace SkiaSharp.Tests
mask.FreeImage();
}
#if __ANDROID__ || __IOS__
[SkippableTheory(Skip = "Mobile devices sometimes run out of memory.")]
#else
[SkippableTheory]
#endif
[InlineData(100, 1000)]
public static void ImageScalingMultipleThreadsTest(int numThreads, int numIterationsPerThread)
{

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

@ -180,12 +180,19 @@ namespace SkiaSharp.Tests
[SkippableFact]
public void PremultipliedColorsHaveCorrectBitShift()
{
var isARGB =
#if __ANDROID__
false;
#else
IsWindows || IsLinux;
#endif
var color = (SKColor)0x12345678;
Assert.Equal(new SKColor(0x34, 0x56, 0x78, 0x12), color);
SKPMColor pmcolor;
if (IsWindows || IsLinux) {
if (isARGB) {
pmcolor = (SKPMColor)0x12345678;
} else {
pmcolor = (SKPMColor)0x12785634;
@ -203,7 +210,7 @@ namespace SkiaSharp.Tests
Assert.Equal(0x78, color.Blue);
Assert.Equal(0x78, pmcolor.Blue);
if (IsWindows || IsLinux) {
if (isARGB) {
// ARGB
Assert.Equal(24, SKImageInfo.PlatformColorAlphaShift);
Assert.Equal(16, SKImageInfo.PlatformColorRedShift);

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

@ -13,16 +13,19 @@ namespace SkiaSharp.Tests
var fonts = SKFontManager.Default;
var emoji = "🚀";
var emojiChar = StringUtilities.GetUnicodeCharacterCode(emoji, SKTextEncoding.Utf32);
using (var typeface = fonts.MatchCharacter(emojiChar))
using var typeface = fonts.MatchCharacter(emojiChar);
Assert.NotNull(typeface);
var familyName = typeface.FamilyName;
if (typeface.FamilyName.EndsWith("##fallback"))
{
Assert.NotNull(typeface);
if (IsLinux)
Assert.Equal("Symbola", typeface.FamilyName);
else if (IsMac)
Assert.Equal("Apple Color Emoji", typeface.FamilyName);
else if (IsWindows)
Assert.Contains(typeface.FamilyName, new[] { "Segoe UI Emoji", "Segoe UI Symbol" });
using var stream = typeface.OpenStream();
using var temp = SKTypeface.FromStream(stream);
familyName = temp.FamilyName;
}
Assert.Contains(familyName, UnicodeFontFamilies);
}
[SkippableFact]

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

@ -59,7 +59,7 @@ namespace SkiaSharp.Tests
}
[SkippableFact]
public void ToRasterImageFalseReturnsNonLazy()
public void ToRasterImageTrueFalseReturnsNonLazy()
{
using var data = SKData.Create(Path.Combine(PathToImages, "baboon.jpg"));
using var image = SKImage.FromEncodedData(data);
@ -74,6 +74,22 @@ namespace SkiaSharp.Tests
Assert.Equal(nonLazy, nonLazy.ToRasterImage());
}
[SkippableFact]
public void ToRasterImageTrueTrueReturnsNonLazy()
{
using var data = SKData.Create(Path.Combine(PathToImages, "baboon.jpg"));
using var image = SKImage.FromEncodedData(data);
Assert.True(image.IsLazyGenerated);
Assert.Null(image.PeekPixels());
using var nonLazy = image.ToRasterImage(true);
Assert.NotEqual(image, nonLazy);
Assert.False(nonLazy.IsLazyGenerated);
Assert.NotNull(nonLazy.PeekPixels());
Assert.Equal(nonLazy, nonLazy.ToRasterImage(true));
}
[SkippableFact]
public void ImmutableBitmapsAreNotCopied()
{

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

@ -321,6 +321,7 @@ namespace SkiaSharp.Tests
}
}
#if !__ANDROID__ && !__IOS__
[SkippableFact]
public async Task DelayedConstructionDoesNotCreateInvalidState()
{
@ -437,6 +438,7 @@ namespace SkiaSharp.Tests
Assert.True(SKObject.GetInstance<DelayedDestructionObject>(handle, out var final));
Assert.Same(objSlow, final);
}
#endif
private class DelayedConstructionObject : SKObject
{

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

@ -145,6 +145,7 @@ namespace SkiaSharp.Tests
{
try
{
#if !__ANDROID__ && !__IOS__
if (IsLinux)
{
return new GlxContext();
@ -158,6 +159,7 @@ namespace SkiaSharp.Tests
return new WglContext();
}
else
#endif
{
throw new PlatformNotSupportedException();
}