Browse Source

First pass at SkiaSharp rendering back end. Lot's left to do still - see Skia/Perspex.Skia/readme.md.

pull/506/head
Jason Jarvis 11 years ago
parent
commit
3bb58a2031
  1. 35
      Perspex.sln
  2. 18
      samples/TestApplicationShared/App.cs
  3. 2
      src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj
  4. 2
      src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj.bak
  5. 3
      src/Skia/Perspex.Skia.Android/AndroidRenderTarget.cs
  6. 2
      src/Skia/Perspex.Skia.Android/MethodTable.cs
  7. 13
      src/Skia/Perspex.Skia.Android/Perspex.Skia.Android.csproj
  8. 4
      src/Skia/Perspex.Skia.Android/packages.config
  9. 14
      src/Skia/Perspex.Skia.Desktop.TestApp/App.config
  10. 209
      src/Skia/Perspex.Skia.Desktop.TestApp/Perspex.Skia.Desktop.TestApp.csproj
  11. 91
      src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs
  12. 36
      src/Skia/Perspex.Skia.Desktop.TestApp/Properties/AssemblyInfo.cs
  13. 11
      src/Skia/Perspex.Skia.Desktop.TestApp/packages.config
  14. 3
      src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs
  15. 38
      src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj
  16. 4
      src/Skia/Perspex.Skia.Desktop/packages.config
  17. 2
      src/Skia/Perspex.Skia.iOS.TestApp/Info.plist
  18. 7
      src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj
  19. 31
      src/Skia/Perspex.Skia.iOS/SkiaView.cs
  20. 6
      src/Skia/Perspex.Skia.iOS/iOSMethodTable.cs
  21. 4
      src/Skia/Perspex.Skia.iOS/packages.config
  22. 69
      src/Skia/Perspex.Skia/BitmapImpl.cs
  23. 289
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  24. 339
      src/Skia/Perspex.Skia/FormattedTextImpl.cs
  25. 4
      src/Skia/Perspex.Skia/MethodTable.cs
  26. 2
      src/Skia/Perspex.Skia/NativeBrush.cs
  27. 14
      src/Skia/Perspex.Skia/NativeDrawingContextSettings.cs
  28. 6
      src/Skia/Perspex.Skia/NativeFormattedText.cs
  29. 11
      src/Skia/Perspex.Skia/Perspex.Skia.projitems
  30. 6
      src/Skia/Perspex.Skia/PerspexHandleHolder.cs
  31. 25
      src/Skia/Perspex.Skia/PlatformRenderInterface.cs
  32. 165
      src/Skia/Perspex.Skia/RenderTarget.cs
  33. 7
      src/Skia/Perspex.Skia/SkRect.cs
  34. 17
      src/Skia/Perspex.Skia/SkiaPlatform.cs
  35. 6
      src/Skia/Perspex.Skia/SkiaPoint.cs
  36. 57
      src/Skia/Perspex.Skia/SkiaSharpExtensions.cs
  37. 178
      src/Skia/Perspex.Skia/StreamGeometryImpl.cs
  38. 52
      src/Skia/Perspex.Skia/TypefaceCache.cs
  39. 42
      src/Skia/Perspex.Skia/readme.md
  40. 67
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  41. 68
      src/iOS/Perspex.iOSTestApplication/Info.plist

35
Perspex.sln

@ -145,11 +145,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Benchmarks", "tests
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Logging.Serilog", "src\Perspex.Logging.Serilog\Perspex.Logging.Serilog.csproj", "{B61B66A3-B82D-4875-8001-89D3394FE0C9}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.DesignerSupport", "src\Perspex.DesignerSupport\Perspex.DesignerSupport.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Skia.Desktop.TestApp", "src\Skia\Perspex.Skia.Desktop.TestApp\Perspex.Skia.Desktop.TestApp.csproj", "{383168DF-5A4E-45E5-A239-FB54D40D4F5C}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
samples\TestApplicationShared\TestApplicationShared.projitems*{383168df-5a4e-45e5-a239-fb54d40d4f5c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
@ -1015,10 +1017,10 @@ Global
{925DD807-B651-475F-9F7C-CBEB974CE43D}.AppStore|iPhone.Build.0 = Release|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhone.Build.0 = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.ActiveCfg = Debug|x86
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.Build.0 = Debug|x86
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhone.ActiveCfg = Debug|x86
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhone.Build.0 = Debug|x86
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1349,6 +1351,30 @@ Global
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.Build.0 = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|Any CPU.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|iPhone.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|Any CPU.ActiveCfg = Debug|x86
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|Any CPU.Build.0 = Debug|x86
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|iPhone.Build.0 = Debug|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|Any CPU.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|iPhone.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|iPhone.Build.0 = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{383168DF-5A4E-45E5-A239-FB54D40D4F5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1394,5 +1420,6 @@ Global
{61BEC86C-F307-4295-B5B8-9428610D7D55} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{383168DF-5A4E-45E5-A239-FB54D40D4F5C} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
EndGlobalSection
EndGlobal

18
samples/TestApplicationShared/App.cs

@ -16,8 +16,14 @@ namespace TestApplication
public App()
{
RegisterServices();
InitializeSubsystems((int)Environment.OSVersion.Platform);
Styles.Add(new DefaultTheme());
#if !__IOS__ // IOS Startup flow is a bit different and cannot use this
RegisterPlatformCallback(PlatformInitialization);
#endif
InitializeSubsystems((int)Environment.OSVersion.Platform);
Styles.Add(new DefaultTheme());
var loader = new PerspexXamlLoader();
var baseLight = (IStyle)loader.Load(
@ -32,5 +38,11 @@ namespace TestApplication
x => x.Children),
};
}
}
protected virtual void PlatformInitialization()
{
// default behavior
InitializeSubsystems((int)Environment.OSVersion.Platform);
}
}
}

2
src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj

@ -16,7 +16,7 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

2
src/Skia/Perspex.Skia.Android.TestApp/Perspex.Skia.Android.TestApp.csproj.bak

@ -16,7 +16,7 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

3
src/Skia/Perspex.Skia.Android/AndroidRenderTarget.cs

@ -14,6 +14,8 @@ using Perspex.Platform;
namespace Perspex.Skia
{
/* This lives in shared project now, but we may need an android specific implementation anyways
*
class RenderTarget : IRenderTarget
{
private IntPtr _currentRenderTarget = IntPtr.Zero;
@ -61,4 +63,5 @@ namespace Perspex.Skia
new DrawingContextImpl(MethodTable.Instance.RenderTargetCreateRenderingContext(_currentRenderTarget)));
}
}
*/
}

2
src/Skia/Perspex.Skia.Android/MethodTable.cs

@ -14,6 +14,7 @@ using Android.Widget;
namespace Perspex.Skia
{
/* No longer needed with SkiaSharp
class MethodTableImpl : MethodTable
{
[DllImport(@"perspesk")]
@ -26,4 +27,5 @@ namespace Perspex.Skia
PerspexJniInit(JNIEnv.Handle);
}
}
*/
}

13
src/Skia/Perspex.Skia.Android/Perspex.Skia.Android.csproj

@ -15,14 +15,14 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -31,7 +31,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -39,6 +39,10 @@
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\MonoAndroid\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -95,6 +99,9 @@
<Link>native\x86\libperspesk.so</Link>
</EmbeddedNativeLibrary>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />

4
src/Skia/Perspex.Skia.Android/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="monoandroid60" />
</packages>

14
src/Skia/Perspex.Skia.Desktop.TestApp/App.config

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

209
src/Skia/Perspex.Skia.Desktop.TestApp/Perspex.Skia.Desktop.TestApp.csproj

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{383168DF-5A4E-45E5-A239-FB54D40D4F5C}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Perspex.Skia.Desktop.TestApp</RootNamespace>
<AssemblyName>Perspex.Skia.Desktop.TestApp</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Serilog.FullNetFx, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\net45\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Reactive.Windows.Threading, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Rx-XAML.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Gtk\Perspex.Cairo\Perspex.Cairo.csproj">
<Project>{FB05AC90-89BA-4F2F-A924-F37875FB547C}</Project>
<Name>Perspex.Cairo</Name>
</ProjectReference>
<ProjectReference Include="..\..\Gtk\Perspex.Gtk\Perspex.Gtk.csproj">
<Project>{54F237D5-A70A-4752-9656-0C70B1A7B047}</Project>
<Name>Perspex.Gtk</Name>
</ProjectReference>
<ProjectReference Include="..\..\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj">
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project>
<Name>Perspex.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\Markup\Perspex.Markup\Perspex.Markup.csproj">
<Project>{6417E941-21BC-467B-A771-0DE389353CE6}</Project>
<Name>Perspex.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj">
<Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project>
<Name>Perspex.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.DesignerSupport\Perspex.DesignerSupport.csproj">
<Project>{799A7BB5-3C2C-48B6-85A7-406A12C420DA}</Project>
<Name>Perspex.Application</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Base\Perspex.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Perspex.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Controls\Perspex.Controls.csproj">
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
<Name>Perspex.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Diagnostics\Perspex.Diagnostics.csproj">
<Project>{7062AE20-5DCC-4442-9645-8195BDECE63E}</Project>
<Name>Perspex.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj">
<Project>{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}</Project>
<Name>Perspex.HtmlRenderer</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Input\Perspex.Input.csproj">
<Project>{62024B2D-53EB-4638-B26B-85EEAA54866E}</Project>
<Name>Perspex.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Interactivity\Perspex.Interactivity.csproj">
<Project>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</Project>
<Name>Perspex.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Layout\Perspex.Layout.csproj">
<Project>{42472427-4774-4C81-8AFF-9F27B8E31721}</Project>
<Name>Perspex.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.ReactiveUI\Perspex.ReactiveUI.csproj">
<Project>{6417B24E-49C2-4985-8DB2-3AB9D898EC91}</Project>
<Name>Perspex.ReactiveUI</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project>
<Name>Perspex.SceneGraph</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Styling\Perspex.Styling.csproj">
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
<Name>Perspex.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Themes.Default\Perspex.Themes.Default.csproj">
<Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project>
<Name>Perspex.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\Windows\Perspex.Direct2D1\Perspex.Direct2D1.csproj">
<Project>{3E908F67-5543-4879-A1DC-08EACE79B3CD}</Project>
<Name>Perspex.Direct2D1</Name>
</ProjectReference>
<ProjectReference Include="..\..\Windows\Perspex.Win32\Perspex.Win32.csproj">
<Project>{811A76CF-1CF6-440F-963B-BBE31BD72A82}</Project>
<Name>Perspex.Win32</Name>
</ProjectReference>
<ProjectReference Include="..\Perspex.Skia.Desktop\Perspex.Skia.Desktop.csproj">
<Project>{925dd807-b651-475f-9f7c-cbeb974ce43d}</Project>
<Name>Perspex.Skia.Desktop</Name>
</ProjectReference>
</ItemGroup>
<Import Project="..\..\..\samples\TestApplicationShared\TestApplicationShared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets" Condition="Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

91
src/Skia/Perspex.Skia.Desktop.TestApp/Program.cs

@ -0,0 +1,91 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.IO;
using System.Reactive.Linq;
using Perspex;
using Perspex.Animation;
using Perspex.Collections;
using Perspex.Controls;
using Perspex.Controls.Html;
using Perspex.Controls.Primitives;
using Perspex.Controls.Shapes;
using Perspex.Controls.Templates;
using Perspex.Diagnostics;
using Perspex.Layout;
using Perspex.Media;
using Perspex.Media.Imaging;
using TestApplication;
using Perspex.Rendering;
#if PERSPEX_GTK
using Perspex.Gtk;
#endif
using ReactiveUI;
namespace Perspex.Skia.Desktop.TestApp
{
internal class Program
{
private static void Main(string[] args)
{
// The version of ReactiveUI currently included is for WPF and so expects a WPF
// dispatcher. This makes sure it's initialized.
System.Windows.Threading.Dispatcher foo = System.Windows.Threading.Dispatcher.CurrentDispatcher;
new SkiaApp();
//RendererMixin.DrawFpsCounter = true;
MainWindow.RootNamespace = "Perspex.Skia.Desktop.TestApp";
var wnd = MainWindow.Create();
// let's start small :)
//var wnd = SimpleWindow.Create();
DevTools.Attach(wnd);
Application.Current.Run(wnd);
}
}
internal class SkiaApp : App
{
protected override void PlatformInitialization()
{
SkiaPlatform.Initialize();
InitializeSubsystem("Perspex.Win32");
}
}
internal class SimpleWindow
{
public static Window Create()
{
Border container;
Window window = new Window
{
Title = "Perspex Test Application",
Content = (container = new Border
{
Background = Brushes.Green,
BorderBrush = Brushes.Yellow,
BorderThickness = 12,
Padding = new Thickness(5),
Child = new Rectangle
{
Fill = Brushes.Blue,
Width = 200,
Height = 200,
Margin = new Thickness(50)
}
})
};
window.Show();
return window;
}
}
}

36
src/Skia/Perspex.Skia.Desktop.TestApp/Properties/AssemblyInfo.cs

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Perspex.Skia.Desktop.TestApp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Perspex.Skia.Desktop.TestApp")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("383168df-5a4e-45e5-a239-fb54d40d4f5c")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

11
src/Skia/Perspex.Skia.Desktop.TestApp/packages.config

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Rx-Core" version="2.2.5" targetFramework="net46" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net46" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net46" />
<package id="Rx-Main" version="2.2.5" targetFramework="net46" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net46" />
<package id="Rx-XAML" version="2.2.5" targetFramework="net46" />
<package id="Serilog" version="1.5.14" targetFramework="net46" />
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="net46" />
</packages>

3
src/Skia/Perspex.Skia.Desktop/MethodTableImpl.cs

@ -9,6 +9,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
/* OBSOLETE
//Library loaders were taken from https://github.com/kekekeks/evhttp-sharp/tree/master/EvHttpSharp/Interop (MIT licensed)
@ -233,3 +234,5 @@ namespace Perspex.Skia
}
}
}
*/

38
src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj

@ -12,6 +12,8 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -22,6 +24,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -32,7 +35,31 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;WIN32</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;WIN32</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\net45\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -45,6 +72,9 @@
<ItemGroup>
<Compile Include="MethodTableImpl.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\..\Windows\Perspex.Win32\Interop\UnmanagedMethods.cs">
<Link>UnmanagedMethods.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj">
@ -98,10 +128,18 @@
<None Include="..\native\Linux\x86_64\libperspesk.so">
<Link>native\Linux\x86_64\libperspesk.so</Link>
</None>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets" Condition="Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SkiaSharp.1.49.2.0-beta\build\net45\SkiaSharp.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

4
src/Skia/Perspex.Skia.Desktop/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="net45" />
</packages>

2
src/Skia/Perspex.Skia.iOS.TestApp/Info.plist

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>6.0</string>
<string>8.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>

7
src/Skia/Perspex.Skia.iOS/Perspex.Skia.iOS.csproj

@ -46,6 +46,10 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Reference Include="SkiaSharp, Version=1.49.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SkiaSharp.1.49.2.0-beta\lib\XamariniOS\SkiaSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
@ -89,6 +93,9 @@
<Name>Perspex.Themes.Default</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />

31
src/Skia/Perspex.Skia.iOS/SkiaView.cs

@ -15,30 +15,33 @@ using UIKit;
namespace Perspex.Skia.iOS
{
public abstract class SkiaView : GLKView
{
[DllImport("__Internal")]
static extern IntPtr GetPerspexEAGLContext();
public abstract class SkiaView : UIView //GLKView
{
//[DllImport("__Internal")]
//static extern IntPtr GetPerspexEAGLContext();
bool _drawQueued;
CADisplayLink _link;
static EAGLContext GetContext()
{
//Ensure initialization
MethodTable.Instance.SetOption((MethodTable.Option)0x10009999, IntPtr.Zero);
var ctx = GetPerspexEAGLContext();
var rv = Runtime.GetNSObject<EAGLContext>(ctx);
rv.DangerousRetain();
return rv;
/* No longer needed with SkiaSharp
//Ensure initialization
MethodTable.Instance.SetOption((MethodTable.Option)0x10009999, IntPtr.Zero);
var ctx = GetPerspexEAGLContext();
var rv = Runtime.GetNSObject<EAGLContext>(ctx);
rv.DangerousRetain();
return rv;
*/
return null;
}
protected SkiaView(Action<Action> registerFrame) : base(UIScreen.MainScreen.ApplicationFrame, GetContext())
protected SkiaView(Action<Action> registerFrame) : base(UIScreen.MainScreen.ApplicationFrame) //, GetContext())
{
registerFrame(OnFrame);
}
protected SkiaView() : base(UIScreen.MainScreen.ApplicationFrame, GetContext())
protected SkiaView() : base(UIScreen.MainScreen.ApplicationFrame) //, GetContext())
{
(_link = CADisplayLink.Create(() => OnFrame())).AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
}
@ -48,7 +51,9 @@ namespace Perspex.Skia.iOS
if (_drawQueued)
{
_drawQueued = false;
Display();
// GLKView
//Display();
}
}

6
src/Skia/Perspex.Skia.iOS/iOSMethodTable.cs

@ -2,6 +2,8 @@
using System.Runtime.InteropServices;
using ObjCRuntime;
/* No longer needed with SkiaSharp
[assembly: LinkWith("Perspex.Skia.iOS.libperspesk_standalone.a", ForceLoad = true, SmartLink = true, IsCxx = true, Frameworks = "ImageIO MobileCoreServices CoreText")]
namespace Perspex.Skia
@ -16,4 +18,6 @@ namespace Perspex.Skia
}
}
}
}
*/

4
src/Skia/Perspex.Skia.iOS/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="xamarinios10" />
</packages>

69
src/Skia/Perspex.Skia/BitmapImpl.cs

@ -5,50 +5,57 @@ using System.Runtime.InteropServices;
using System.Text;
using Perspex.Media;
using Perspex.Platform;
using SkiaSharp;
namespace Perspex.Skia
{
class BitmapImpl : PerspexHandleHolder, IRenderTargetBitmapImpl
class BitmapImpl : IRenderTargetBitmapImpl
{
public void Save(string fileName)
public SKBitmap Bitmap { get; private set; }
public BitmapImpl(SKBitmap bm)
{
Bitmap = bm;
PixelHeight = bm.Width;
PixelWidth = bm.Height;
}
public BitmapImpl(int width, int height)
{
throw new NotImplementedException();
}
public void Dispose()
{
}
public void Save(string fileName)
{
var ext = Path.GetExtension(fileName)?.ToLower();
var type = MethodTable.SkiaImageType.Png;
if(ext=="gif")
type = MethodTable.SkiaImageType.Gif;
if(ext=="jpeg" || ext =="jpg")
type = MethodTable.SkiaImageType.Jpeg;
var skdata = MethodTable.Instance.SaveImage(Handle, type, 100);
var size = MethodTable.Instance.GetSkDataSize(skdata);
var buffer = new byte[size];
MethodTable.Instance.ReadSkData(skdata, buffer, size);
File.WriteAllBytes(fileName, buffer);
// TODO: Implement this for SkiaSharp
throw new NotImplementedException();
//var ext = Path.GetExtension(fileName)?.ToLower();
//var type = MethodTable.SkiaImageType.Png;
//if(ext=="gif")
// type = MethodTable.SkiaImageType.Gif;
//if(ext=="jpeg" || ext =="jpg")
// type = MethodTable.SkiaImageType.Jpeg;
//var skdata = MethodTable.Instance.SaveImage(Handle, type, 100);
//var size = MethodTable.Instance.GetSkDataSize(skdata);
//var buffer = new byte[size];
//MethodTable.Instance.ReadSkData(skdata, buffer, size);
//File.WriteAllBytes(fileName, buffer);
}
public int PixelWidth { get; private set; }
public int PixelHeight { get; private set; }
protected override void Delete(IntPtr handle)
{
MethodTable.Instance.DisposeImage(handle);
}
public DrawingContext CreateDrawingContext()
{
return
new DrawingContext(
new DrawingContextImpl(MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
}
public BitmapImpl(IntPtr handle, int width, int height) : base(handle)
{
PixelHeight = height;
PixelWidth = width;
return
new DrawingContext(
new DrawingContextImpl(null)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
}
public BitmapImpl(int width, int height)
: this(MethodTable.Instance.CreateRenderTargetBitmap(width, height), width, height)
{
}
}
}

289
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@ -3,181 +3,257 @@ using System.Collections.Generic;
using Perspex.Media;
using Perspex.Media.Imaging;
using Perspex.RenderHelpers;
using SkiaSharp;
using System.Linq;
namespace Perspex.Skia
{
unsafe class DrawingContextImpl : PerspexHandleHolder, IDrawingContextImpl
unsafe class DrawingContextImpl : IDrawingContextImpl
{
private readonly NativeDrawingContextSettings* _settings;
public DrawingContextImpl(IntPtr handle) : base(handle)
public SKCanvas Canvas { get; private set; }
public DrawingContextImpl(SKCanvas canvas)
{
_settings = MethodTable.Instance.GetDrawingContextSettingsPtr(handle);
_settings->Opacity = 1;
Canvas = canvas;
}
protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderingContext(handle);
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
{
var impl = (BitmapImpl) source.PlatformImpl;
var s = SkRect.FromRect(sourceRect);
var d = SkRect.FromRect(destRect);
MethodTable.Instance.DrawImage(Handle, impl.Handle, (float) opacity, ref s, ref d);
var impl = (BitmapImpl)source.PlatformImpl;
var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect();
Canvas.DrawBitmap(impl.Bitmap, s, d);
}
public void DrawLine(Pen pen, Point p1, Point p2)
{
using (var brush = CreateBrush(pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
MethodTable.Instance.DrawLine(Handle, brush.Brush,
(float) p1.X, (float) p1.Y, (float) p2.X, (float) p2.Y);
using (var paint = CreatePaint(pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y))))
{
Canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, paint);
}
}
static readonly NativeBrushContainer _dummy = new NativeBrushContainer(null);
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{
var impl = ((StreamGeometryImpl) geometry.PlatformImpl);
var impl = ((StreamGeometryImpl)geometry.PlatformImpl);
var size = geometry.Bounds.Size;
using(var fill = brush!=null?CreateBrush(brush, size):null)
using (var stroke = pen?.Brush != null ? CreateBrush(pen, size) : null)
using (var fill = brush != null ? CreatePaint(brush, size) : null)
using (var stroke = pen?.Brush != null ? CreatePaint(pen, size) : null)
{
MethodTable.Instance.DrawGeometry(Handle, impl.EffectivePath, fill != null ? fill.Brush : null,
stroke != null ? stroke.Brush : null, impl.FillRule == FillRule.EvenOdd);
}
}
if (fill != null)
{
Canvas.DrawPath(impl.EffectivePath, fill);
}
if (stroke != null)
{
Canvas.DrawPath(impl.EffectivePath, stroke);
}
}
}
unsafe NativeBrushContainer CreateBrush(IBrush brush, Size targetSize)
private SKPaint CreatePaint(IBrush brush, Size targetSize)
{
var rv = NativeBrushPool.Instance.Get();
rv.Brush->Opacity = brush.Opacity;
SKPaint paint = new SKPaint();
paint.IsStroke = false;
// TODO: SkiaSharp does not contain alpha yet!
//double opacity = brush.Opacity * _currentOpacity;
//paint.SetAlpha(paint.GetAlpha() * opacity);
paint.IsAntialias = true;
var solid = brush as SolidColorBrush;
if (solid != null)
{
rv.Brush->Type = NativeBrushType.Solid;
rv.Brush->Color = solid.Color.ToUint32();
return rv;
paint.Color = solid.Color.ToSKColor();
return paint;
}
var gradient = brush as GradientBrush;
if (gradient != null)
{
if (gradient.GradientStops.Count > NativeBrush.MaxGradientStops)
throw new NotSupportedException("Maximum supported gradient stop count is " +
NativeBrush.MaxGradientStops);
rv.Brush->GradientSpreadMethod = gradient.SpreadMethod;
rv.Brush->GradientStopCount = gradient.GradientStops.Count;
var tileMode = gradient.SpreadMethod.ToSKShaderTileMode();
var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray();
var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray();
for (var c = 0; c < gradient.GradientStops.Count; c++)
var linearGradient = brush as LinearGradientBrush;
if (linearGradient != null)
{
var st = gradient.GradientStops[c];
rv.Brush->GradientStops[c] = (float) st.Offset;
rv.Brush->GradientStopColors[c] = st.Color.ToUint32();
}
var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint();
var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint();
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
}
var linearGradient = brush as LinearGradientBrush;
if (linearGradient != null)
else
{
rv.Brush->Type = NativeBrushType.LinearGradient;
rv.Brush->GradientStartPoint = linearGradient.StartPoint.ToPixels(targetSize);
rv.Brush->GradientEndPoint = linearGradient.EndPoint.ToPixels(targetSize);
}
var radialGradient = brush as RadialGradientBrush;
if (radialGradient != null)
{
rv.Brush->Type = NativeBrushType.RadialGradient;
rv.Brush->GradientStartPoint = radialGradient.Center.ToPixels(targetSize);
rv.Brush->GradientRadius = (float)radialGradient.Radius;
var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint();
var radius = (float)radialGradient.Radius;
// TODO: There is no SetAlpha in SkiaSharp
//paint.setAlpha(128);
// would be nice to cache these shaders possibly?
var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode);
paint.Shader = shader;
}
}
return paint;
}
var tileBrush = brush as TileBrush;
if (tileBrush != null)
{
rv.Brush->Type = NativeBrushType.Image;
var helper = new TileBrushImplHelper(tileBrush, targetSize);
var bitmap = new BitmapImpl((int) helper.IntermediateSize.Width, (int) helper.IntermediateSize.Height);
rv.AddDisposable(bitmap);
using (var ctx = bitmap.CreateDrawingContext())
helper.DrawIntermediate(ctx);
rv.Brush->Bitmap = bitmap.Handle;
rv.Brush->BitmapTileMode = tileBrush.TileMode;
rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y);
// TODO: Get Tile Brushes working!!!
//
throw new NotImplementedException();
// rv.Brush->Type = NativeBrushType.Image;
// var helper = new TileBrushImplHelper(tileBrush, targetSize);
// var bitmap = new BitmapImpl((int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height);
// rv.AddDisposable(bitmap);
// using (var ctx = bitmap.CreateDrawingContext())
// helper.DrawIntermediate(ctx);
// rv.Brush->Bitmap = bitmap.Handle;
// rv.Brush->BitmapTileMode = tileBrush.TileMode;
// rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y);
// SkMatrix matrix;
// matrix.setTranslate(brush->BitmapTranslation);
// SkShader::TileMode tileX = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipX || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// SkShader::TileMode tileY = brush->BitmapTileMode == ptmNone ? SkShader::kClamp_TileMode
// : (brush->BitmapTileMode == ptmFlipY || brush->BitmapTileMode == ptmFlipXY) ? SkShader::kMirror_TileMode : SkShader::kRepeat_TileMode;
// paint.setShader(SkShader::CreateBitmapShader(brush->Bitmap->Bitmap, tileX, tileY, &matrix))->unref();
}
return rv;
return paint;
}
NativeBrushContainer CreateBrush(Pen pen, Size targetSize)
private SKPaint CreatePaint(Pen pen, Size targetSize)
{
var brush = CreateBrush(pen.Brush, targetSize);
brush.Brush->Stroke = true;
brush.Brush->StrokeThickness = (float)pen.Thickness;
brush.Brush->StrokeLineCap = pen.StartLineCap;
brush.Brush->StrokeLineJoin = pen.LineJoin;
brush.Brush->StrokeMiterLimit = (float)pen.MiterLimit;
if (pen.DashStyle?.Dashes != null)
{
var dashes = pen.DashStyle.Dashes;
if (dashes.Count > NativeBrush.MaxDashCount)
throw new NotSupportedException("Maximum supported dash count is " + NativeBrush.MaxDashCount);
brush.Brush->StrokeDashCount = dashes.Count;
for (int c = 0; c < dashes.Count; c++)
brush.Brush->StrokeDashes[c] = (float) dashes[c];
brush.Brush->StrokeDashOffset = (float)pen.DashStyle.Offset;
var paint = CreatePaint(pen.Brush, targetSize);
}
paint.IsStroke = true;
paint.StrokeWidth = (float) pen.Thickness;
if (pen.StartLineCap == PenLineCap.Round)
paint.StrokeCap = SKStrokeCap.Round;
else if (pen.StartLineCap == PenLineCap.Square)
paint.StrokeCap = SKStrokeCap.Square;
else
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
paint.StrokeJoin = SKStrokeJoin.Mitter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
paint.StrokeJoin = SKStrokeJoin.Bevel;
paint.StrokeMiter = (float) pen.MiterLimit;
//if (pen.DashStyle?.Dashes != null)
//{
// var dashes = pen.DashStyle.Dashes;
// if (dashes.Count > NativeBrush.MaxDashCount)
// throw new NotSupportedException("Maximum supported dash count is " + NativeBrush.MaxDashCount);
// brush.Brush->StrokeDashCount = dashes.Count;
// for (int c = 0; c < dashes.Count; c++)
// brush.Brush->StrokeDashes[c] = (float)dashes[c];
// brush.Brush->StrokeDashOffset = (float)pen.DashStyle.Offset;
//}
return brush;
//if (brush->StrokeDashCount != 0)
//{
// paint.setPathEffect(SkDashPathEffect::Create(brush->StrokeDashes, brush->StrokeDashCount, brush->StrokeDashOffset))->unref();
//}
return paint;
}
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0)
{
using (var brush = CreateBrush(pen, rect.Size))
using (var paint = CreatePaint(pen, rect.Size))
{
var rc = SkRect.FromRect(rect);
MethodTable.Instance.DrawRectangle(Handle, brush.Brush, ref rc, cornerRadius);
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
}
else
{
// this does not appear to exist in SkiaSharp?
throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
}
}
}
public void FillRectangle(IBrush pbrush, Rect rect, float cornerRadius = 0)
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
using (var brush = CreateBrush(pbrush, rect.Size))
using (var paint = CreatePaint(brush, rect.Size))
{
var rc = SkRect.FromRect(rect);
MethodTable.Instance.DrawRectangle(Handle, brush.Brush, ref rc, cornerRadius);
var rc = rect.ToSKRect();
if (cornerRadius == 0)
{
Canvas.DrawRect(rc, paint);
}
else
{
// this does not appear to exist in SkiaSharp?
//throw new NotImplementedException();
//Canvas.DrawRoundedRect(rc, cornerRadius, cornerRadius, paint);
Canvas.DrawRect(rc, paint);
}
}
}
public void DrawText(IBrush foreground, Point origin, FormattedText text)
{
using (var br = CreateBrush(foreground, text.Measure()))
MethodTable.Instance.DrawFormattedText(Handle, br.Brush, ((FormattedTextImpl) text.PlatformImpl).Handle,
(float) origin.X, (float) origin.Y);
using (var paint = CreatePaint(foreground, text.Measure()))
{
var textImpl = text.PlatformImpl as FormattedTextImpl;
textImpl.Draw(this.Canvas, origin.ToSKPoint());
}
}
public void PushClip(Rect clip)
{
var rc = SkRect.FromRect(clip);
MethodTable.Instance.PushClip(Handle, ref rc);
Canvas.Save();
Canvas.ClipRect(clip.ToSKRect());
}
public void PopClip()
{
MethodTable.Instance.PopClip(Handle);
Canvas.Restore();
}
double _currentOpacity = 1.0f;
private readonly Stack<double> _opacityStack = new Stack<double>();
public void PushOpacity(double opacity)
{
_opacityStack.Push(_settings->Opacity);
_settings->Opacity *= opacity;
_opacityStack.Push(_currentOpacity);
_currentOpacity *= opacity;
}
public void PopOpacity() => _settings->Opacity = _opacityStack.Pop();
public void PopOpacity()
{
_currentOpacity = _opacityStack.Pop();
}
public virtual void Dispose()
{
}
private Matrix _currentTransform = Matrix.Identity;
@ -188,9 +264,28 @@ namespace Perspex.Skia
{
if(_currentTransform == value)
return;
_currentTransform = value;
MethodTable.Instance.SetTransform(Handle, value);
}
Canvas.SetMatrix(value.ToSKMatrix());
}
}
}
// not sure we need this yet
internal class WindowDrawingContextImpl : DrawingContextImpl
{
WindowRenderTarget _target;
public WindowDrawingContextImpl(WindowRenderTarget target)
: base(target.Surface.Canvas)
{
_target = target;
}
public override void Dispose()
{
base.Dispose();
_target.Present();
}
}
}

339
src/Skia/Perspex.Skia/FormattedTextImpl.cs

@ -4,32 +4,86 @@ using System.Runtime.InteropServices;
using System.Text;
using Perspex.Media;
using Perspex.Platform;
using SkiaSharp;
namespace Perspex.Skia
{
unsafe class FormattedTextImpl : PerspexHandleHolder, IFormattedTextImpl
unsafe class FormattedTextImpl : IFormattedTextImpl
{
private readonly NativeFormattedText* _shared;
public SKPaint Paint { get; private set; }
//private readonly NativeFormattedText* _shared;
private readonly string _text;
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
public FormattedTextImpl(string text)
{
_text = text;
Paint = new SKPaint();
//Length = length;
Paint.TextEncoding = SKTextEncoding.Utf16;
Paint.IsStroke = false;
Paint.IsAntialias = true;
LineOffset = 0;
//Shared.WidthConstraint = -1.0;
//Data = new pchar[length + 1];
//memcpy(Data, data, length * 2);
//Data[length] = 0;
//Replace 0 characters with zero-width spaces (200B)
_text = _text.Replace((char)0, (char) 0x200B);
//Rebuild();
}
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
TextAlignment textAlignment, FontWeight fontWeight)
{
NativeFormattedText* pShared;
fixed (void* ptext = text)
{
IntPtr handle = MethodTable.Instance.CreateFormattedText(ptext, text.Length,
TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight),
(float) fontSize, textAlignment, &pShared);
return new FormattedTextImpl(handle, pShared, text);
}
}
var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight);
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
FormattedTextImpl instance = new FormattedTextImpl(text);
instance.Paint.Typeface = typeface;
instance.Paint.TextSize = (float) fontSize;
//rv->Paint.setTextAlign(align); //TODO: Manually align
instance.Rebuild();
//NativeFormattedText* pShared;
//fixed (void* ptext = text)
//{
// IntPtr handle = MethodTable.Instance.CreateFormattedText(ptext, text.Length,
// TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight),
// (float) fontSize, textAlignment, &pShared);
// return new FormattedTextImpl(handle, pShared, text);
//}
return instance;
}
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
readonly List<Rect> _rects = new List<Rect>();
Size _size;
public IEnumerable<FormattedTextLine> GetLines()
List<PerspexFormattedTextLine> _skiaLines;
SKRect[] _skiaRects;
Size _size;
const float MAX_LINE_WIDTH = 10000;
float LineOffset;
float WidthConstraint = -1;
struct PerspexFormattedTextLine
{
public float Top;
public int Start;
public int Length;
public float Height;
public float Width;
};
public IEnumerable<FormattedTextLine> GetLines()
{
return _lines;
}
@ -77,37 +131,209 @@ namespace Perspex.Skia
{
}
void Reload()
void Rebuild()
{
_lines.Clear();
_rects.Clear();
float maxX = 0;
for (var c = 0; c < _shared->LineCount; c++)
{
var w = _shared->Lines[c].Width;
if (maxX < w)
maxX = w;
_lines.Add(new FormattedTextLine(_shared->Lines[c].Length, _shared->Lines[c].Height));
}
for (var c = 0; c < _text.Length; c++)
_rects.Add(_shared->Bounds[c].ToRect());
if (_shared->LineCount == 0)
_size = new Size();
else
{
var lastLine = _shared->Lines[_shared->LineCount - 1];
_size = new Size(maxX, lastLine.Top + lastLine.Height);
}
}
var length = _text.Length;
void Rebuild()
{
MethodTable.Instance.RebuildFormattedText(Handle);
Reload();
}
_lines.Clear();
_skiaRects = new SKRect[length];
_skiaLines = new List<PerspexFormattedTextLine>();
int curOff = 0;
int curY = 0;
// TODO: cannot find Font Metrics in SkiaSharp!
//SKPaint.FontMetrics metrics;
float lineHeight = Paint.TextSize; // Paint.getFontMetrics(&metrics);
LineOffset = 0; // -metrics.fTop;
string subString;
byte[] bytes;
GCHandle pinnedArray;
IntPtr pointer;
for (int c = 0; curOff < length; c++)
{
float lineWidth = -1;
int measured;
int extraSkip = 0;
if (WidthConstraint <= 0)
{
measured = length;
}
else
{
float constraint = WidthConstraint;
if (constraint > MAX_LINE_WIDTH)
constraint = MAX_LINE_WIDTH;
subString = _text.Substring(curOff);
// This method is not linking into SkiaSharp so we must use the RAW buffer version
//measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2;
bytes = Encoding.ASCII.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
measured = (int)Paint.BreakText(pointer, (IntPtr)bytes.Length, constraint, out lineWidth);
Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
// some weird unicode byte issue again
if(subString.Length % 2 == 1)
{
measured -= 1;
}
pinnedArray.Free();
if (measured == 0)
{
measured = 1;
lineWidth = -1;
}
char nextChar = ' ';
if (curOff + measured < length)
nextChar = _text[curOff + measured];
if (nextChar != ' ')
{
//Perform scan for the last space and end the line there
for (int si = curOff + measured - 1; si > curOff; si--)
{
if (_text[si] == ' ')
{
measured = si - curOff;
extraSkip = 1;
//Rects[si] = SkRect();
break;
}
}
}
}
PerspexFormattedTextLine line = new PerspexFormattedTextLine();
line.Start = curOff;
line.Length = measured;
line.Width = lineWidth;
line.Height = lineHeight;
line.Top = curY;
if (line.Width < 0)
line.Width = _skiaRects[line.Start + line.Length - 1].Right;
//Build character rects
for (int i = line.Start; i < line.Start + line.Length; i++)
{
float prevRight = 0;
if (i != line.Start)
prevRight = _skiaRects[i - 1].Right;
subString = _text.Substring(line.Start, i - line.Start + 1);
// Unfortunately this version appears to be incorrect and skipping
// even characters. Some issue with Unicode?
//float w = Paint.MeasureText(subString);
bytes = Encoding.ASCII.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
float w = Paint.MeasureText(pointer, (IntPtr)bytes.Length);
pinnedArray.Free();
SKRect rc;
rc.Left = prevRight;
rc.Right = w;
rc.Top = line.Top;
rc.Bottom = line.Top + line.Height;
_skiaRects[i] = rc;
}
subString = _text.Substring(line.Start, line.Length);
// Unfortunately this version appears to be incorrect and skipping
// even characters. Some issue with Unicode?
//line.Width = Paint.MeasureText(subString);
bytes = Encoding.ASCII.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
line.Width = Paint.MeasureText(pointer, (IntPtr)bytes.Length);
pinnedArray.Free();
_skiaLines.Add(line);
curY += (int)lineHeight;
curOff += measured + extraSkip;
}
//Shared.Lines = Lines.data();
//Shared.CharRects = Rects.data();
// Now convert to Perspex data formats
_lines.Clear();
_rects.Clear();
float maxX = 0;
for (var c = 0; c < _skiaLines.Count; c++)
{
var w = _skiaLines[c].Width;
if (maxX < w)
maxX = w;
_lines.Add(new FormattedTextLine(_skiaLines[c].Length, _skiaLines[c].Height));
}
for (var c = 0; c < _text.Length; c++)
_rects.Add(_skiaRects[c].ToPerspexRect());
if (_skiaLines.Count == 0)
_size = new Size();
else
{
var lastLine = _skiaLines[_skiaLines.Count - 1];
_size = new Size(maxX, lastLine.Top + lastLine.Height);
}
}
internal void Draw(SKCanvas canvas, /*PerspexBrush* foreground, */ SKPoint origin)
{
SKPaint paint = Paint;
//ConfigurePaint(paint, ctx, foreground);
/*
//Debugging code for character positions
SkPaint dpaint;
ctx->Canvas->save();
ctx->Canvas->translate(origin.fX, origin.fY);
for (int c = 0; c < Lines.size(); c++)
{
dpaint.setARGB(255, 0, 0, 0);
SkRect rc;
rc.fLeft = 0;
rc.fTop = Lines[c].Top;
rc.fRight = Lines[c].Width;
rc.fBottom = rc.fTop + LineOffset;
ctx->Canvas->drawRect(rc, dpaint);
}
for (int c = 0; c < Length; c++)
{
dpaint.setARGB(255, c % 10 * 125 / 10 + 125, (c * 7) % 10 * 250 / 10, (c * 13) % 10 * 250 / 10);
dpaint.setStyle(SkPaint::kFill_Style);
ctx->Canvas->drawRect(Rects[c], dpaint);
}
ctx->Canvas->restore();
*/
for (int c = 0; c < _skiaLines.Count; c++)
{
PerspexFormattedTextLine line = _skiaLines[c];
var subString = _text.Substring(line.Start, line.Length);
canvas.DrawText(subString, origin.X, origin.Y + line.Top + LineOffset, paint);
}
}
Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
public Size Constraint
{
@ -116,30 +342,23 @@ namespace Perspex.Skia
{
if(_constraint == value)
return;
_constraint = value;
WidthConstraint = (_constraint.Width != double.PositiveInfinity)
? (float)_constraint.Width
: -1;
_shared->WidthConstraint = (_constraint.Width != double.PositiveInfinity)
? (float) _constraint.Width
: -1;
Rebuild();
Rebuild();
}
}
protected override void Delete(IntPtr handle)
{
MethodTable.Instance.DestroyFormattedText(handle);
}
public FormattedTextImpl(IntPtr handle, NativeFormattedText* shared, string text) : base(handle)
{
_shared = shared;
_text = text;
Reload();
}
public override string ToString()
{
return _text;
}
}
public void Dispose()
{
}
}
}

4
src/Skia/Perspex.Skia/MethodTable.cs

@ -7,6 +7,8 @@ using Perspex.Media;
namespace Perspex.Skia
{
/* No longer needed with SkiaSharp
unsafe abstract class MethodTable
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@ -234,5 +236,5 @@ namespace Perspex.Skia
public static readonly MethodTable Instance = new MethodTableImpl();
}
*/
}

2
src/Skia/Perspex.Skia/NativeBrush.cs

@ -6,6 +6,7 @@ using Perspex.Media;
namespace Perspex.Skia
{
/* No longer needed with SkiaSharp
internal enum NativeBrushType
{
Solid,
@ -110,4 +111,5 @@ namespace Perspex.Skia
return _pool.Pop();
}
}
*/
}

14
src/Skia/Perspex.Skia/NativeDrawingContextSettings.cs

@ -1,10 +1,12 @@
using System.Runtime.InteropServices;
/* No longer needed with SkiaSharp
namespace Perspex.Skia
{
[StructLayout(LayoutKind.Sequential)]
struct NativeDrawingContextSettings
{
public double Opacity;
}
}
[StructLayout(LayoutKind.Sequential)]
struct NativeDrawingContextSettings
{
public double Opacity;
}
}
*/

6
src/Skia/Perspex.Skia/NativeFormattedText.cs

@ -1,5 +1,7 @@
using System.Runtime.InteropServices;
/* No longer needed with SkiaSharp
namespace Perspex.Skia
{
[StructLayout(LayoutKind.Sequential)]
@ -20,4 +22,6 @@ namespace Perspex.Skia
public float Height;
public float Width;
};
}
}
*/

11
src/Skia/Perspex.Skia/Perspex.Skia.projitems

@ -12,17 +12,14 @@
<Compile Include="$(MSBuildThisFileDirectory)BitmapImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DrawingContextImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FormattedTextImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MethodTable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NativeBrush.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NativeDrawingContextSettings.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NativeFormattedText.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PerspexHandleHolder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PlatformRenderInterface.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RenderTarget.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkiaPlatform.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkiaPoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkRect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkiaSharpExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)StreamGeometryImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TypefaceCache.cs" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)readme.md" />
</ItemGroup>
</Project>

6
src/Skia/Perspex.Skia/PerspexHandleHolder.cs

@ -1,5 +1,7 @@
using System;
/* No longer needed with SkiaSharp
namespace Perspex.Skia
{
abstract class PerspexHandleHolder : IDisposable
@ -103,4 +105,6 @@ namespace Perspex.Skia
_shared = null;
}
}
}
}
*/

25
src/Skia/Perspex.Skia/PlatformRenderInterface.cs

@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using Perspex.Media;
using Perspex.Platform;
using SkiaSharp;
namespace Perspex.Skia
{
@ -29,15 +30,16 @@ namespace Perspex.Skia
IBitmapImpl LoadBitmap(byte[] data)
{
IntPtr ptr;
int width;
int height;
if (!MethodTable.Instance.LoadImage(data, data.Length, out ptr, out width, out height))
throw new ArgumentException("Unable to load bitmap from provided data");
return new BitmapImpl(ptr, width, height);
}
var bitmap = new SKBitmap();
if(!SKImageDecoder.DecodeMemory(data, bitmap))
{
throw new ArgumentException("Unable to load bitmap from provided data");
}
return new BitmapImpl(bitmap);
}
public IBitmapImpl LoadBitmap(System.IO.Stream stream)
public IBitmapImpl LoadBitmap(System.IO.Stream stream)
{
using (var sr = new BinaryReader(stream))
{
@ -56,10 +58,13 @@ namespace Perspex.Skia
throw new ArgumentException("Width can't be less than 1", nameof(width));
if (height < 1)
throw new ArgumentException("Height can't be less than 1", nameof(height));
return new BitmapImpl(width, height);
}
public IRenderTarget CreateRenderer(IPlatformHandle handle)
=> new RenderTarget(handle);
public IRenderTarget CreateRenderer(IPlatformHandle handle)
{
return new WindowRenderTarget(handle.Handle);
}
}
}

165
src/Skia/Perspex.Skia/RenderTarget.cs

@ -4,24 +4,167 @@ using System.Runtime.InteropServices;
using System.Text;
using Perspex.Media;
using Perspex.Platform;
using SkiaSharp;
// TODO: I'm not sure the best way to bring in the platform specific rendering
//
#if __IOS__
using CoreGraphics;
using UIKit;
#elif WIN32
using Perspex.Win32.Interop;
#endif
namespace Perspex.Skia
{
#if !__ANDROID__
class RenderTarget : PerspexHandleHolder, IRenderTarget
internal class RenderTarget : IRenderTarget
{
public RenderTarget(IPlatformHandle handle) : base(MethodTable.Instance.CreateWindowRenderTarget(handle.Handle))
{
}
public SKSurface Surface { get; protected set; }
protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderTarget(handle);
//protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderTarget(handle);
public DrawingContext CreateDrawingContext()
public virtual DrawingContext CreateDrawingContext()
{
return
new DrawingContext(
new DrawingContextImpl(MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
return
new DrawingContext(
new DrawingContextImpl(Surface.Canvas)); // MethodTable.Instance.RenderTargetCreateRenderingContext(Handle)));
}
}
public void Dispose()
{
//throw new NotImplementedException();
}
}
internal class WindowRenderTarget : RenderTarget
{
private readonly IntPtr _hwnd;
SKBitmap _bitmap;
int Width { get; set; }
int Height { get; set; }
public WindowRenderTarget(IntPtr hwnd)
{
_hwnd = hwnd;
FixSize();
}
void FixSize()
{
int width, height;
GetPlatformWindowSize(_hwnd, out width, out height);
if (Width == width && Height == height)
return;
Width = width;
Height = height;
if(Surface != null)
{
Surface.Dispose();
}
if(_bitmap != null)
{
_bitmap.Dispose();
}
_bitmap = new SKBitmap(width, height, SKColorType.N_32, SKAlphaType.Premul);
//bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
// Wrap the bitmap in a Surface and keep it cached
Surface = SKSurface.Create(_bitmap.Info, pixels, _bitmap.RowBytes);
}
void GetPlatformWindowSize(IntPtr hwnd, out int w, out int h)
{
#if __IOS__
var bounds = UIScreen.MainScreen.ApplicationFrame;
w = (int) bounds.Width;
h = (int)bounds.Height;
#elif WIN32
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_hwnd, out rc);
w = rc.right - rc.left;
h = rc.bottom - rc.top;
#else
throw new NotImplementedException();
#endif
}
public override DrawingContext CreateDrawingContext()
{
FixSize();
var canvas = Surface.Canvas;
canvas.RestoreToCount(0);
canvas.Save();
#if __IOS__
var screenScale = UIScreen.MainScreen.Scale;
canvas.Scale((float)screenScale, (float)screenScale);
#endif
canvas.Clear(SKColors.Red);
canvas.ResetMatrix();
return
new DrawingContext(
new WindowDrawingContextImpl(this));
}
public void Present()
{
_bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
#if __IOS__
const int bitmapInfo = ((int)CGBitmapFlags.ByteOrder32Big) | ((int)CGImageAlphaInfo.PremultipliedLast);
var bounds = UIScreen.MainScreen.ApplicationFrame;
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
using (var bContext = new CGBitmapContext(pixels, _bitmap.Width, _bitmap.Height, 8, _bitmap.Width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
using (var image = bContext.ToImage())
using (var context = UIGraphics.GetCurrentContext())
{
// flip the image for CGContext.DrawImage
context.TranslateCTM(0, bounds.Height); // Frame.Height);
context.ScaleCTM(1, -1);
context.DrawImage(bounds, image);
}
#elif WIN32
UnmanagedMethods.BITMAPINFO bmi = new UnmanagedMethods.BITMAPINFO();
bmi.biSize = UnmanagedMethods.SizeOf_BITMAPINFOHEADER;
bmi.biWidth = _bitmap.Width;
bmi.biHeight = -_bitmap.Height; // top-down image
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biCompression = (uint)UnmanagedMethods.BitmapCompressionMode.BI_RGB;
bmi.biSizeImage = 0;
IntPtr hdc = UnmanagedMethods.GetDC(_hwnd);
int ret = UnmanagedMethods.SetDIBitsToDevice(hdc,
0, 0,
(uint) _bitmap.Width, (uint) _bitmap.Height,
0, 0,
0, (uint) _bitmap.Height,
pixels,
ref bmi,
(uint)UnmanagedMethods.DIBColorTable.DIB_RGB_COLORS);
UnmanagedMethods.ReleaseDC(_hwnd, hdc);
#endif
_bitmap.UnlockPixels();
}
}
}

7
src/Skia/Perspex.Skia/SkRect.cs

@ -1,8 +1,11 @@
using System;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
/* No longer needed with SkiaSharp
namespace Perspex.Skia
{
[StructLayout(LayoutKind.Sequential)]
@ -24,3 +27,5 @@ namespace Perspex.Skia
public Rect ToRect() => new Rect(Left, Top, Right - Left, Bottom - Top);
}
}
*/

17
src/Skia/Perspex.Skia/SkiaPlatform.cs

@ -9,17 +9,22 @@ namespace Perspex.Skia
{
private static bool s_forceSoftwareRendering;
public static void Initialize()
=> PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new PlatformRenderInterface());
public static void Initialize()
{
PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new PlatformRenderInterface());
}
public static bool ForceSoftwareRendering
public static bool ForceSoftwareRendering
{
get { return s_forceSoftwareRendering; }
set
{
s_forceSoftwareRendering = value;
MethodTable.Instance.SetOption(MethodTable.Option.ForceSoftware, new IntPtr(value ? 1 : 0));
}
}
// Do we still need this with SkiaSharp??
//MethodTable.Instance.SetOption(MethodTable.Option.ForceSoftware, new IntPtr(value ? 1 : 0));
throw new NotImplementedException();
}
}
}
}

6
src/Skia/Perspex.Skia/SkiaPoint.cs

@ -1,5 +1,7 @@
using System.Runtime.InteropServices;
/* No longer needed with SkiaSharp
namespace Perspex.Skia
{
[StructLayout(LayoutKind.Sequential)]
@ -26,4 +28,6 @@ namespace Perspex.Skia
public static implicit operator SkiaPoint(Point pt) => new SkiaPoint(pt);
}
}
}
*/

57
src/Skia/Perspex.Skia/SkiaSharpExtensions.cs

@ -0,0 +1,57 @@
using SkiaSharp;
namespace Perspex.Skia
{
public static class SkiaSharpExtensions
{
public static SKPoint ToSKPoint(this Point p)
{
return new SKPoint((float)p.X, (float)p.Y);
}
public static SKRect ToSKRect(this Rect r)
{
return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom);
}
public static Rect ToPerspexRect(this SKRect r)
{
return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top);
}
public static SKMatrix ToSKMatrix(this Matrix m)
{
var sm = new SKMatrix
{
ScaleX = (float)m.M11,
SkewX = (float)m.M21,
TransX = (float)m.M31,
SkewY = (float)m.M12,
ScaleY = (float)m.M22,
TransY = (float)m.M32,
Persp0 = 0,
Persp1 = 0,
Persp2 = 1
};
return sm;
}
public static SKColor ToSKColor(this Media.Color c)
{
return new SKColor(c.R, c.G, c.B, c.A);
}
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m)
{
switch (m)
{
default:
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp;
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror;
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
}
}
}
}

178
src/Skia/Perspex.Skia/StreamGeometryImpl.cs

@ -6,46 +6,18 @@ using System.Text;
using Perspex.Media;
using Perspex.Platform;
using Perspex.RenderHelpers;
using SkiaSharp;
namespace Perspex.Skia
{
enum SkiaGeometryElementType
{
LineTo,
QuadTo,
BezierTo,
BeginFigure,
EndFigure
};
[StructLayout(LayoutKind.Sequential)]
struct SkiaGeometryElement
{
public SkiaGeometryElementType Type;
public SkiaPoint Point1, Point2, Point3;
public bool Flag;
}
class SkPath : PerspexHandleHolder
{
public SkPath(IntPtr handle) : base(handle)
{
}
protected override void Delete(IntPtr handle)
{
MethodTable.Instance.DisposePath(handle);
}
}
class StreamGeometryImpl : IStreamGeometryImpl
{
RefCountable<SkPath> _path;
RefCountable<SkPath> _transformedPath;
private Matrix _transform = Matrix.Identity;
SKPath _path;
SKPath _transformedPath;
private Matrix _transform = Matrix.Identity;
public IntPtr EffectivePath => (_transformedPath ?? _path).Handle;
public SKPath EffectivePath => (_transformedPath ?? _path);
public Rect GetRenderBounds(double strokeThickness)
{
@ -62,6 +34,7 @@ namespace Perspex.Skia
{
if(_transform == value)
return;
_transform = value;
ApplyTransform();
}
@ -69,108 +42,115 @@ namespace Perspex.Skia
void ApplyTransform()
{
if(_path == null)
return;
if (_transformedPath != null)
{
_transformedPath.Dispose();
_transformedPath = null;
}
if (!_transform.IsIdentity)
_transformedPath =
new RefCountable<SkPath>(new SkPath(MethodTable.Instance.TransformPath(_path.Handle, Transform)));
}
public IStreamGeometryImpl Clone()
{
return new StreamGeometryImpl {_path = _path?.Clone(), _transformedPath = _transformedPath?.Clone(), _transform = Transform, Bounds = Bounds};
}
if (_path == null)
return;
if (_transformedPath != null)
{
_transformedPath.Dispose();
_transformedPath = null;
}
// TODO: SkiaSharp does not expose Transform yet!!!
//if (!Transform.IsIdentity)
//{
// _transformedPath = new SKPath();
// _path.Transform(Transform.ToSKMatrix(), _transformedPath);
//}
}
public IStreamGeometryImpl Clone()
{
// TODO: there is no SKPath.Clone yet!!!!!!!!!!!!!
//
//return new StreamGeometryImpl
//{
// _path = _path?.Clone(),
// _transformedPath = _transformedPath?.Clone(),
// _transform = Transform,
// Bounds = Bounds
//};
return this; // this will probably end bad!!
}
public IStreamGeometryContextImpl Open()
{
_path = new SKPath();
return new StreamContext(this);
}
class StreamContext : IStreamGeometryContextImpl
{
private readonly StreamGeometryImpl _geometryImpl;
readonly List<SkiaGeometryElement> _elements = new List<SkiaGeometryElement>();
private SKPath _path;
//readonly List<SkiaGeometryElement> _elements = new List<SkiaGeometryElement>();
Point _currentPoint;
public StreamContext(StreamGeometryImpl geometryImpl)
{
_geometryImpl = geometryImpl;
}
_path = _geometryImpl._path;
}
public void Dispose()
{
var arr = _elements.ToArray();
SkRect rc;
_geometryImpl._path?.Dispose();
_geometryImpl._path =
new RefCountable<SkPath>(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)));
_geometryImpl.ApplyTransform();
public void Dispose()
{
// Not sure what we need to do here....
_geometryImpl.Bounds = rc.ToRect();
// //throw new NotImplementedException();
}
// var arr = _elements.ToArray();
// SkRect rc;
// _path?.Dispose();
// _path = new SKPath(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)));
// _geometryImpl.ApplyTransform();
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
// _geometryImpl.Bounds = rc.ToRect();
SKRect rc;
_path.GetBounds(out rc);
_geometryImpl.ApplyTransform();
_geometryImpl.Bounds = rc.ToPerspexRect();
}
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
{
ArcToHelper.ArcTo(this, _currentPoint, point, size, rotationAngle, isLargeArc, sweepDirection);
_currentPoint = point;
throw new NotImplementedException();
//ArcToHelper.ArcTo(this, _currentPoint, point, size, rotationAngle, isLargeArc, sweepDirection);
//_currentPoint = point;
}
public void BeginFigure(Point startPoint, bool isFilled)
{
_elements.Add(new SkiaGeometryElement
{
Type = SkiaGeometryElementType.BeginFigure,
Point1 = new SkiaPoint(startPoint),
Flag = isFilled
});
_currentPoint = startPoint;
_path.MoveTo((float)startPoint.X, (float)startPoint.Y);
_currentPoint = startPoint;
}
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
_elements.Add(new SkiaGeometryElement
{
Type = SkiaGeometryElementType.BezierTo,
Point1 = new SkiaPoint(point1),
Point2 = new SkiaPoint(point2),
Point3 = new SkiaPoint(point3)
});
_currentPoint = point3;
_path.CubicTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y, (float)point3.X, (float)point3.Y);
_currentPoint = point3;
}
public void QuadraticBezierTo(Point control, Point endPoint)
public void QuadraticBezierTo(Point point1, Point point2)
{
_elements.Add(new SkiaGeometryElement
{
Type = SkiaGeometryElementType.QuadTo,
Point1 = control,
Point2 = endPoint
});
_currentPoint = endPoint;
_path.QuadTo((float)point1.X, (float)point1.Y, (float)point2.X, (float)point2.Y);
_currentPoint = point2;
}
public void LineTo(Point point)
{
_elements.Add(new SkiaGeometryElement
{
Type = SkiaGeometryElementType.LineTo,
Point1 = new SkiaPoint(point)
});
_currentPoint = point;
_path.LineTo((float)point.X, (float)point.Y);
_currentPoint = point;
}
public void EndFigure(bool isClosed)
{
_elements.Add(new SkiaGeometryElement
{
Type = SkiaGeometryElementType.EndFigure,
Flag = isClosed
});
if(isClosed)
{
_path.Close();
}
}
public void SetFillRule(FillRule fillRule)

52
src/Skia/Perspex.Skia/TypefaceCache.cs

@ -2,49 +2,45 @@
using System.Collections.Generic;
using System.Text;
using Perspex.Media;
using SkiaSharp;
namespace Perspex.Skia
{
static class TypefaceCache
{
static readonly Dictionary<string, Dictionary<Style, IntPtr>>Cache = new Dictionary<string, Dictionary<Style, IntPtr>>();
unsafe static IntPtr GetTypeface(string name, Style style)
static readonly Dictionary<string, Dictionary<SKTypefaceStyle, SKTypeface>> Cache = new Dictionary<string, Dictionary<SKTypefaceStyle, SKTypeface>>();
unsafe static SKTypeface GetTypeface(string name, SKTypefaceStyle style)
{
if (name == null)
name = "Arial";
Dictionary<Style, IntPtr> entry;
Dictionary<SKTypefaceStyle, SKTypeface> entry;
if (!Cache.TryGetValue(name, out entry))
Cache[name] = entry = new Dictionary<Style, IntPtr>();
IntPtr rv;
if (!entry.TryGetValue(style, out rv))
Cache[name] = entry = new Dictionary<SKTypefaceStyle, SKTypeface>();
SKTypeface typeface = null;
if (!entry.TryGetValue(style, out typeface))
{
var bytes = Encoding.ASCII.GetBytes(name);
var buffer = new byte[bytes.Length + 1];
bytes.CopyTo(buffer, 0);
fixed (void* pname = buffer)
{
entry[style] = rv = MethodTable.Instance.CreateTypeface(pname, (int)style);
}
}
return rv;
typeface = SKTypeface.FromFamilyName(name, style);
if(typeface == null)
{
typeface = SKTypeface.FromFamilyName(null, style);
}
entry[style] = typeface;
}
return typeface;
}
[Flags]
enum Style
public static SKTypeface GetTypeface(string name, FontStyle style, FontWeight weight)
{
Normal = 0,
Bold = 0x01,
Italic = 0x02,
BoldItalic = 0x03
};
public static IntPtr GetTypeface(string name, FontStyle style, FontWeight weight)
{
Style sstyle = Style.Normal;
SKTypefaceStyle sstyle = SKTypefaceStyle.Normal;
if (style != FontStyle.Normal)
sstyle |= Style.Italic;
sstyle |= SKTypefaceStyle.Italic;
if(weight>FontWeight.Normal)
sstyle |= Style.Bold;
sstyle |= SKTypefaceStyle.Bold;
return GetTypeface(name, sstyle);
}

42
src/Skia/Perspex.Skia/readme.md

@ -0,0 +1,42 @@
TODO:
BitmapImpl
- constructor from Width/Height
- Save
DrawingContextImpl
- DrawRoundRect is not properly implemented due to lack of support in Skia
- Alpha support missing as SkiaSharp does not expose this
- Gradient Shader caching?
- TileBrushes
- Pen Dash styles
Formatted Text Rendering
- still needs a lot of work once SkiaSharp implementation is better
RenderTarget
- Figure out a cleaner implementation across all platforms
- HW accelerated working?
StreamGeometry
- Paths within Paths may not work right
- Paths cannot be Cloned (lack of SkiaSupport)
- Paths cannot be transformed (lack of SkiaSupport)
- ArcTo
App Bootstrapping
- Cleanup the testapplications across all platforms
- Add a cleaner Fluent API for the subsystems
- ie. app.UseDirect2D() (via platform specific extension methods)
Android
- Testing & fixes
iOS
- Get GLView working again?
General
- Cleanup/eliminate obsolete files
- Cleanup the many Test Applications
- Get Skia Unit Tests passing

67
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -532,7 +532,72 @@ namespace Perspex.Win32.Interop
WM_DISPATCH_WORK_ITEM = WM_USER,
}
[DllImport("user32.dll", SetLastError = true)]
public enum BitmapCompressionMode : uint
{
BI_RGB = 0,
BI_RLE8 = 1,
BI_RLE4 = 2,
BI_BITFIELDS = 3,
BI_JPEG = 4,
BI_PNG = 5
}
public enum DIBColorTable
{
DIB_RGB_COLORS = 0, /* color table in RGBs */
DIB_PAL_COLORS /* color table in palette indices */
};
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
// C# cannot inlay structs in structs so must expand directly here
//
//[StructLayout(LayoutKind.Sequential)]
//public struct BITMAPINFOHEADER
//{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public BitmapCompressionMode biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
//}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public uint[] cols;
}
public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll")]
public static extern int SetDIBitsToDevice(IntPtr hdc, int XDest, int YDest,
uint dwWidth, uint dwHeight,
int XSrc, int YSrc,
uint uStartScan, uint cScanLines,
IntPtr lpvBits, [In] ref BITMAPINFO lpbmi, uint fuColorUse);
[DllImport("user32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool AdjustWindowRectEx(ref RECT lpRect, uint dwStyle, bool bMenu, uint dwExStyle);
[DllImport("user32.dll")]

68
src/iOS/Perspex.iOSTestApplication/Info.plist

@ -1,34 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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>CFBundleDisplayName</key>
<string>Perspex.iOSTestApplication</string>
<key>CFBundleIdentifier</key>
<string>com.your-company.Perspex.iOSTestApplication</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</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>7.0</string>
</dict>
</plist>
<dict>
<key>CFBundleDisplayName</key>
<string>Perspex.iOSTestApplication</string>
<key>CFBundleIdentifier</key>
<string>com.your-company.Perspex.iOSTestApplication</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</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>8.0</string>
<key>CFBundleIconFiles</key>
<array>
<string>Default-568h@2x.png</string>
</array>
</dict>
</plist>

Loading…
Cancel
Save