Support the SkiaSharp (#202) accepted/tizen/5.5/unified/20200421.150457 accepted/tizen/unified/20200421.051621 submit/tizen/20200420.234117 submit/tizen_5.5/20200420.234045
author유리나/Common Platform Lab(SR)/Staff Engineer/삼성전자 <rina6350.you@samsung.com>
Mon, 20 Apr 2020 07:52:18 +0000 (03:52 -0400)
committer윤정현/Common Platform Lab(SR)/Staff Engineer/삼성전자 <jh0506.yun@samsung.com>
Mon, 20 Apr 2020 07:52:18 +0000 (16:52 +0900)
Head Commit :
  commit 6a3f6cdbacc8bb6f7425a8087eefdf5a7b0563e3 (origin/master, origin/HEAD)
  Author: Immo Landwerth <immo@landwerth.net>
  Date:   Wed Apr 8 02:00:42 2020 -0700

     Link Code of Conduct (#1216)

libSkiaSharp version : 1.68.2
libHarfBuzz : 2.6.1

137 files changed:
LICENSE
packages/system.buffers.4.4.0.nupkg [new file with mode: 0644]
packages/system.memory.4.5.3.nupkg [new file with mode: 0644]
packages/system.numerics.vectors.4.4.0.nupkg [new file with mode: 0644]
packages/system.runtime.compilerservices.unsafe.4.5.2.nupkg [new file with mode: 0644]
packaging/xsf.spec
src/XSF/HarfBuzzSharp/Blob.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Buffer.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Definitions.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/DelegateProxies.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/DelegateProxies.font.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/DelegateProxies.shared.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/DelegateProxies.unicode.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Face.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Font.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/FontFunctions.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/HarfBuzzApi.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/HashCode.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Language.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/NativeObject.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/PlatformConfiguration.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Script.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Script.fields.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/Tag.cs [new file with mode: 0644]
src/XSF/HarfBuzzSharp/UnicodeFunctions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.HarfBuzz/BlobExtensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.HarfBuzz/CanvasExtensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.HarfBuzz/FontExtensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.HarfBuzz/SKShaper.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/Extensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/Registrar.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/RendererTypes.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasView.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRenderer.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRendererBase.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLView.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRenderer.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRendererBase.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSource.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSourceHandler.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintGLSurfaceEventArgs.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintSurfaceEventArgs.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchEventArgs.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchHandler.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/CustomRenderingView.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/Extensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/GlesInterop/Gles.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/Interop/Elementary.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/Interop/Evas.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/Interop/Libraries.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/RenderingMode.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/SKCanvasView.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/SKGLSurfaceView.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/SKPaintGLSurfaceEventArgs.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/SKPaintSurfaceEventArgs.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/ScalingInfo.cs [new file with mode: 0644]
src/XSF/SkiaSharp.Views.Tizen/TizenExtensions.cs [new file with mode: 0644]
src/XSF/SkiaSharp/Definitions.cs [new file with mode: 0644]
src/XSF/SkiaSharp/DelegateProxies.cs [new file with mode: 0644]
src/XSF/SkiaSharp/DelegateProxies.shared.cs [new file with mode: 0644]
src/XSF/SkiaSharp/GRBackendRenderTarget.cs [new file with mode: 0644]
src/XSF/SkiaSharp/GRBackendTexture.cs [new file with mode: 0644]
src/XSF/SkiaSharp/GRContext.cs [new file with mode: 0644]
src/XSF/SkiaSharp/GRDefinitions.cs [new file with mode: 0644]
src/XSF/SkiaSharp/GRGlInterface.cs [new file with mode: 0644]
src/XSF/SkiaSharp/HandleDictionary.cs [new file with mode: 0644]
src/XSF/SkiaSharp/HashCode.cs [new file with mode: 0644]
src/XSF/SkiaSharp/MathTypes.cs [new file with mode: 0644]
src/XSF/SkiaSharp/PlatformConfiguration.cs [new file with mode: 0644]
src/XSF/SkiaSharp/PreserveAttribute.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SK3dView.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKAbstractManagedStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKAbstractManagedWStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKAutoCoInitialize.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKBitmap.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKCanvas.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKCodec.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColor.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColorF.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColorFilter.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColorSpace.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColorSpaceStructs.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColorTable.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKColors.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKData.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKDocument.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKDrawable.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKFontManager.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKFontStyle.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKFontStyleSet.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKFrontBufferedManagedStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKFrontBufferedStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKImage.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKImageFilter.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKImageInfo.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKManagedStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKManagedWStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKMask.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKMaskFilter.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKMatrix.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKMatrix44.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKNWayCanvas.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKNoDrawCanvas.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKObject.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKOverdrawCanvas.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPMColor.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPaint.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPath.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPathEffect.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPathMeasure.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPicture.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPictureRecorder.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPixelSerializer.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKPixmap.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKRegion.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKRotationScaleMatrix.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKRoundRect.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKRunBuffer.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKSVG.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKShader.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKStream.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKString.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKSurface.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKSurfaceProperties.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKSwizzle.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKTextBlob.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKTypeface.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKVertices.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SKXml.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SkiaApi.cs [new file with mode: 0644]
src/XSF/SkiaSharp/SkiaApi.generated.cs [new file with mode: 0644]
src/XSF/SkiaSharp/Util.cs [new file with mode: 0644]
src/XSF/XSF.csproj
src/XSF/lib/armel/libHarfBuzzSharp.2.6.1.so [new file with mode: 0755]
src/XSF/lib/armel/libSkiaSharp.1.68.2.so [new file with mode: 0755]
src/XSF/lib/x86/libHarfBuzzSharp.2.6.1.so [new file with mode: 0755]
src/XSF/lib/x86/libSkiaSharp.1.68.2.so [new file with mode: 0755]

diff --git a/LICENSE b/LICENSE
index f0cafc6..2b4661e 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -23,3 +23,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
+
+SkiaSharp
+
+Copyright (c) 2015-2016 Xamarin, Inc.
+Copyright (c) 2017-2018 Microsoft Corporation.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/system.buffers.4.4.0.nupkg b/packages/system.buffers.4.4.0.nupkg
new file mode 100644 (file)
index 0000000..c261fa8
Binary files /dev/null and b/packages/system.buffers.4.4.0.nupkg differ
diff --git a/packages/system.memory.4.5.3.nupkg b/packages/system.memory.4.5.3.nupkg
new file mode 100644 (file)
index 0000000..5fa1550
Binary files /dev/null and b/packages/system.memory.4.5.3.nupkg differ
diff --git a/packages/system.numerics.vectors.4.4.0.nupkg b/packages/system.numerics.vectors.4.4.0.nupkg
new file mode 100644 (file)
index 0000000..d1faf30
Binary files /dev/null and b/packages/system.numerics.vectors.4.4.0.nupkg differ
diff --git a/packages/system.runtime.compilerservices.unsafe.4.5.2.nupkg b/packages/system.runtime.compilerservices.unsafe.4.5.2.nupkg
new file mode 100644 (file)
index 0000000..4f464e1
Binary files /dev/null and b/packages/system.runtime.compilerservices.unsafe.4.5.2.nupkg differ
index 597635e..3f0e134 100644 (file)
@@ -7,15 +7,16 @@ Group: Graphics & UI Framework/Libraries
 Source0: %{name}-%{version}.tar.gz
 Source1: %{name}.manifest
 
-BuildArch: noarch
 AutoReqProv: no
 ExcludeArch: aarch64
 
 %define DOTNET_ASSEMBLY_PATH /usr/share/dotnet.tizen/framework
 %define DOTNET_NUGET_SOURCE /nuget
+%define SKIA_LIBRARY_PATH /usr/lib
+%define LIB_HARFBUZZ_NAME libHarfBuzzSharp.2.6.1.so
+%define LIB_SKIASHARP_NAME libSkiaSharp.1.68.2.so
 
 BuildRequires: dotnet-build-tools
-
 Requires: coreclr
 Requires: mscorlib
 Requires: corefx-native
@@ -38,7 +39,17 @@ cp %{DOTNET_NUGET_SOURCE}/*.nupkg ./packages/
 mkdir -p %{buildroot}%{DOTNET_ASSEMBLY_PATH}
 mv ./src/XSF/bin/Release/netstandard2.0/XSF.dll %{buildroot}%{DOTNET_ASSEMBLY_PATH}
 
+mkdir -p %{buildroot}%{SKIA_LIBRARY_PATH}
+%ifarch %{ix86}
+mv ./src/XSF/lib/x86/%{LIB_HARFBUZZ_NAME} %{buildroot}%{SKIA_LIBRARY_PATH}
+mv ./src/XSF/lib/x86/%{LIB_SKIASHARP_NAME} %{buildroot}%{SKIA_LIBRARY_PATH}
+%else
+mv ./src/XSF/lib/armel/%{LIB_HARFBUZZ_NAME} %{buildroot}%{SKIA_LIBRARY_PATH}
+mv ./src/XSF/lib/armel/%{LIB_SKIASHARP_NAME} %{buildroot}%{SKIA_LIBRARY_PATH}
+%endif
+
 %files
 %manifest %{name}.manifest
 %license LICENSE LICENSE.Flora-1.1 LICENSE.Apache-2.0
 %attr(644,root,root) %{DOTNET_ASSEMBLY_PATH}/*.dll
+%attr(644,root,root) %{SKIA_LIBRARY_PATH}/*.so
diff --git a/src/XSF/HarfBuzzSharp/Blob.cs b/src/XSF/HarfBuzzSharp/Blob.cs
new file mode 100644 (file)
index 0000000..4bd1808
--- /dev/null
@@ -0,0 +1,108 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace HarfBuzzSharp
+{
+       public class Blob : NativeObject
+       {
+               private static readonly Lazy<Blob> emptyBlob = new Lazy<Blob> (() => new StaticBlob (HarfBuzzApi.hb_blob_get_empty ()));
+
+               public static Blob Empty => emptyBlob.Value;
+
+               internal Blob (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Blob(IntPtr, int, MemoryMode, ReleaseDelegate) instead.")]
+               public Blob (IntPtr data, uint length, MemoryMode mode, object userData, BlobReleaseDelegate releaseDelegate)
+                       : this (data, (int)length, mode, () => releaseDelegate?.Invoke (userData))
+               {
+               }
+
+               public Blob (IntPtr data, int length, MemoryMode mode)
+                       : this (data, length, mode, null)
+               {
+               }
+
+               public Blob (IntPtr data, int length, MemoryMode mode, ReleaseDelegate releaseDelegate)
+                       : this (Create (data, length, mode, releaseDelegate))
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_blob_destroy (Handle);
+                       }
+               }
+
+               public int Length => HarfBuzzApi.hb_blob_get_length (Handle);
+
+               public int FaceCount => HarfBuzzApi.hb_face_count (Handle);
+
+               public bool IsImmutable => HarfBuzzApi.hb_blob_is_immutable (Handle);
+
+               public void MakeImmutable () => HarfBuzzApi.hb_blob_make_immutable (Handle);
+
+               public unsafe Stream AsStream ()
+               {
+                       var dataPtr = HarfBuzzApi.hb_blob_get_data (Handle, out var length);
+                       return new UnmanagedMemoryStream (dataPtr, length);
+               }
+
+               public unsafe ReadOnlySpan<byte> AsSpan ()
+               {
+                       var dataPtr = HarfBuzzApi.hb_blob_get_data (Handle, out var length);
+                       return new ReadOnlySpan<byte> (dataPtr, length);
+               }
+
+               public static Blob FromFile (string fileName)
+               {
+                       if (!File.Exists (fileName)) {
+                               throw new FileNotFoundException ("Unable to find file.", fileName);
+                       }
+
+                       var blob = HarfBuzzApi.hb_blob_create_from_file (fileName);
+                       return new Blob (blob);
+               }
+
+               public static unsafe Blob FromStream (Stream stream)
+               {
+                       // TODO: check to see if we can avoid the second copy (the ToArray)
+
+                       using (var ms = new MemoryStream ()) {
+                               stream.CopyTo (ms);
+                               var data = ms.ToArray ();
+
+                               fixed (byte* dataPtr = data) {
+                                       return new Blob ((IntPtr)dataPtr, data.Length, MemoryMode.ReadOnly, () => ms.Dispose ());
+                               }
+                       }
+               }
+
+               private static IntPtr Create (IntPtr data, int length, MemoryMode mode, ReleaseDelegate releaseProc)
+               {
+                       var proxy = DelegateProxies.Create (releaseProc, DelegateProxies.ReleaseDelegateProxy, out _, out var ctx);
+                       return HarfBuzzApi.hb_blob_create (data, length, mode, ctx, proxy);
+               }
+
+               private class StaticBlob : Blob
+               {
+                       public StaticBlob (IntPtr handle)
+                               : base (handle)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Buffer.cs b/src/XSF/HarfBuzzSharp/Buffer.cs
new file mode 100644 (file)
index 0000000..a8cd881
--- /dev/null
@@ -0,0 +1,367 @@
+using System;
+using System.Buffers;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace HarfBuzzSharp
+{
+       public class Buffer : NativeObject
+       {
+               public const int DefaultReplacementCodepoint = '\uFFFD';
+
+               internal Buffer (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               public Buffer ()
+                       : this (HarfBuzzApi.hb_buffer_create ())
+               {
+               }
+
+               public ContentType ContentType {
+                       get => HarfBuzzApi.hb_buffer_get_content_type (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_content_type (Handle, value);
+               }
+
+               public Direction Direction {
+                       get => HarfBuzzApi.hb_buffer_get_direction (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_direction (Handle, value);
+               }
+
+               public Language Language {
+                       get => new Language (HarfBuzzApi.hb_buffer_get_language (Handle));
+                       set => HarfBuzzApi.hb_buffer_set_language (Handle, value.Handle);
+               }
+
+               public BufferFlags Flags {
+                       get => HarfBuzzApi.hb_buffer_get_flags (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_flags (Handle, value);
+               }
+
+               public ClusterLevel ClusterLevel {
+                       get => HarfBuzzApi.hb_buffer_get_cluster_level (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_cluster_level (Handle, value);
+               }
+
+               public uint ReplacementCodepoint {
+                       get => HarfBuzzApi.hb_buffer_get_replacement_codepoint (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_replacement_codepoint (Handle, value);
+               }
+
+               public uint InvisibleGlyph {
+                       get => HarfBuzzApi.hb_buffer_get_invisible_glyph (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_invisible_glyph (Handle, value);
+               }
+
+               public Script Script {
+                       get => HarfBuzzApi.hb_buffer_get_script (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_script (Handle, value);
+               }
+
+               public int Length {
+                       get => HarfBuzzApi.hb_buffer_get_length (Handle);
+                       set => HarfBuzzApi.hb_buffer_set_length (Handle, value);
+               }
+
+               public UnicodeFunctions UnicodeFunctions {
+                       get => new UnicodeFunctions (HarfBuzzApi.hb_buffer_get_unicode_funcs (Handle));
+                       set => HarfBuzzApi.hb_buffer_set_unicode_funcs (Handle, value.Handle);
+               }
+
+               public GlyphInfo[] GlyphInfos {
+                       get {
+                               var array = GetGlyphInfoSpan ().ToArray ();
+                               GC.KeepAlive (this);
+                               return array;
+                       }
+               }
+
+               public GlyphPosition[] GlyphPositions {
+                       get {
+                               var array = GetGlyphPositionSpan ().ToArray ();
+                               GC.KeepAlive (this);
+                               return array;
+                       }
+               }
+
+               public void Add (int codepoint, int cluster) => Add ((uint)codepoint, (uint)cluster);
+
+               public void Add (uint codepoint, uint cluster)
+               {
+                       if (Length != 0 && ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("Non empty buffer's ContentType must be of type Unicode.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be of type Glyphs");
+
+                       HarfBuzzApi.hb_buffer_add (Handle, codepoint, cluster);
+               }
+
+               public void AddUtf8 (string utf8text) => AddUtf8 (Encoding.UTF8.GetBytes (utf8text), 0, -1);
+
+               public void AddUtf8 (byte[] bytes) => AddUtf8 (new ReadOnlySpan<byte> (bytes));
+
+               public void AddUtf8 (ReadOnlySpan<byte> text) => AddUtf8 (text, 0, -1);
+
+               public unsafe void AddUtf8 (ReadOnlySpan<byte> text, int itemOffset, int itemLength)
+               {
+                       fixed (byte* bytes = text) {
+                               AddUtf8 ((IntPtr)bytes, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddUtf8 (IntPtr text, int textLength) => AddUtf8 (text, textLength, 0, -1);
+
+               public void AddUtf8 (IntPtr text, int textLength, int itemOffset, int itemLength)
+               {
+                       if (itemOffset < 0)
+                               throw new ArgumentOutOfRangeException (nameof (itemOffset), "ItemOffset must be non negative.");
+                       if (Length != 0 && ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("Non empty buffer's ContentType must be of type Unicode.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be Glyphs");
+
+                       HarfBuzzApi.hb_buffer_add_utf8 (Handle, text, textLength, itemOffset, itemLength);
+               }
+
+               public void AddUtf16 (string text) => AddUtf16 (text, 0, -1);
+
+               public unsafe void AddUtf16 (string text, int itemOffset, int itemLength)
+               {
+                       fixed (char* chars = text) {
+                               AddUtf16 ((IntPtr)chars, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public unsafe void AddUtf16 (ReadOnlySpan<byte> text)
+               {
+                       fixed (byte* bytes = text) {
+                               AddUtf16 ((IntPtr)bytes, text.Length / 2);
+                       }
+               }
+
+               public void AddUtf16 (ReadOnlySpan<char> text) => AddUtf16 (text, 0, -1);
+
+               public unsafe void AddUtf16 (ReadOnlySpan<char> text, int itemOffset, int itemLength)
+               {
+                       fixed (char* chars = text) {
+                               AddUtf16 ((IntPtr)chars, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddUtf16 (IntPtr text, int textLength) =>
+                       AddUtf16 (text, textLength, 0, -1);
+
+               public void AddUtf16 (IntPtr text, int textLength, int itemOffset, int itemLength)
+               {
+                       if (itemOffset < 0)
+                               throw new ArgumentOutOfRangeException (nameof (itemOffset), "ItemOffset must be non negative.");
+                       if (Length != 0 && ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("Non empty buffer's ContentType must be of type Unicode.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be of type Glyphs");
+
+                       HarfBuzzApi.hb_buffer_add_utf16 (Handle, text, textLength, itemOffset, itemLength);
+               }
+
+               public void AddUtf32 (string text) => AddUtf32 (Encoding.UTF32.GetBytes (text));
+
+               public unsafe void AddUtf32 (ReadOnlySpan<byte> text)
+               {
+                       fixed (byte* bytes = text) {
+                               AddUtf32 ((IntPtr)bytes, text.Length / 4);
+                       }
+               }
+
+               public void AddUtf32 (ReadOnlySpan<uint> text) => AddUtf32 (text, 0, -1);
+
+               public unsafe void AddUtf32 (ReadOnlySpan<uint> text, int itemOffset, int itemLength)
+               {
+                       fixed (uint* integers = text) {
+                               AddUtf32 ((IntPtr)integers, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddUtf32 (ReadOnlySpan<int> text) => AddUtf32 (text, 0, -1);
+
+               public unsafe void AddUtf32 (ReadOnlySpan<int> text, int itemOffset, int itemLength)
+               {
+                       fixed (int* integers = text) {
+                               AddUtf32 ((IntPtr)integers, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddUtf32 (IntPtr text, int textLength) =>
+                       AddUtf32 (text, textLength, 0, -1);
+
+               public void AddUtf32 (IntPtr text, int textLength, int itemOffset, int itemLength)
+               {
+                       if (itemOffset < 0)
+                               throw new ArgumentOutOfRangeException (nameof (itemOffset), "ItemOffset must be non negative.");
+                       if (Length != 0 && ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("Non empty buffer's ContentType must be of type Unicode.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be of type Glyphs");
+
+                       HarfBuzzApi.hb_buffer_add_utf32 (Handle, text, textLength, itemOffset, itemLength);
+               }
+
+               public void AddCodepoints (ReadOnlySpan<uint> text) => AddCodepoints (text, 0, -1);
+
+               public unsafe void AddCodepoints (ReadOnlySpan<uint> text, int itemOffset, int itemLength)
+               {
+                       fixed (uint* codepoints = text) {
+                               AddCodepoints ((IntPtr)codepoints, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddCodepoints (ReadOnlySpan<int> text) => AddCodepoints (text, 0, -1);
+
+               public unsafe void AddCodepoints (ReadOnlySpan<int> text, int itemOffset, int itemLength)
+               {
+                       fixed (int* codepoints = text) {
+                               AddCodepoints ((IntPtr)codepoints, text.Length, itemOffset, itemLength);
+                       }
+               }
+
+               public void AddCodepoints (IntPtr text, int textLength) => AddCodepoints (text, textLength, 0, -1);
+
+               public void AddCodepoints (IntPtr text, int textLength, int itemOffset, int itemLength)
+               {
+                       if (itemOffset < 0)
+                               throw new ArgumentOutOfRangeException (nameof (itemOffset), "ItemOffset must be non negative.");
+                       if (Length != 0 && ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("Non empty buffer's ContentType must be of type Unicode.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be of type Glyphs");
+
+                       HarfBuzzApi.hb_buffer_add_codepoints (Handle, text, textLength, itemOffset, itemLength);
+               }
+
+               public unsafe ReadOnlySpan<GlyphInfo> GetGlyphInfoSpan ()
+               {
+                       var infoPtrs = HarfBuzzApi.hb_buffer_get_glyph_infos (Handle, out var length);
+                       return new ReadOnlySpan<GlyphInfo> (infoPtrs, length);
+               }
+
+               public unsafe ReadOnlySpan<GlyphPosition> GetGlyphPositionSpan ()
+               {
+                       var infoPtrs = HarfBuzzApi.hb_buffer_get_glyph_positions (Handle, out var length);
+                       return new ReadOnlySpan<GlyphPosition> (infoPtrs, length);
+               }
+
+               public void GuessSegmentProperties ()
+               {
+                       if (ContentType != ContentType.Unicode)
+                               throw new InvalidOperationException ("ContentType must be of type Unicode.");
+
+                       HarfBuzzApi.hb_buffer_guess_segment_properties (Handle);
+               }
+
+               public void ClearContents () => HarfBuzzApi.hb_buffer_clear_contents (Handle);
+
+               public void Reset () => HarfBuzzApi.hb_buffer_reset (Handle);
+
+               public void Append (Buffer buffer) => Append (buffer, 0, -1);
+
+               public void Append (Buffer buffer, int start, int end)
+               {
+                       if (buffer.Length == 0)
+                               throw new ArgumentException ("Buffer must be non empty.", nameof (buffer));
+                       if (buffer.ContentType != ContentType)
+                               throw new InvalidOperationException ("ContentType must be of same type.");
+
+                       HarfBuzzApi.hb_buffer_append (Handle, buffer.Handle, start, end == -1 ? buffer.Length : end);
+               }
+
+               public void NormalizeGlyphs ()
+               {
+                       if (ContentType != ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must be of type Glyphs.");
+                       if (GlyphPositions.Length == 0)
+                               throw new InvalidOperationException ("GlyphPositions can't be empty.");
+
+                       HarfBuzzApi.hb_buffer_normalize_glyphs (Handle);
+               }
+
+               public void Reverse () => HarfBuzzApi.hb_buffer_reverse (Handle);
+
+               public void ReverseRange (int start, int end) =>
+                       HarfBuzzApi.hb_buffer_reverse_range (Handle, start, end == -1 ? Length : end);
+
+               public void ReverseClusters () => HarfBuzzApi.hb_buffer_reverse_clusters (Handle);
+
+               public string SerializeGlyphs () =>
+                       SerializeGlyphs (0, -1, null, SerializeFormat.Text, SerializeFlag.Default);
+
+               public string SerializeGlyphs (int start, int end) =>
+                       SerializeGlyphs (start, end, null, SerializeFormat.Text, SerializeFlag.Default);
+
+               public string SerializeGlyphs (Font font) =>
+                       SerializeGlyphs (0, -1, font, SerializeFormat.Text, SerializeFlag.Default);
+
+               public string SerializeGlyphs (Font font, SerializeFormat format, SerializeFlag flags) =>
+                       SerializeGlyphs (0, -1, font, format, flags);
+
+               public unsafe string SerializeGlyphs (int start, int end, Font font, SerializeFormat format, SerializeFlag flags)
+               {
+                       if (Length == 0)
+                               throw new InvalidOperationException ("Buffer should not be empty.");
+                       if (ContentType != ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType should be of type Glyphs.");
+
+                       if (end == -1)
+                               end = Length;
+
+                       using (var buffer = MemoryPool<byte>.Shared.Rent ())
+                       using (var pinned = buffer.Memory.Pin ()) {
+                               var bufferSize = buffer.Memory.Length;
+                               var currentPosition = start;
+                               var builder = new StringBuilder (bufferSize);
+
+                               while (currentPosition < end) {
+                                       currentPosition += HarfBuzzApi.hb_buffer_serialize_glyphs (
+                                               Handle,
+                                               currentPosition,
+                                               end,
+                                               (IntPtr)pinned.Pointer,
+                                               bufferSize,
+                                               out var consumed,
+                                               font?.Handle ?? IntPtr.Zero,
+                                               format,
+                                               flags);
+
+                                       builder.Append (Marshal.PtrToStringAnsi ((IntPtr)pinned.Pointer, consumed));
+                               }
+
+                               return builder.ToString ();
+                       }
+               }
+
+               public void DeserializeGlyphs (string data) =>
+                       DeserializeGlyphs (data, null, SerializeFormat.Text);
+
+               public void DeserializeGlyphs (string data, Font font) =>
+                       DeserializeGlyphs (data, font, SerializeFormat.Text);
+
+               public void DeserializeGlyphs (string data, Font font, SerializeFormat format)
+               {
+                       if (Length != 0)
+                               throw new InvalidOperationException ("Buffer must be empty.");
+                       if (ContentType == ContentType.Glyphs)
+                               throw new InvalidOperationException ("ContentType must not be Glyphs.");
+
+                       HarfBuzzApi.hb_buffer_deserialize_glyphs (Handle, data, -1, out _, font?.Handle ?? IntPtr.Zero, format);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_buffer_destroy (Handle);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Definitions.cs b/src/XSF/HarfBuzzSharp/Definitions.cs
new file mode 100644 (file)
index 0000000..7937455
--- /dev/null
@@ -0,0 +1,442 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using hb_codepoint_t = System.UInt32;
+using hb_mask_t = System.UInt32;
+using hb_position_t = System.Int32;
+using hb_var_int_t = System.Int32;
+
+namespace HarfBuzzSharp
+{
+       public enum MemoryMode
+       {
+               Duplicate,
+               ReadOnly,
+               Writeable,
+               ReadOnlyMayMakeWriteable
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       public struct Feature
+       {
+               private const int MaxFeatureStringSize = 128;
+
+               private Tag tag;
+               private uint val;
+               private uint start;
+               private uint end;
+
+               public Feature (Tag tag)
+                       : this (tag, 1u, 0, uint.MaxValue)
+               {
+               }
+
+               public Feature (Tag tag, uint value)
+                       : this (tag, value, 0, uint.MaxValue)
+               {
+               }
+
+               public Feature (Tag tag, uint value, uint start, uint end)
+               {
+                       this.tag = tag;
+                       this.val = value;
+                       this.start = start;
+                       this.end = end;
+               }
+
+               public Tag Tag {
+                       get => tag;
+                       set => tag = value;
+               }
+
+               public uint Value {
+                       get => val;
+                       set => val = value;
+               }
+
+               public uint Start {
+                       get => start;
+                       set => start = value;
+               }
+
+               public uint End {
+                       get => end;
+                       set => end = value;
+               }
+
+               public override string ToString ()
+               {
+                       var buffer = new StringBuilder (MaxFeatureStringSize);
+                       HarfBuzzApi.hb_feature_to_string (ref this, buffer, MaxFeatureStringSize);
+                       return buffer.ToString ();
+               }
+
+               public static bool TryParse (string s, out Feature feature) =>
+                       HarfBuzzApi.hb_feature_from_string (s, -1, out feature);
+
+               public static Feature Parse (string s) =>
+                       TryParse (s, out var feature) ? feature : throw new FormatException ("Unrecognized feature string format.");
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       public struct GlyphInfo
+       {
+               private hb_codepoint_t codepoint;
+               private hb_mask_t mask;
+               private uint cluster;
+
+               // < private >
+               private hb_var_int_t var1;
+               private hb_var_int_t var2;
+
+               public hb_codepoint_t Codepoint {
+                       get => codepoint;
+                       set => codepoint = value;
+               }
+
+               public hb_mask_t Mask {
+                       get => mask;
+                       set => mask = value;
+               }
+
+               public uint Cluster {
+                       get => cluster;
+                       set => cluster = value;
+               }
+
+               public GlyphFlags GlyphFlags =>
+                       HarfBuzzApi.hb_glyph_info_get_glyph_flags (ref this);
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       public struct GlyphPosition
+       {
+               private hb_position_t x_advance;
+               private hb_position_t y_advance;
+               private hb_position_t x_offset;
+               private hb_position_t y_offset;
+
+               // < private >
+               private hb_var_int_t var;
+
+               public hb_position_t XAdvance {
+                       get => x_advance;
+                       set => x_advance = value;
+               }
+
+               public hb_position_t YAdvance {
+                       get => y_advance;
+                       set => y_advance = value;
+               }
+
+               public hb_position_t XOffset {
+                       get => x_offset;
+                       set => x_offset = value;
+               }
+
+               public hb_position_t YOffset {
+                       get => y_offset;
+                       set => y_offset = value;
+               }
+       }
+
+       public enum Direction
+       {
+               Invalid = 0,
+
+               LeftToRight = 4,
+               RightToLeft,
+               TopToBottom,
+               BottomToTop
+       }
+
+       public enum ClusterLevel
+       {
+               MonotoneGraphemes = 0,
+               MonotoneCharacters = 1,
+               Characters = 2,
+               Default = MonotoneGraphemes
+       }
+
+       [Flags]
+       public enum BufferFlags : uint
+       {
+               Default = 0x00000000u,
+               BeginningOfText = 0x00000001u,
+               EndOfText = 0x00000002u,
+               PreserveDefaultIgnorables = 0x00000004u,
+               RemoveDefaultIgnorables = 0x00000008u,
+               DoNotInsertDottedCircle = 0x00000010u,
+       }
+
+       public enum ContentType
+       {
+               Invalid = 0,
+               Unicode,
+               Glyphs
+       }
+
+       [Flags]
+       public enum GlyphFlags
+       {
+               UnsafeToBreak = 0x00000001,
+               Defined = 0x00000001
+       }
+
+       [Flags]
+       public enum SerializeFlag : uint
+       {
+               Default = 0x00000000u,
+               NoClusters = 0x00000001u,
+               NoPositions = 0x00000002u,
+               NoGlyphNames = 0x00000004u,
+               GlyphExtents = 0x00000008u,
+               GlyphFlags = 0x00000010u,
+               NoAdvances = 0x00000020u
+       }
+
+       public enum SerializeFormat : uint
+       {
+               Text = (((byte)'T' << 24) | ((byte)'E' << 16) | ((byte)'X' << 8) | (byte)'T'),
+               Json = (((byte)'J' << 24) | ((byte)'S' << 16) | ((byte)'O' << 8) | (byte)'N'),
+               Invalid = 0
+       }
+
+       public struct GlyphExtents
+       {
+               hb_position_t x_bearing; /* left side of glyph from origin. */
+               hb_position_t y_bearing; /* top side of glyph from origin. */
+               hb_position_t width; /* distance from left to right side. */
+               hb_position_t height; /* distance from top to bottom side. */
+
+               public hb_position_t XBearing {
+                       get => x_bearing;
+                       set => x_bearing = value;
+               }
+               public hb_position_t YBearing {
+                       get => y_bearing;
+                       set => y_bearing = value;
+               }
+               public hb_position_t Width {
+                       get => width;
+                       set => width = value;
+               }
+               public hb_position_t Height {
+                       get => height;
+                       set => height = value;
+               }
+       }
+
+       public struct FontExtents
+       {
+               private hb_position_t ascender; // typographic ascender.
+               private hb_position_t descender; // typographic descender.
+               private hb_position_t line_gap; // suggested line spacing gap.
+
+               // < private >
+               private hb_position_t reserved9;
+               private hb_position_t reserved8;
+               private hb_position_t reserved7;
+               private hb_position_t reserved6;
+               private hb_position_t reserved5;
+               private hb_position_t reserved4;
+               private hb_position_t reserved3;
+               private hb_position_t reserved2;
+               private hb_position_t reserved1;
+
+               public hb_position_t Ascender {
+                       get => ascender;
+                       set => ascender = value;
+               }
+
+               public hb_position_t Descender {
+                       get => descender;
+                       set => descender = value;
+               }
+
+               public hb_position_t LineGap {
+                       get => line_gap;
+                       set => line_gap = value;
+               }
+       }
+
+       public enum UnicodeCombiningClass
+       {
+               NotReordered = 0,
+               Overlay = 1,
+               Nukta = 7,
+               KanaVoicing = 8,
+               Virama = 9,
+
+               // Hebrew
+               CCC10 = 10,
+               CCC11 = 11,
+               CCC12 = 12,
+               CCC13 = 13,
+               CCC14 = 14,
+               CCC15 = 15,
+               CCC16 = 16,
+               CCC17 = 17,
+               CCC18 = 18,
+               CCC19 = 19,
+               CCC20 = 20,
+               CCC21 = 21,
+               CCC22 = 22,
+               CCC23 = 23,
+               CCC24 = 24,
+               CCC25 = 25,
+               CCC26 = 26,
+
+               // Arabic
+               CCC27 = 27,
+               CCC28 = 28,
+               CCC29 = 29,
+               CCC30 = 30,
+               CCC31 = 31,
+               CCC32 = 32,
+               CCC33 = 33,
+               CCC34 = 34,
+               CCC35 = 35,
+
+               // Syriac
+               CCC36 = 36,
+
+               // Telugu
+               CCC84 = 84,
+               CCC91 = 91,
+
+               // Thai
+               CCC103 = 103,
+               CCC107 = 107,
+
+               // Lao
+               CCC118 = 118,
+               CCC122 = 122,
+
+               // Tibetan
+               CCC129 = 129,
+               CCC130 = 130,
+               CCC133 = 132,
+
+               AttachedBelowLeft = 200,
+               AttachedBelow = 202,
+               AttachedAbove = 214,
+               AttachedAboveRight = 216,
+               BelowLeft = 218,
+               Below = 220,
+               BelowRight = 222,
+               Left = 224,
+               Right = 226,
+               AboveLeft = 228,
+               Above = 230,
+               AboveRight = 232,
+               DoubleBelow = 233,
+               DoubleAbove = 234,
+               IotaSubscript = 240,
+
+               Invalid = 255
+       }
+
+       public enum UnicodeGeneralCategory
+       {
+               Control,              // Cc
+               Format,               // Cf
+               Unassigned,           // Cn
+               PrivateUse,           // Co
+               Surrogate,            // Cs
+
+               LowercaseLetter,      // Ll
+               ModifierLetter,       // Lm
+               OtherLetter,          // Lo
+               TitlecaseLetter,      // Lt
+               UppercaseLetter,      // Lu
+
+               SpacingMark,          // Mc
+               EnclosingMark,        // Me
+               NonSpacingMark,       // Mn
+
+               DecimalNumber,        // Nd
+               LetterNumber,         // Nl
+               OtherNumber,          // No
+
+               ConnectPunctuation,   // Pc
+               DashPunctuation,      // Pd
+               ClosePunctuation,     // Pe
+               FinalPunctuation,     // Pf
+               InitialPunctuation,   // Pi
+               OtherPunctuation,     // Po
+               OpenPunctuation,      // Ps
+
+               CurrencySymbol,       // Sc
+               ModifierSymbol,       // Sk
+               MathSymbol,           // Sm
+               OtherSymbol,          // So
+
+               LineSeparator,        // Zl
+               ParagraphSeparator,   // Zp
+               SpaceSeparator        // Zs
+       }
+
+       public enum OpenTypeMetricsTag : uint
+       {
+               HorizontalAscender = (((byte)'h' << 24) | ((byte)'a' << 16) | ((byte)'s' << 8) | (byte)'c'),
+               HorizontalDescender = (((byte)'h' << 24) | ((byte)'d' << 16) | ((byte)'s' << 8) | (byte)'c'),
+               HorizontalLineGap = (((byte)'h' << 24) | ((byte)'l' << 16) | ((byte)'g' << 8) | (byte)'p'),
+               HorizontalClippingAscent = (((byte)'h' << 24) | ((byte)'c' << 16) | ((byte)'l' << 8) | (byte)'a'),
+               HorizontalClippingDescent = (((byte)'h' << 24) | ((byte)'c' << 16) | ((byte)'l' << 8) | (byte)'d'),
+
+               VerticalAscender = (((byte)'v' << 24) | ((byte)'a' << 16) | ((byte)'s' << 8) | (byte)'c'),
+               VerticalDescender = (((byte)'v' << 24) | ((byte)'d' << 16) | ((byte)'s' << 8) | (byte)'c'),
+               VerticalLineGap = (((byte)'v' << 24) | ((byte)'l' << 16) | ((byte)'g' << 8) | (byte)'p'),
+
+               HorizontalCaretRise = (((byte)'h' << 24) | ((byte)'c' << 16) | ((byte)'r' << 8) | (byte)'s'),
+               HorizontalCaretRun = (((byte)'h' << 24) | ((byte)'c' << 16) | ((byte)'r' << 8) | (byte)'n'),
+               HorizontalCaretOffset = (((byte)'h' << 24) | ((byte)'c' << 16) | ((byte)'o' << 8) | (byte)'f'),
+
+               VerticalCaretRise = (((byte)'v' << 24) | ((byte)'c' << 16) | ((byte)'r' << 8) | (byte)'s'),
+               VerticalCaretRun = (((byte)'v' << 24) | ((byte)'c' << 16) | ((byte)'r' << 8) | (byte)'n'),
+               VerticalCaretOffset = (((byte)'v' << 24) | ((byte)'c' << 16) | ((byte)'o' << 8) | (byte)'f'),
+
+               XHeight = (((byte)'x' << 24) | ((byte)'h' << 16) | ((byte)'g' << 8) | (byte)'t'),
+
+               CapHeight = (((byte)'c' << 24) | ((byte)'p' << 16) | ((byte)'h' << 8) | (byte)'t'),
+
+               SubScriptEmXSize = (((byte)'s' << 24) | ((byte)'b' << 16) | ((byte)'x' << 8) | (byte)'s'),
+               SubScriptEmYSize = (((byte)'s' << 24) | ((byte)'b' << 16) | ((byte)'y' << 8) | (byte)'s'),
+               SubScriptEmXOffset = (((byte)'s' << 24) | ((byte)'b' << 16) | ((byte)'x' << 8) | (byte)'o'),
+               SubScriptEmYOffset = (((byte)'s' << 24) | ((byte)'b' << 16) | ((byte)'y' << 8) | (byte)'o'),
+
+               SuperScriptEmXSize = (((byte)'s' << 24) | ((byte)'p' << 16) | ((byte)'x' << 8) | (byte)'s'),
+               SuperScriptEmYSize = (((byte)'s' << 24) | ((byte)'p' << 16) | ((byte)'y' << 8) | (byte)'s'),
+               SuperScriptEmXOffset = (((byte)'s' << 24) | ((byte)'p' << 16) | ((byte)'x' << 8) | (byte)'o'),
+               SuperScriptEmYOffset = (((byte)'s' << 24) | ((byte)'p' << 16) | ((byte)'y' << 8) | (byte)'o'),
+
+               StrikeoutSize = (((byte)'s' << 24) | ((byte)'t' << 16) | ((byte)'r' << 8) | (byte)'s'),
+               StrikeoutOffset = (((byte)'s' << 24) | ((byte)'t' << 16) | ((byte)'r' << 8) | (byte)'o'),
+
+               UnderlineSize = (((byte)'u' << 24) | ((byte)'n' << 16) | ((byte)'d' << 8) | (byte)'s'),
+               UnderlineOffset = (((byte)'u' << 24) | ((byte)'n' << 16) | ((byte)'d' << 8) | (byte)'o'),
+       }
+
+       public readonly struct OpenTypeMetrics
+       {
+               private readonly IntPtr font;
+
+               public OpenTypeMetrics (IntPtr font)
+               {
+                       this.font = font;
+               }
+
+               public bool TryGetPosition (OpenTypeMetricsTag metricsTag, out int position) =>
+                       HarfBuzzApi.hb_ot_metrics_get_position (font, metricsTag, out position);
+
+               public float GetVariation (OpenTypeMetricsTag metricsTag) =>
+                       HarfBuzzApi.hb_ot_metrics_get_variation (font, metricsTag);
+
+               public int GetXVariation (OpenTypeMetricsTag metricsTag) =>
+                       HarfBuzzApi.hb_ot_metrics_get_x_variation (font, metricsTag);
+
+               public int GetYVariation (OpenTypeMetricsTag metricsTag) =>
+                       HarfBuzzApi.hb_ot_metrics_get_y_variation (font, metricsTag);
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/DelegateProxies.cs b/src/XSF/HarfBuzzSharp/DelegateProxies.cs
new file mode 100644 (file)
index 0000000..6ba319c
--- /dev/null
@@ -0,0 +1,65 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       // public delegates
+
+       public delegate void ReleaseDelegate ();
+
+       public delegate Blob GetTableDelegate (Face face, Tag tag);
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use ReleaseDelegate instead.")]
+       public delegate void BlobReleaseDelegate (object context);
+
+       // internal proxy delegates
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate void ReleaseDelegateProxyDelegate (IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate IntPtr GetTableDelegateProxyDelegate (IntPtr face, Tag tag, IntPtr context);
+
+       internal static partial class DelegateProxies
+       {
+               // references to the proxy implementations
+               public static readonly ReleaseDelegateProxyDelegate ReleaseDelegateProxy = ReleaseDelegateProxyImplementation;
+               public static readonly ReleaseDelegateProxyDelegate ReleaseDelegateProxyForMulti = ReleaseDelegateProxyImplementationForMulti;
+               public static readonly GetTableDelegateProxyDelegate GetTableDelegateProxy = GetTableDelegateProxyImplementation;
+
+               // internal proxy implementations
+
+               [MonoPInvokeCallback (typeof (ReleaseDelegateProxyDelegate))]
+               private static void ReleaseDelegateProxyImplementation (IntPtr context)
+               {
+                       var del = Get<ReleaseDelegate> (context, out var gch);
+                       try {
+                               del.Invoke ();
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (GetTableDelegateProxyDelegate))]
+               private static IntPtr GetTableDelegateProxyImplementation (IntPtr face, Tag tag, IntPtr context)
+               {
+                       GetMultiUserData<GetTableDelegate, Face> (context, out var getTable, out var userData, out _);
+                       var blob = getTable.Invoke (userData, tag);
+                       return blob?.Handle ?? IntPtr.Zero;
+               }
+
+               [MonoPInvokeCallback (typeof (ReleaseDelegateProxyDelegate))]
+               private static void ReleaseDelegateProxyImplementationForMulti (IntPtr context)
+               {
+                       var del = GetMulti<ReleaseDelegate> (context, out var gch);
+                       try {
+                               del?.Invoke ();
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/DelegateProxies.font.cs b/src/XSF/HarfBuzzSharp/DelegateProxies.font.cs
new file mode 100644 (file)
index 0000000..c0ff2e0
--- /dev/null
@@ -0,0 +1,207 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       // public delegates
+
+       public delegate bool FontExtentsDelegate (Font font, object fontData, out FontExtents extents);
+
+       public delegate bool NominalGlyphDelegate (Font font, object fontData, uint unicode, out uint glyph);
+
+       public delegate uint NominalGlyphsDelegate (Font font, object fontData, uint count, ReadOnlySpan<uint> codepoints, Span<uint> glyphs);
+
+       public delegate bool VariationGlyphDelegate (Font font, object fontData, uint unicode, uint variationSelector, out uint glyph);
+
+       public delegate int GlyphAdvanceDelegate (Font font, object fontData, uint glyph);
+
+       public delegate void GlyphAdvancesDelegate (Font font, object fontData, uint count, ReadOnlySpan<uint> glyphs, Span<int> advances);
+
+       public delegate bool GlyphOriginDelegate (Font font, object fontData, uint glyph, out int x, out int y);
+
+       public delegate int GlyphKerningDelegate (Font font, object fontData, uint firstGlyph, uint secondGlyph);
+
+       public delegate bool GlyphExtentsDelegate (Font font, object fontData, uint glyph, out GlyphExtents extents);
+
+       public delegate bool GlyphContourPointDelegate (Font font, object fontData, uint glyph, uint pointIndex, out int x, out int y);
+
+       public delegate bool GlyphNameDelegate (Font font, object fontData, uint glyph, out string name);
+
+       public delegate bool GlyphFromNameDelegate (Font font, object fontData, string name, out uint glyph);
+
+       // internal proxy delegates
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool FontExtentsProxyDelegate (IntPtr font, IntPtr fontData, out FontExtents extents, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool NominalGlyphProxyDelegate (IntPtr font, IntPtr fontData, uint unicode, out uint glyph, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate uint NominalGlyphsProxyDelegate (IntPtr font, IntPtr fontData, uint count, uint* firstUnicode, uint unicodeStride, uint* firstGlyph, uint glyphStride, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool VariationGlyphProxyDelegate (IntPtr font, IntPtr fontData, uint unicode, uint variationSelector, out uint glyph, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate int GlyphAdvanceProxyDelegate (IntPtr font, IntPtr fontData, uint glyph, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void GlyphAdvancesProxyDelegate (IntPtr font, IntPtr fontData, uint count, uint* firstGlyph, uint glyphStride, int* firstAdvance, uint advanceStride, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool GlyphOriginProxyDelegate (IntPtr font, IntPtr fontData, uint glyph, out int x, out int y, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate int GlyphKerningProxyDelegate (IntPtr font, IntPtr fontData, uint firstGlyph, uint secondGlyph, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool GlyphExtentsProxyDelegate (IntPtr font, IntPtr fontData, uint glyph, out GlyphExtents extents, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool GlyphContourPointProxyDelegate (IntPtr font, IntPtr fontData, uint glyph, uint pointIndex, out int x, out int y, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool GlyphNameProxyDelegate (IntPtr font, IntPtr fontData, uint glyph, char* name, int size, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool GlyphFromNameProxyDelegate (IntPtr font, IntPtr fontData, char* name, int len, out uint glyph, IntPtr context);
+
+       internal static unsafe partial class DelegateProxies
+       {
+               // references to the proxy implementations
+               public static readonly FontExtentsProxyDelegate FontExtentsProxy = FontExtentsProxyImplementation;
+               public static readonly NominalGlyphProxyDelegate NominalGlyphProxy = NominalGlyphProxyImplementation;
+               public static readonly VariationGlyphProxyDelegate VariationGlyphProxy = VariationGlyphProxyImplementation;
+               public static readonly NominalGlyphsProxyDelegate NominalGlyphsProxy = NominalGlyphsProxyImplementation;
+               public static readonly GlyphAdvanceProxyDelegate GlyphAdvanceProxy = GlyphAdvanceProxyImplementation;
+               public static readonly GlyphAdvancesProxyDelegate GlyphAdvancesProxy = GlyphAdvancesProxyImplementation;
+               public static readonly GlyphOriginProxyDelegate GlyphOriginProxy = GlyphOriginProxyImplementation;
+               public static readonly GlyphKerningProxyDelegate GlyphKerningProxy = GlyphKerningProxyImplementation;
+               public static readonly GlyphExtentsProxyDelegate GlyphExtentsProxy = GlyphExtentsProxyImplementation;
+               public static readonly GlyphContourPointProxyDelegate GlyphContourPointProxy = GlyphContourPointProxyImplementation;
+               public static readonly GlyphNameProxyDelegate GlyphNameProxy = GlyphNameProxyImplementation;
+               public static readonly GlyphFromNameProxyDelegate GlyphFromNameProxy = GlyphFromNameProxyImplementation;
+
+               // internal proxy implementations
+
+               [MonoPInvokeCallback (typeof (FontExtentsProxyDelegate))]
+               private static bool FontExtentsProxyImplementation (IntPtr font, IntPtr fontData, out FontExtents extents, IntPtr context)
+               {
+                       var del = GetMulti<FontExtentsDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, out extents);
+               }
+
+               [MonoPInvokeCallback (typeof (NominalGlyphProxyDelegate))]
+               private static bool NominalGlyphProxyImplementation (IntPtr font, IntPtr fontData, uint unicode, out uint glyph, IntPtr context)
+               {
+                       var del = GetMulti<NominalGlyphDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, unicode, out glyph);
+               }
+
+               [MonoPInvokeCallback (typeof (NominalGlyphsProxyDelegate))]
+               private static uint NominalGlyphsProxyImplementation (IntPtr font, IntPtr fontData, uint count, uint* firstUnicode, uint unicodeStride, uint* firstGlyph, uint glyphStride, IntPtr context)
+               {
+                       var del = GetMulti<NominalGlyphsDelegate> (context, out _);
+                       var unicodes = new ReadOnlySpan<uint> (firstUnicode, (int)count);
+                       var glyphs = new Span<uint> (firstGlyph, (int)count);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, count, unicodes, glyphs);
+               }
+
+               [MonoPInvokeCallback (typeof (VariationGlyphProxyDelegate))]
+               private static bool VariationGlyphProxyImplementation (IntPtr font, IntPtr fontData, uint unicode, uint variationSelector, out uint glyph, IntPtr context)
+               {
+                       var del = GetMulti<VariationGlyphDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, unicode, variationSelector, out glyph);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphAdvanceProxyDelegate))]
+               private static int GlyphAdvanceProxyImplementation (IntPtr font, IntPtr fontData, uint glyph, IntPtr context)
+               {
+                       var del = GetMulti<GlyphAdvanceDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, glyph);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphAdvancesProxyDelegate))]
+               private static void GlyphAdvancesProxyImplementation (IntPtr font, IntPtr fontData, uint count, uint* firstGlyph, uint glyphStride, int* firstAdvance, uint advanceStride, IntPtr context)
+               {
+                       var del = GetMulti<GlyphAdvancesDelegate> (context, out _);
+                       var glyphs = new ReadOnlySpan<uint> (firstGlyph, (int)count);
+                       var advances = new Span<int> (firstAdvance, (int)count);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       del.Invoke (userData.Font, userData.FontData, count, glyphs, advances);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphOriginProxyDelegate))]
+               private static bool GlyphOriginProxyImplementation (IntPtr font, IntPtr fontData, uint glyph, out int x, out int y, IntPtr context)
+               {
+                       var del = GetMulti<GlyphOriginDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, glyph, out x, out y);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphKerningProxyDelegate))]
+               private static int GlyphKerningProxyImplementation (IntPtr font, IntPtr fontData, uint firstGlyph, uint secondGlyph, IntPtr context)
+               {
+                       var del = GetMulti<GlyphKerningDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, firstGlyph, secondGlyph);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphExtentsProxyDelegate))]
+               private static bool GlyphExtentsProxyImplementation (IntPtr font, IntPtr fontData, uint glyph, out GlyphExtents extents, IntPtr context)
+               {
+                       var del = GetMulti<GlyphExtentsDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, glyph, out extents);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphContourPointProxyDelegate))]
+               private static bool GlyphContourPointProxyImplementation (IntPtr font, IntPtr fontData, uint glyph, uint pointIndex, out int x, out int y, IntPtr context)
+               {
+                       var del = GetMulti<GlyphContourPointDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       return del.Invoke (userData.Font, userData.FontData, glyph, pointIndex, out x, out y);
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphNameProxyDelegate))]
+               private static bool GlyphNameProxyImplementation (IntPtr font, IntPtr fontData, uint glyph, char* nameBuffer, int size, IntPtr context)
+               {
+                       var del = GetMulti<GlyphNameDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+                       var result = del.Invoke (userData.Font, userData.FontData, glyph, out var realName);
+
+                       var nameSpan = realName.AsSpan ();
+                       var bufferSpan = new Span<char> (nameBuffer, size);
+                       nameSpan.CopyTo (bufferSpan);
+
+                       return result;
+               }
+
+               [MonoPInvokeCallback (typeof (GlyphFromNameProxyDelegate))]
+               private static bool GlyphFromNameProxyImplementation (IntPtr font, IntPtr fontData, char* name, int len, out uint glyph, IntPtr context)
+               {
+                       var del = GetMulti<GlyphFromNameDelegate> (context, out _);
+                       var userData = GetMultiUserData<FontUserData> (fontData, out _);
+
+                       var actualName = len < 0
+                               ? new string (name)
+                               : new string (name, 0, len);
+
+                       return del.Invoke (userData.Font, userData.FontData, actualName, out glyph);
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/DelegateProxies.shared.cs b/src/XSF/HarfBuzzSharp/DelegateProxies.shared.cs
new file mode 100644 (file)
index 0000000..40ab67d
--- /dev/null
@@ -0,0 +1,329 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+#if THROW_OBJECT_EXCEPTIONS
+       using GCHandle = GCHandleProxy;
+#endif
+
+       // helper delegates
+
+       internal delegate Delegate GetMultiDelegateDelegate (Type index);
+
+       internal delegate object UserDataDelegate ();
+
+       internal static partial class DelegateProxies
+       {
+               // normal delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T Create<T> (object managedDel, T nativeDel, out GCHandle gch, out IntPtr contextPtr)
+               {
+                       if (managedDel == null) {
+                               gch = default (GCHandle);
+                               contextPtr = IntPtr.Zero;
+                               return default (T);
+                       }
+
+                       gch = GCHandle.Alloc (managedDel);
+                       contextPtr = GCHandle.ToIntPtr (gch);
+                       return nativeDel;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void Create (object managedDel, out GCHandle gch, out IntPtr contextPtr)
+               {
+                       if (managedDel == null) {
+                               gch = default (GCHandle);
+                               contextPtr = IntPtr.Zero;
+                               return;
+                       }
+
+                       gch = GCHandle.Alloc (managedDel);
+                       contextPtr = GCHandle.ToIntPtr (gch);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T Get<T> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       if (contextPtr == IntPtr.Zero) {
+                               gch = default (GCHandle);
+                               return default (T);
+                       }
+
+                       gch = GCHandle.FromIntPtr (contextPtr);
+                       return (T)gch.Target;
+               }
+
+               // user data delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateUserData (object userData, bool makeWeak = false)
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var del = new UserDataDelegate (() => userData);
+                       Create (del, out _, out var ctx);
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T GetUserData<T> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       var del = Get<UserDataDelegate> (contextPtr, out gch);
+                       var value = del.Invoke ();
+                       return value is WeakReference weak ? (T)weak.Target : (T)value;
+               }
+
+               // multi-value delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMulti<T1, T2> (T1 wrappedDelegate1, T2 wrappedDelegate2)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMulti<T1, T2, T3> (T1 wrappedDelegate1, T2 wrappedDelegate2, T3 wrappedDelegate3)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (T3))
+                                       return wrappedDelegate3;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T GetMulti<T> (IntPtr contextPtr, out GCHandle gch)
+                       where T : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       return (T)multi.Invoke (typeof (T));
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMulti<T1, T2> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMulti<T1, T2, T3> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out T3 wrappedDelegate3, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       wrappedDelegate3 = (T3)multi.Invoke (typeof (T3));
+               }
+
+               // multi-value delegate with user data
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T> (T wrappedDelegate, object userData, bool makeWeak = false)
+                       where T : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T))
+                                       return wrappedDelegate;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T1, T2> (T1 wrappedDelegate1, T2 wrappedDelegate2, object userData, bool makeWeak = false)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T1, T2, T3> (T1 wrappedDelegate1, T2 wrappedDelegate2, T3 wrappedDelegate3, object userData, bool makeWeak = false)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (T3))
+                                       return wrappedDelegate3;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static TUserData GetMultiUserData<TUserData> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       return GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T, TUserData> (IntPtr contextPtr, out T wrappedDelegate, out TUserData userData, out GCHandle gch)
+                       where T : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate = (T)multi.Invoke (typeof (T));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T1, T2, TUserData> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out TUserData userData, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T1, T2, T3, TUserData> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out T3 wrappedDelegate3, out TUserData userData, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       wrappedDelegate3 = (T3)multi.Invoke (typeof (T3));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static TUserData GetUserData<TUserData> (GetMultiDelegateDelegate multi)
+               {
+                       var userDataDelegate = (UserDataDelegate)multi.Invoke (typeof (UserDataDelegate));
+                       var value = userDataDelegate.Invoke ();
+                       return value is WeakReference weak ? (TUserData)weak.Target : (TUserData)value;
+               }
+       }
+
+#if THROW_OBJECT_EXCEPTIONS
+       // an internal, debug-only proxy that we can use to make sure we are not
+       // leaking GC handles by accident
+       internal struct GCHandleProxy
+       {
+               internal static readonly ConcurrentDictionary<IntPtr, WeakReference> allocatedHandles = new ConcurrentDictionary<IntPtr, WeakReference> ();
+
+               private System.Runtime.InteropServices.GCHandle gch;
+
+               public GCHandleProxy (System.Runtime.InteropServices.GCHandle gcHandle)
+               {
+                       gch = gcHandle;
+               }
+
+               public bool IsAllocated => gch.IsAllocated;
+
+               public object Target => gch.Target;
+
+               public void Free ()
+               {
+                       if (!allocatedHandles.TryRemove (ToIntPtr (this), out _))
+                               throw new InvalidOperationException ($"Allocated GC handle has already been freed.");
+
+                       gch.Free ();
+               }
+
+               internal static GCHandleProxy Alloc (object value)
+               {
+                       var gch = new GCHandleProxy (System.Runtime.InteropServices.GCHandle.Alloc (value));
+
+                       var weak = new WeakReference (value);
+                       var oldWeak = allocatedHandles.GetOrAdd (ToIntPtr (gch), weak);
+                       if (weak != oldWeak)
+                               throw new InvalidOperationException (
+                                       $"GC handle has already been allocated for this memory location. " +
+                                       $"Old: {oldWeak.Target} New: {value}");
+
+                       return gch;
+               }
+
+               internal static GCHandleProxy FromIntPtr (IntPtr value) =>
+                       new GCHandleProxy (System.Runtime.InteropServices.GCHandle.FromIntPtr (value));
+
+               internal static IntPtr ToIntPtr (GCHandleProxy value) =>
+                       System.Runtime.InteropServices.GCHandle.ToIntPtr (value.gch);
+       }
+#endif
+
+       [AttributeUsage (AttributeTargets.Method)]
+       internal sealed class MonoPInvokeCallbackAttribute : Attribute
+       {
+               public MonoPInvokeCallbackAttribute (Type type)
+               {
+                       Type = type;
+               }
+
+               public Type Type { get; private set; }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/DelegateProxies.unicode.cs b/src/XSF/HarfBuzzSharp/DelegateProxies.unicode.cs
new file mode 100644 (file)
index 0000000..22bc72f
--- /dev/null
@@ -0,0 +1,93 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       // public delegates
+
+       public delegate UnicodeCombiningClass CombiningClassDelegate (UnicodeFunctions ufuncs, uint unicode);
+
+       public delegate UnicodeGeneralCategory GeneralCategoryDelegate (UnicodeFunctions ufuncs, uint unicode);
+
+       public delegate uint MirroringDelegate (UnicodeFunctions ufuncs, uint unicode);
+
+       public delegate Script ScriptDelegate (UnicodeFunctions ufuncs, uint unicode);
+
+       public delegate bool ComposeDelegate (UnicodeFunctions ufuncs, uint a, uint b, out uint ab);
+
+       public delegate bool DecomposeDelegate (UnicodeFunctions ufuncs, uint ab, out uint a, out uint b);
+
+       // internal proxy delegates
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate UnicodeCombiningClass hb_unicode_combining_class_func_t (IntPtr ufuncs, uint unicode, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate UnicodeGeneralCategory hb_unicode_general_category_func_t (IntPtr ufuncs, uint unicode, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate uint hb_unicode_mirroring_func_t (IntPtr ufuncs, uint unicode, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal delegate uint hb_unicode_script_func_t (IntPtr ufuncs, uint unicode, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool hb_unicode_compose_func_t (IntPtr ufuncs, uint a, uint b, out uint ab, IntPtr context);
+
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal delegate bool hb_unicode_decompose_func_t (IntPtr ufuncs, uint ab, out uint a, out uint b, IntPtr context);
+
+       internal static partial class DelegateProxies
+       {
+               public static readonly hb_unicode_combining_class_func_t CombiningClassProxy = CombiningClassProxyImplementation;
+               public static readonly hb_unicode_general_category_func_t GeneralCategoryProxy = GeneralCategoryProxyImplementation;
+               public static readonly hb_unicode_mirroring_func_t MirroringProxy = MirroringProxyImplementation;
+               public static readonly hb_unicode_script_func_t ScriptProxy = ScriptProxyImplementation;
+               public static readonly hb_unicode_compose_func_t ComposeProxy = ComposeProxyImplementation;
+               public static readonly hb_unicode_decompose_func_t DecomposeProxy = DecomposeProxyImplementation;
+
+               [MonoPInvokeCallback (typeof (hb_unicode_combining_class_func_t))]
+               private static UnicodeCombiningClass CombiningClassProxyImplementation (IntPtr ufuncs, uint unicode, IntPtr context)
+               {
+                       GetMultiUserData<CombiningClassDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, unicode);
+               }
+
+               [MonoPInvokeCallback (typeof (hb_unicode_general_category_func_t))]
+               private static UnicodeGeneralCategory GeneralCategoryProxyImplementation (IntPtr ufuncs, uint unicode, IntPtr context)
+               {
+                       GetMultiUserData<GeneralCategoryDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, unicode);
+               }
+
+               [MonoPInvokeCallback (typeof (hb_unicode_mirroring_func_t))]
+               private static uint MirroringProxyImplementation (IntPtr ufuncs, uint unicode, IntPtr context)
+               {
+                       GetMultiUserData<MirroringDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, unicode);
+               }
+
+               [MonoPInvokeCallback (typeof (hb_unicode_script_func_t))]
+               private static uint ScriptProxyImplementation (IntPtr ufuncs, uint unicode, IntPtr context)
+               {
+                       GetMultiUserData<ScriptDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, unicode);
+               }
+
+               [MonoPInvokeCallback (typeof (hb_unicode_compose_func_t))]
+               private static bool ComposeProxyImplementation (IntPtr ufuncs, uint a, uint b, out uint ab, IntPtr context)
+               {
+                       GetMultiUserData<ComposeDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, a, b, out ab);
+               }
+
+               [MonoPInvokeCallback (typeof (hb_unicode_decompose_func_t))]
+               private static bool DecomposeProxyImplementation (IntPtr ufuncs, uint ab, out uint a, out uint b, IntPtr context)
+               {
+                       GetMultiUserData<DecomposeDelegate, UnicodeFunctions> (context, out var del, out var functions, out _);
+                       return del.Invoke (functions, ab, out a, out b);
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Face.cs b/src/XSF/HarfBuzzSharp/Face.cs
new file mode 100644 (file)
index 0000000..119068f
--- /dev/null
@@ -0,0 +1,109 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public class Face : NativeObject
+       {
+               private static readonly Lazy<Face> emptyFace = new Lazy<Face> (() => new StaticFace (HarfBuzzApi.hb_face_get_empty ()));
+
+               public static Face Empty => emptyFace.Value;
+
+               public Face (Blob blob, uint index)
+                       : this (blob, (int)index)
+               {
+               }
+
+               public Face (Blob blob, int index)
+                       : this (IntPtr.Zero)
+               {
+                       if (blob == null) {
+                               throw new ArgumentNullException (nameof (blob));
+                       }
+
+                       if (index < 0) {
+                               throw new ArgumentOutOfRangeException (nameof (index), "Index must be non negative.");
+                       }
+
+                       Handle = HarfBuzzApi.hb_face_create (blob.Handle, index);
+               }
+
+               public Face (GetTableDelegate getTable)
+                       : this (getTable, null)
+               {
+               }
+
+               public Face (GetTableDelegate getTable, ReleaseDelegate destroy)
+                       : this (IntPtr.Zero)
+               {
+                       if (getTable == null)
+                               throw new ArgumentNullException (nameof (getTable));
+
+                       Handle = HarfBuzzApi.hb_face_create_for_tables (
+                               DelegateProxies.GetTableDelegateProxy,
+                               DelegateProxies.CreateMultiUserData (getTable, destroy, this),
+                               DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               internal Face (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               public int Index {
+                       get => HarfBuzzApi.hb_face_get_index (Handle);
+                       set => HarfBuzzApi.hb_face_set_index (Handle, value);
+               }
+
+               public int UnitsPerEm {
+                       get => HarfBuzzApi.hb_face_get_upem (Handle);
+                       set => HarfBuzzApi.hb_face_set_upem (Handle, value);
+               }
+
+               public int GlyphCount {
+                       get => HarfBuzzApi.hb_face_get_glyph_count (Handle);
+                       set => HarfBuzzApi.hb_face_set_glyph_count (Handle, value);
+               }
+
+               public unsafe Tag[] Tables {
+                       get {
+                               var tableCount = 0;
+                               var count = HarfBuzzApi.hb_face_get_table_tags (Handle, 0, ref tableCount, IntPtr.Zero);
+                               var buffer = new Tag[count];
+                               fixed (Tag* ptr = buffer) {
+                                       HarfBuzzApi.hb_face_get_table_tags (Handle, 0, ref count, (IntPtr)ptr);
+                               }
+                               return buffer;
+                       }
+               }
+
+               public Blob ReferenceTable (Tag table) =>
+                       new Blob (HarfBuzzApi.hb_face_reference_table (Handle, table));
+
+               public bool IsImmutable => HarfBuzzApi.hb_face_is_immutable (Handle);
+
+               public void MakeImmutable () => HarfBuzzApi.hb_face_make_immutable (Handle);
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_face_destroy (Handle);
+                       }
+               }
+
+               private class StaticFace : Face
+               {
+                       public StaticFace (IntPtr handle)
+                               : base (handle)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Font.cs b/src/XSF/HarfBuzzSharp/Font.cs
new file mode 100644 (file)
index 0000000..242868d
--- /dev/null
@@ -0,0 +1,280 @@
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       public class Font : NativeObject
+       {
+               internal const int NameBufferLength = 128;
+
+               public Font (Face face)
+                       : base (IntPtr.Zero)
+               {
+                       if (face == null)
+                               throw new ArgumentNullException (nameof (face));
+
+                       Handle = HarfBuzzApi.hb_font_create (face.Handle);
+                       OpenTypeMetrics = new OpenTypeMetrics (Handle);
+               }
+
+               public Font (Font parent)
+                       : base (IntPtr.Zero)
+               {
+                       if (parent == null)
+                               throw new ArgumentNullException (nameof (parent));
+                       if (parent.Handle == IntPtr.Zero)
+                               throw new ArgumentException (nameof (parent.Handle));
+
+                       Parent = parent;
+                       Handle = HarfBuzzApi.hb_font_create_sub_font (parent.Handle);
+                       OpenTypeMetrics = new OpenTypeMetrics (Handle);
+               }
+
+               public Font Parent { get; }
+
+               public OpenTypeMetrics OpenTypeMetrics { get; }
+
+               public string[] SupportedShapers =>
+                       PtrToStringArray (HarfBuzzApi.hb_shape_list_shapers ()).ToArray ();
+
+               public void SetFontFunctions (FontFunctions fontFunctions) =>
+                       SetFontFunctions (fontFunctions, null, null);
+
+               public void SetFontFunctions (FontFunctions fontFunctions, object fontData) =>
+                       SetFontFunctions (fontFunctions, fontData, null);
+
+               public void SetFontFunctions (FontFunctions fontFunctions, object fontData, ReleaseDelegate destroy)
+               {
+                       _ = fontFunctions ?? throw new ArgumentNullException (nameof (fontFunctions));
+
+                       var container = new FontUserData (this, fontData);
+                       var ctx = DelegateProxies.CreateMultiUserData (destroy, container);
+                       HarfBuzzApi.hb_font_set_funcs (Handle, fontFunctions.Handle, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void GetScale (out int xScale, out int yScale) =>
+                       HarfBuzzApi.hb_font_get_scale (Handle, out xScale, out yScale);
+
+               public void SetScale (int xScale, int yScale) =>
+                       HarfBuzzApi.hb_font_set_scale (Handle, xScale, yScale);
+
+               public bool TryGetHorizontalFontExtents (out FontExtents extents) =>
+                       HarfBuzzApi.hb_font_get_h_extents (Handle, out extents);
+
+               public bool TryGetVerticalFontExtents (out FontExtents extents) =>
+                       HarfBuzzApi.hb_font_get_v_extents (Handle, out extents);
+
+               public bool TryGetNominalGlyph (int unicode, out uint glyph) =>
+                       TryGetNominalGlyph ((uint)unicode, out glyph);
+
+               public bool TryGetNominalGlyph (uint unicode, out uint glyph) =>
+                       HarfBuzzApi.hb_font_get_nominal_glyph (Handle, unicode, out glyph);
+
+               public bool TryGetVariationGlyph (int unicode, out uint glyph) =>
+                       TryGetVariationGlyph (unicode, 0, out glyph);
+
+               public bool TryGetVariationGlyph (uint unicode, out uint glyph) =>
+                       HarfBuzzApi.hb_font_get_variation_glyph (Handle, unicode, 0, out glyph);
+
+               public bool TryGetVariationGlyph (int unicode, uint variationSelector, out uint glyph) =>
+                       TryGetVariationGlyph ((uint)unicode, variationSelector, out glyph);
+
+               public bool TryGetVariationGlyph (uint unicode, uint variationSelector, out uint glyph) =>
+                       HarfBuzzApi.hb_font_get_variation_glyph (Handle, unicode, variationSelector, out glyph);
+
+               public int GetHorizontalGlyphAdvance (uint glyph) =>
+                       HarfBuzzApi.hb_font_get_glyph_h_advance (Handle, glyph);
+
+               public int GetVerticalGlyphAdvance (uint glyph) =>
+                       HarfBuzzApi.hb_font_get_glyph_v_advance (Handle, glyph);
+
+               public unsafe int[] GetHorizontalGlyphAdvances (ReadOnlySpan<uint> glyphs)
+               {
+                       fixed (uint* firstGlyph = glyphs) {
+                               return GetHorizontalGlyphAdvances ((IntPtr)firstGlyph, glyphs.Length);
+                       }
+               }
+
+               public unsafe int[] GetHorizontalGlyphAdvances (IntPtr firstGlyph, int count)
+               {
+                       var advances = new int[count];
+
+                       fixed (int* firstAdvance = advances) {
+                               HarfBuzzApi.hb_font_get_glyph_h_advances (Handle, count, firstGlyph, 4, (IntPtr)firstAdvance, 4);
+                       }
+
+                       return advances;
+               }
+
+               public unsafe int[] GetVerticalGlyphAdvances (ReadOnlySpan<uint> glyphs)
+               {
+                       fixed (uint* firstGlyph = glyphs) {
+                               return GetVerticalGlyphAdvances ((IntPtr)firstGlyph, glyphs.Length);
+                       }
+               }
+
+               public unsafe int[] GetVerticalGlyphAdvances (IntPtr firstGlyph, int count)
+               {
+                       var advances = new int[count];
+
+                       fixed (int* firstAdvance = advances) {
+                               HarfBuzzApi.hb_font_get_glyph_v_advances (Handle, count, firstGlyph, 4, (IntPtr)firstAdvance, 4);
+                       }
+
+                       return advances;
+               }
+
+               public bool TryGetHorizontalGlyphOrigin (uint glyph, out int xOrigin, out int yOrigin) =>
+                       HarfBuzzApi.hb_font_get_glyph_h_origin (Handle, glyph, out xOrigin, out yOrigin);
+
+               public bool TryGetVerticalGlyphOrigin (uint glyph, out int xOrigin, out int yOrigin) =>
+                       HarfBuzzApi.hb_font_get_glyph_v_origin (Handle, glyph, out xOrigin, out yOrigin);
+
+               public int GetHorizontalGlyphKerning (uint leftGlyph, uint rightGlyph) =>
+                       HarfBuzzApi.hb_font_get_glyph_h_kerning (Handle, leftGlyph, rightGlyph);
+
+               public bool TryGetGlyphExtents (uint glyph, out GlyphExtents extents) =>
+                       HarfBuzzApi.hb_font_get_glyph_extents (Handle, glyph, out extents);
+
+               public bool TryGetGlyphContourPoint (uint glyph, uint pointIndex, out int x, out int y) =>
+                       HarfBuzzApi.hb_font_get_glyph_contour_point (Handle, glyph, pointIndex, out x, out y);
+
+               public unsafe bool TryGetGlyphName (uint glyph, out string name)
+               {
+                       var buffer = ArrayPool<byte>.Shared.Rent (NameBufferLength);
+                       try {
+                               fixed (byte* first = buffer) {
+                                       if (!HarfBuzzApi.hb_font_get_glyph_name (Handle, glyph, first, buffer.Length)) {
+                                               name = string.Empty;
+                                               return false;
+                                       }
+                                       name = Marshal.PtrToStringAnsi ((IntPtr)first);
+                                       return true;
+                               }
+                       } finally {
+                               ArrayPool<byte>.Shared.Return (buffer);
+                       }
+               }
+
+               public bool TryGetGlyphFromName (string name, out uint glyph) =>
+                       HarfBuzzApi.hb_font_get_glyph_from_name (Handle, name, name.Length, out glyph);
+
+               public bool TryGetGlyph (int unicode, out uint glyph) =>
+                       TryGetGlyph ((uint)unicode, 0, out glyph);
+
+               public bool TryGetGlyph (uint unicode, out uint glyph) =>
+                       TryGetGlyph (unicode, 0, out glyph);
+
+               public bool TryGetGlyph (int unicode, uint variationSelector, out uint glyph) =>
+                       TryGetGlyph ((uint)unicode, variationSelector, out glyph);
+
+               public bool TryGetGlyph (uint unicode, uint variationSelector, out uint glyph) =>
+                       HarfBuzzApi.hb_font_get_glyph (Handle, unicode, variationSelector, out glyph);
+
+               public FontExtents GetFontExtentsForDirection (Direction direction)
+               {
+                       HarfBuzzApi.hb_font_get_extents_for_direction (Handle, direction, out var extents);
+                       return extents;
+               }
+
+               public void GetGlyphAdvanceForDirection (uint glyph, Direction direction, out int x, out int y) =>
+                       HarfBuzzApi.hb_font_get_glyph_advance_for_direction (Handle, glyph, direction, out x, out y);
+
+               public unsafe int[] GetGlyphAdvancesForDirection (ReadOnlySpan<uint> glyphs, Direction direction)
+               {
+                       fixed (uint* firstGlyph = glyphs) {
+                               return GetGlyphAdvancesForDirection ((IntPtr)firstGlyph, glyphs.Length, direction);
+                       }
+               }
+
+               public unsafe int[] GetGlyphAdvancesForDirection (IntPtr firstGlyph, int count, Direction direction)
+               {
+                       var advances = new int[count];
+
+                       fixed (int* firstAdvance = advances) {
+                               HarfBuzzApi.hb_font_get_glyph_advances_for_direction (Handle, direction, count, firstGlyph, 4, (IntPtr)firstAdvance, 4);
+                       }
+
+                       return advances;
+               }
+
+               public bool TryGetGlyphContourPointForOrigin (uint glyph, uint pointIndex, Direction direction, out int x, out int y) =>
+                       HarfBuzzApi.hb_font_get_glyph_contour_point_for_origin (Handle, glyph, pointIndex, direction, out x, out y);
+
+               public unsafe string GlyphToString (uint glyph)
+               {
+                       var buffer = ArrayPool<byte>.Shared.Rent (NameBufferLength);
+                       try {
+                               fixed (byte* first = buffer) {
+                                       HarfBuzzApi.hb_font_glyph_to_string (Handle, glyph, first, buffer.Length);
+                                       return Marshal.PtrToStringAnsi ((IntPtr)first);
+                               }
+                       } finally {
+                               ArrayPool<byte>.Shared.Return (buffer);
+                       }
+               }
+
+               public bool TryGetGlyphFromString (string s, out uint glyph) =>
+                       HarfBuzzApi.hb_font_glyph_from_string (Handle, s, -1, out glyph);
+
+               public void SetFunctionsOpenType () =>
+                       HarfBuzzApi.hb_ot_font_set_funcs (Handle);
+
+               public void Shape (Buffer buffer, params Feature[] features) =>
+                       Shape (buffer, features, null);
+
+               public void Shape (Buffer buffer, IReadOnlyList<Feature> features, IReadOnlyList<string> shapers)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       if (buffer.Direction == Direction.Invalid)
+                               throw new InvalidOperationException ("Buffer's Direction must be valid.");
+
+                       if (buffer.ContentType != ContentType.Unicode) {
+                               throw new InvalidOperationException ("Buffer's ContentType must of type Unicode.");
+                       }
+
+                       var featuresPtr = features == null || features.Count == 0 ? IntPtr.Zero : StructureArrayToPtr (features);
+                       var shapersPtr = shapers == null || shapers.Count == 0 ? IntPtr.Zero : StructureArrayToPtr (shapers);
+
+                       HarfBuzzApi.hb_shape_full (
+                               Handle,
+                               buffer.Handle,
+                               featuresPtr,
+                               features?.Count ?? 0,
+                               shapersPtr);
+
+                       if (featuresPtr != IntPtr.Zero)
+                               Marshal.FreeCoTaskMem (featuresPtr);
+                       if (shapersPtr != IntPtr.Zero)
+                               Marshal.FreeCoTaskMem (shapersPtr);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_font_destroy (Handle);
+                       }
+               }
+       }
+
+       internal class FontUserData
+       {
+               public FontUserData (Font font, object fontData)
+               {
+                       Font = font;
+                       FontData = fontData;
+               }
+
+               public Font Font { get; }
+
+               public object FontData { get; }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/FontFunctions.cs b/src/XSF/HarfBuzzSharp/FontFunctions.cs
new file mode 100644 (file)
index 0000000..e86ddc9
--- /dev/null
@@ -0,0 +1,216 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public class FontFunctions : NativeObject
+       {
+               private static readonly Lazy<FontFunctions> emptyFontFunctions =
+                       new Lazy<FontFunctions> (() => new StaticFontFunctions (HarfBuzzApi.hb_font_funcs_get_empty ()));
+
+               public FontFunctions ()
+                       : this (HarfBuzzApi.hb_font_funcs_create ())
+               {
+               }
+
+               internal FontFunctions (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               public static FontFunctions Empty => emptyFontFunctions.Value;
+
+               public bool IsImmutable => HarfBuzzApi.hb_font_funcs_is_immutable (Handle);
+
+               public void MakeImmutable () => HarfBuzzApi.hb_font_funcs_make_immutable (Handle);
+
+               public void SetHorizontalFontExtentsDelegate (FontExtentsDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_font_h_extents_func (
+                               Handle, DelegateProxies.FontExtentsProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetVerticalFontExtentsDelegate (FontExtentsDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_font_v_extents_func (
+                               Handle, DelegateProxies.FontExtentsProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetNominalGlyphDelegate (NominalGlyphDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_nominal_glyph_func (
+                               Handle, DelegateProxies.NominalGlyphProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetNominalGlyphsDelegate (NominalGlyphsDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_nominal_glyphs_func (
+                               Handle, DelegateProxies.NominalGlyphsProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetVariationGlyphDelegate (VariationGlyphDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_variation_glyph_func (
+                               Handle, DelegateProxies.VariationGlyphProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetHorizontalGlyphAdvanceDelegate (GlyphAdvanceDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_h_advance_func (
+                               Handle, DelegateProxies.GlyphAdvanceProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetVerticalGlyphAdvanceDelegate (GlyphAdvanceDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_v_advance_func (
+                               Handle, DelegateProxies.GlyphAdvanceProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetHorizontalGlyphAdvancesDelegate (GlyphAdvancesDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_h_advances_func (
+                               Handle, DelegateProxies.GlyphAdvancesProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetVerticalGlyphAdvancesDelegate (GlyphAdvancesDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_v_advances_func (
+                               Handle, DelegateProxies.GlyphAdvancesProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetHorizontalGlyphOriginDelegate (GlyphOriginDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_h_origin_func (
+                               Handle, DelegateProxies.GlyphOriginProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetVerticalGlyphOriginDelegate (GlyphOriginDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_v_origin_func (
+                               Handle, DelegateProxies.GlyphOriginProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetHorizontalGlyphKerningDelegate (GlyphKerningDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_h_kerning_func (
+                               Handle, DelegateProxies.GlyphKerningProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetGlyphExtentsDelegate (GlyphExtentsDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_extents_func (
+                               Handle, DelegateProxies.GlyphExtentsProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+               public void SetGlyphContourPointDelegate (GlyphContourPointDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_contour_point_func (
+                               Handle, DelegateProxies.GlyphContourPointProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetGlyphNameDelegate (GlyphNameDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_name_func (
+                               Handle, DelegateProxies.GlyphNameProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetGlyphFromNameDelegate (GlyphFromNameDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMulti (del, destroy);
+
+                       HarfBuzzApi.hb_font_funcs_set_glyph_from_name_func (
+                               Handle, DelegateProxies.GlyphFromNameProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_font_funcs_destroy (Handle);
+                       }
+               }
+
+               private void VerifyParameters (Delegate del)
+               {
+                       _ = del ?? throw new ArgumentNullException (nameof (del));
+
+                       if (IsImmutable)
+                               throw new InvalidOperationException ($"{nameof (FontFunctions)} is immutable and can't be changed.");
+               }
+
+               private class StaticFontFunctions : FontFunctions
+               {
+                       public StaticFontFunctions (IntPtr handle)
+                               : base (handle)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/HarfBuzzApi.cs b/src/XSF/HarfBuzzSharp/HarfBuzzApi.cs
new file mode 100644 (file)
index 0000000..6ef1197
--- /dev/null
@@ -0,0 +1,375 @@
+#pragma warning disable IDE1006 // Naming Styles
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using hb_blob_t = System.IntPtr;
+using hb_bool_t = System.Boolean;
+using hb_buffer_t = System.IntPtr;
+using hb_codepoint_t = System.UInt32;
+using hb_destroy_func_t = HarfBuzzSharp.ReleaseDelegateProxyDelegate;
+using hb_direction_t = HarfBuzzSharp.Direction;
+using hb_face_t = System.IntPtr;
+using hb_font_extents_t = HarfBuzzSharp.FontExtents;
+using hb_font_funcs_t = System.IntPtr;
+using hb_font_t = System.IntPtr;
+using hb_position_t = System.Int32;
+using hb_script_t = System.UInt32;
+using hb_unicode_funcs_t = System.IntPtr;
+
+namespace HarfBuzzSharp
+{
+       internal unsafe class HarfBuzzApi
+       {
+               private const string HARFBUZZ = "libHarfBuzzSharp.2.6.1.so";
+
+               // hb_blob_t
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_blob_t hb_blob_create (IntPtr data, int length, MemoryMode mode, IntPtr user_data, ReleaseDelegateProxyDelegate destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_blob_destroy (hb_blob_t blob);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_blob_make_immutable (hb_blob_t blob);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_blob_is_immutable (hb_blob_t blob);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_blob_get_length (hb_blob_t blob);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static byte* hb_blob_get_data (hb_blob_t blob, out int length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_blob_t hb_blob_create_from_file ([MarshalAs (UnmanagedType.LPStr)] string file_name);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_blob_t hb_blob_get_empty ();
+
+               // hb_face_t
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_face_count (hb_blob_t blob);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_face_t hb_face_create (hb_blob_t blob, int index);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_face_t hb_face_create_for_tables (GetTableDelegateProxyDelegate reference_table_func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_face_t hb_face_get_empty ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_face_destroy (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_face_set_index (hb_face_t face, int index);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_face_get_index (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_face_set_upem (hb_face_t face, int upem);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_face_get_upem (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_face_set_glyph_count (hb_face_t face, int glyph_count);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_face_get_glyph_count (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_face_make_immutable (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_face_is_immutable (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_blob_t hb_face_reference_table (hb_face_t face, Tag tag);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_face_get_table_tags (hb_face_t face, int start_offset, ref int table_count, IntPtr table_tags);
+
+               // hb_font_funcs_t
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_font_funcs_t hb_font_funcs_create ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_font_funcs_t hb_font_funcs_get_empty ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_destroy (hb_font_funcs_t ffuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_make_immutable (hb_font_funcs_t ffuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t ffuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t ffuncs, FontExtentsProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t ffuncs, FontExtentsProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t ffuncs, NominalGlyphProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t ffuncs, NominalGlyphsProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t ffuncs, VariationGlyphProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t ffuncs, GlyphAdvanceProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t ffuncs, GlyphAdvanceProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t ffuncs, GlyphAdvancesProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t ffuncs, GlyphAdvancesProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t ffuncs, GlyphOriginProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t ffuncs, GlyphOriginProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t ffuncs, GlyphKerningProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t ffuncs, GlyphExtentsProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t ffuncs, GlyphContourPointProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_name_func (hb_font_funcs_t ffuncs, GlyphNameProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t ffuncs, GlyphFromNameProxyDelegate func, IntPtr user_data, hb_destroy_func_t destroy);
+
+               // hb_font_t
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_font_t hb_font_create (hb_face_t face);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_font_t hb_font_create_sub_font (hb_font_t parent);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_destroy (hb_font_t font);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_set_funcs (hb_font_t font, hb_font_funcs_t klass, IntPtr font_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_set_scale (hb_font_t font, int x_scale, int y_scale);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_scale (hb_font_t font, out int x_scale, out int y_scale);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_h_extents (hb_font_t font, out hb_font_extents_t extents);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_v_extents (hb_font_t font, out hb_font_extents_t extents);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_nominal_glyph (hb_font_t font, hb_codepoint_t unicode, out hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_variation_glyph (hb_font_t font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, out hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_position_t hb_font_get_glyph_h_advance (hb_font_t font, hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_position_t hb_font_get_glyph_v_advance (hb_font_t font, hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_glyph_h_advances (hb_font_t font, int count, IntPtr first_glyph, uint glyph_stride, IntPtr first_advance, uint advance_stride);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_glyph_v_advances (hb_font_t font, int count, IntPtr first_glyph, uint glyph_stride, IntPtr first_advance, uint advance_stride);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_h_origin (hb_font_t font, hb_codepoint_t glyph, out hb_position_t x, out hb_position_t y);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_v_origin (hb_font_t font, hb_codepoint_t glyph, out hb_position_t x, out hb_position_t y);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_position_t hb_font_get_glyph_h_kerning (hb_font_t font, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_extents (hb_font_t font, hb_codepoint_t glyph, out GlyphExtents extents);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_contour_point (hb_font_t font, hb_codepoint_t glyph, uint point_index, out hb_position_t x, out hb_position_t y);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_name (hb_font_t font, hb_codepoint_t glyph, byte* nameBuffer, int size);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_from_name (hb_font_t font, [MarshalAs (UnmanagedType.LPStr)]string name, int len, out hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph (hb_font_t font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, out hb_codepoint_t glyph);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_extents_for_direction (hb_font_t font, hb_direction_t direction, out hb_font_extents_t extents);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_glyph_advance_for_direction (hb_font_t font, hb_codepoint_t glyph, hb_direction_t direction, out hb_position_t x, out hb_position_t y);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_get_glyph_advances_for_direction (hb_font_t font, hb_direction_t direction, int count, IntPtr first_glyph, uint glyph_stride, IntPtr first_advance, uint advance_stride);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_get_glyph_contour_point_for_origin (hb_font_t font, hb_codepoint_t glyph, uint point_index, hb_direction_t direction, out hb_position_t x, out hb_position_t y);
+               /* Generates gidDDD if glyph has no name. */
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_font_glyph_to_string (hb_font_t font, hb_codepoint_t glyph, byte* s, int size);
+               /* Parses gidDDD and uniUUUU strings automatically. */
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_font_glyph_from_string (hb_font_t font, [MarshalAs (UnmanagedType.LPStr)]string s, int len, /* -1 means nul-terminated */ out hb_codepoint_t glyph);
+
+               // hb_font_t (OT)
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_ot_font_set_funcs (hb_font_t font);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_ot_metrics_get_position (hb_font_t font, OpenTypeMetricsTag metrics_tag, out hb_position_t position     /* OUT.  May be NULL. */);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static float hb_ot_metrics_get_variation (hb_font_t font, OpenTypeMetricsTag metrics_tag);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_position_t hb_ot_metrics_get_x_variation (hb_font_t font, OpenTypeMetricsTag metrics_tag);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_position_t hb_ot_metrics_get_y_variation (hb_font_t font, OpenTypeMetricsTag metrics_tag);
+
+               // hb_buffer_t
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static hb_buffer_t hb_buffer_create ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_destroy (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_reset (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_append (hb_buffer_t buffer, hb_buffer_t source, int start, int end);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_add (hb_buffer_t buffer, hb_codepoint_t codepoint, hb_codepoint_t cluster);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_add_utf8 (hb_buffer_t buffer, IntPtr text, int text_length, int item_offset, int item_length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_add_utf16 (hb_buffer_t buffer, IntPtr text, int text_length, int item_offset, int item_length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_add_utf32 (hb_buffer_t buffer, IntPtr text, int text_length, int item_offset, int item_length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_add_codepoints (hb_buffer_t buffer, IntPtr text, int text_length, int item_offset, int item_length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_guess_segment_properties (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_set_length (hb_buffer_t buffer, int length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static int hb_buffer_get_length (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_clear_contents (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void* hb_buffer_get_glyph_infos (hb_buffer_t buffer, out int length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void* hb_buffer_get_glyph_positions (hb_buffer_t buffer, out int length);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_set_script (hb_buffer_t buffer, hb_script_t script);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_script_t hb_buffer_get_script (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static void hb_buffer_set_direction (hb_buffer_t buffer, hb_direction_t direction);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static Direction hb_buffer_get_direction (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_language (hb_buffer_t buffer, IntPtr language);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern IntPtr hb_buffer_get_language (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_content_type (hb_buffer_t buffer, ContentType content_type);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern ContentType hb_buffer_get_content_type (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_replacement_codepoint (hb_buffer_t buffer, hb_codepoint_t replacement);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_codepoint_t hb_buffer_get_replacement_codepoint (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_invisible_glyph (hb_buffer_t buffer, hb_codepoint_t invisible);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_codepoint_t hb_buffer_get_invisible_glyph (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_flags (hb_buffer_t buffer, BufferFlags flags);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern BufferFlags hb_buffer_get_flags (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_cluster_level (hb_buffer_t buffer, ClusterLevel cluster_level);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern ClusterLevel hb_buffer_get_cluster_level (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_normalize_glyphs (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_reverse (hb_buffer_t buffer);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_reverse_range (hb_buffer_t buffer, int start, int end);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_reverse_clusters (hb_buffer_t buffer);
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern int hb_buffer_serialize_glyphs (hb_buffer_t buffer, int start, int end, IntPtr buf, int buf_size, out int buf_consumed, hb_font_t font, SerializeFormat format, SerializeFlag flags);
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public static extern hb_bool_t hb_buffer_deserialize_glyphs (IntPtr buffer, [MarshalAs (UnmanagedType.LPStr)] string buf, int buf_len, out IntPtr end_ptr, hb_font_t font, SerializeFormat format);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_buffer_set_unicode_funcs (hb_buffer_t buffer, hb_unicode_funcs_t unicode_funcs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_unicode_funcs_t hb_buffer_get_unicode_funcs (hb_buffer_t buffer);
+
+               // hb_shape
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public extern static hb_bool_t hb_shape_full (hb_font_t font, hb_buffer_t buffer, IntPtr features, int num_features, IntPtr shaper_list);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public extern static IntPtr hb_shape_list_shapers ();
+
+               // hb_language
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern IntPtr hb_language_from_string ([MarshalAs (UnmanagedType.LPStr)] string str, int len);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern IntPtr hb_language_to_string (IntPtr language);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern IntPtr hb_language_get_default ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern GlyphFlags hb_glyph_info_get_glyph_flags (ref GlyphInfo info);
+
+               // hb_feature
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_feature_to_string (ref Feature feature, [MarshalAs (UnmanagedType.LPStr)] StringBuilder buf, uint size);
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public static extern hb_bool_t hb_feature_from_string ([MarshalAs (UnmanagedType.LPStr)] string str, int len, out Feature feature);
+
+               // hb_script
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_direction_t hb_script_get_horizontal_direction (hb_script_t script);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_script_t hb_script_from_string ([MarshalAs (UnmanagedType.LPStr)] string str, int len);
+
+               // hb_unicode
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_unicode_funcs_t hb_unicode_funcs_get_default ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_unicode_funcs_t hb_unicode_funcs_get_empty ();
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_unicode_funcs_t hb_unicode_funcs_create (hb_unicode_funcs_t parent);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_destroy (hb_unicode_funcs_t ufuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_make_immutable (hb_unicode_funcs_t ufuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public static extern hb_bool_t hb_unicode_funcs_is_immutable (hb_unicode_funcs_t ufuncs);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern UnicodeCombiningClass hb_unicode_combining_class (hb_unicode_funcs_t ufuncs, hb_codepoint_t unicode);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern UnicodeGeneralCategory hb_unicode_general_category (hb_unicode_funcs_t ufuncs, hb_codepoint_t unicode);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_codepoint_t hb_unicode_mirroring (hb_unicode_funcs_t ufuncs, hb_codepoint_t unicode);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern hb_script_t hb_unicode_script (hb_unicode_funcs_t ufuncs, hb_codepoint_t unicode);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public static extern hb_bool_t hb_unicode_compose (hb_unicode_funcs_t ufuncs, hb_codepoint_t a, hb_codepoint_t b, out hb_codepoint_t ab);
+
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               public static extern hb_bool_t hb_unicode_decompose (hb_unicode_funcs_t ufuncs, hb_codepoint_t ab, out hb_codepoint_t a, out hb_codepoint_t b);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t ufuncs, hb_unicode_combining_class_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t ufuncs, hb_unicode_general_category_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t ufuncs, hb_unicode_mirroring_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_script_func (hb_unicode_funcs_t ufuncs, hb_unicode_script_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t ufuncs, hb_unicode_compose_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+               [DllImport (HARFBUZZ, CallingConvention = CallingConvention.Cdecl)]
+               public static extern void hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t ufuncs, hb_unicode_decompose_func_t func, IntPtr user_data, hb_destroy_func_t destroy);
+       }
+}
+#pragma warning restore IDE1006 // Naming Styles
diff --git a/src/XSF/HarfBuzzSharp/HashCode.cs b/src/XSF/HarfBuzzSharp/HashCode.cs
new file mode 100644 (file)
index 0000000..d5143fc
--- /dev/null
@@ -0,0 +1,143 @@
+// Partial code copied from:
+// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/HashCode.cs
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace HarfBuzzSharp
+{
+       internal unsafe struct HashCode
+       {
+               private static readonly uint s_seed = GenerateGlobalSeed ();
+
+               private const uint Prime1 = 2654435761U;
+               private const uint Prime2 = 2246822519U;
+               private const uint Prime3 = 3266489917U;
+               private const uint Prime4 = 668265263U;
+               private const uint Prime5 = 374761393U;
+
+               private uint _v1, _v2, _v3, _v4;
+               private uint _queue1, _queue2, _queue3;
+               private uint _length;
+
+               private static unsafe uint GenerateGlobalSeed ()
+               {
+                       var rnd = new Random ();
+                       var result = rnd.Next ();
+                       return unchecked((uint)result);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static void Initialize (out uint v1, out uint v2, out uint v3, out uint v4)
+               {
+                       v1 = s_seed + Prime1 + Prime2;
+                       v2 = s_seed + Prime2;
+                       v3 = s_seed;
+                       v4 = s_seed - Prime1;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint Round (uint hash, uint input) =>
+                       RotateLeft (hash + input * Prime2, 13) * Prime1;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint QueueRound (uint hash, uint queuedValue) =>
+                       RotateLeft (hash + queuedValue * Prime3, 17) * Prime4;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint MixState (uint v1, uint v2, uint v3, uint v4) =>
+                       RotateLeft (v1, 1) + RotateLeft (v2, 7) + RotateLeft (v3, 12) + RotateLeft (v4, 18);
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint RotateLeft (uint value, int offset) =>
+                       (value << offset) | (value >> (32 - offset));
+
+               private static uint MixEmptyState () =>
+                       s_seed + Prime5;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint MixFinal (uint hash)
+               {
+                       hash ^= hash >> 15;
+                       hash *= Prime2;
+                       hash ^= hash >> 13;
+                       hash *= Prime3;
+                       hash ^= hash >> 16;
+                       return hash;
+               }
+
+               public void Add (void* value) =>
+                       Add (value == null ? 0 : ((IntPtr)value).GetHashCode ());
+
+               public void Add<T> (T value) =>
+                       Add (value?.GetHashCode () ?? 0);
+
+               private void Add (int value)
+               {
+                       uint val = (uint)value;
+
+                       // Storing the value of _length locally shaves of quite a few bytes
+                       // in the resulting machine code.
+                       uint previousLength = _length++;
+                       uint position = previousLength % 4;
+
+                       // Switch can't be inlined.
+
+                       if (position == 0)
+                               _queue1 = val;
+                       else if (position == 1)
+                               _queue2 = val;
+                       else if (position == 2)
+                               _queue3 = val;
+                       else // position == 3
+                       {
+                               if (previousLength == 3)
+                                       Initialize (out _v1, out _v2, out _v3, out _v4);
+
+                               _v1 = Round (_v1, _queue1);
+                               _v2 = Round (_v2, _queue2);
+                               _v3 = Round (_v3, _queue3);
+                               _v4 = Round (_v4, val);
+                       }
+               }
+
+               public int ToHashCode ()
+               {
+                       // Storing the value of _length locally shaves of quite a few bytes
+                       // in the resulting machine code.
+                       uint length = _length;
+
+                       // position refers to the *next* queue position in this method, so
+                       // position == 1 means that _queue1 is populated; _queue2 would have
+                       // been populated on the next call to Add.
+                       uint position = length % 4;
+
+                       // If the length is less than 4, _v1 to _v4 don't contain anything
+                       // yet. xxHash32 treats this differently.
+
+                       uint hash = length < 4 ? MixEmptyState () : MixState (_v1, _v2, _v3, _v4);
+
+                       // _length is incremented once per Add(Int32) and is therefore 4
+                       // times too small (xxHash length is in bytes, not ints).
+
+                       hash += length * 4;
+
+                       // Mix what remains in the queue
+
+                       // Switch can't be inlined right now, so use as few branches as
+                       // possible by manually excluding impossible scenarios (position > 1
+                       // is always false if position is not > 0).
+                       if (position > 0) {
+                               hash = QueueRound (hash, _queue1);
+                               if (position > 1) {
+                                       hash = QueueRound (hash, _queue2);
+                                       if (position > 2)
+                                               hash = QueueRound (hash, _queue3);
+                               }
+                       }
+
+                       hash = MixFinal (hash);
+                       return (int)hash;
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Language.cs b/src/XSF/HarfBuzzSharp/Language.cs
new file mode 100644 (file)
index 0000000..6a7eb0b
--- /dev/null
@@ -0,0 +1,53 @@
+using System;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       public class Language : NativeObject
+       {
+               private static readonly Lazy<Language> defaultLanguage =
+                       new Lazy<Language> (() => new StaticLanguage (HarfBuzzApi.hb_language_get_default ()));
+
+               public static Language Default => defaultLanguage.Value;
+
+               internal Language (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               public Language (CultureInfo culture)
+                       : this (culture.TwoLetterISOLanguageName)
+               {
+               }
+
+               public Language (string name)
+                       : base (IntPtr.Zero)
+               {
+                       Handle = HarfBuzzApi.hb_language_from_string (name, -1);
+                       Name = Marshal.PtrToStringAnsi (HarfBuzzApi.hb_language_to_string (Handle));
+               }
+
+               public string Name { get; }
+
+               public override string ToString () => Name;
+
+               public override bool Equals (object obj) =>
+                       obj is Language language && Handle == language.Handle;
+
+               public override int GetHashCode () => Name != null ? Name.GetHashCode () : 0;
+
+               private class StaticLanguage : Language
+               {
+                       public StaticLanguage (IntPtr handle)
+                               : base (handle)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/NativeObject.cs b/src/XSF/HarfBuzzSharp/NativeObject.cs
new file mode 100644 (file)
index 0000000..70c6b0a
--- /dev/null
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       public class NativeObject : IDisposable
+       {
+               private bool isDisposed;
+               private readonly bool zero;
+
+               internal NativeObject (IntPtr handle)
+               {
+                       Handle = handle;
+                       zero = true;
+               }
+
+               internal NativeObject (IntPtr handle, bool zero)
+               {
+                       Handle = handle;
+                       this.zero = zero;
+               }
+
+               ~NativeObject ()
+               {
+                       Dispose (false);
+               }
+
+               public virtual IntPtr Handle { get; protected set; }
+
+               // Dispose method - always called
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (isDisposed) {
+                               return;
+                       }
+
+                       isDisposed = true;
+
+                       if (!disposing) {
+                               return;
+                       }
+
+                       DisposeHandler ();
+
+                       if (zero) {
+                               Handle = IntPtr.Zero;
+                       }
+               }
+
+               // Intended to be overridden - always safe to use
+               // since it will never be called unless applicable
+               protected virtual void DisposeHandler ()
+               {
+               }
+
+               public void Dispose ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+
+               internal static int SizeOf<T> ()
+               {
+                       return Marshal.SizeOf (typeof (T));
+               }
+
+               internal static IntPtr StructureArrayToPtr<T> (IReadOnlyList<T> items)
+               {
+                       var size = SizeOf<T> ();
+                       var memory = Marshal.AllocCoTaskMem (size * items.Count);
+                       for (var i = 0; i < items.Count; i++) {
+                               var ptr = new IntPtr (memory.ToInt64 () + (i * size));
+                               Marshal.StructureToPtr (items[i], ptr, true);
+                       }
+                       return memory;
+               }
+
+               internal static IEnumerable<string> PtrToStringArray (IntPtr intPtr)
+               {
+                       if (intPtr != IntPtr.Zero) {
+                               var ptr = Marshal.ReadIntPtr (intPtr);
+                               while (ptr != IntPtr.Zero) {
+                                       var element = Marshal.PtrToStringAnsi (ptr);
+                                       yield return element;
+                                       intPtr = new IntPtr (intPtr.ToInt64 () + IntPtr.Size);
+                                       ptr = Marshal.ReadIntPtr (intPtr);
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/PlatformConfiguration.cs b/src/XSF/HarfBuzzSharp/PlatformConfiguration.cs
new file mode 100644 (file)
index 0000000..2f85544
--- /dev/null
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace HarfBuzzSharp
+{
+       internal static class PlatformConfiguration
+       {
+               public static bool IsUnix { get; }
+               public static bool IsWindows { get; }
+
+               static PlatformConfiguration ()
+               {
+                       IsUnix = Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix;
+                       IsWindows = !IsUnix;
+               }
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Script.cs b/src/XSF/HarfBuzzSharp/Script.cs
new file mode 100644 (file)
index 0000000..3be6e24
--- /dev/null
@@ -0,0 +1,40 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public partial struct Script : IEquatable<Script>
+       {
+               private readonly Tag tag;
+
+               private Script (Tag tag)
+               {
+                       this.tag = tag;
+               }
+
+               public Direction HorizontalDirection =>
+                       HarfBuzzApi.hb_script_get_horizontal_direction (tag);
+
+               public static Script Parse (string str) =>
+                       HarfBuzzApi.hb_script_from_string (str, -1);
+
+               public static bool TryParse (string str, out Script script)
+               {
+                       script = Parse (str);
+
+                       return script != Unknown;
+               }
+
+               public override string ToString () => tag.ToString ();
+
+               public static implicit operator uint (Script script) => script.tag;
+
+               public static implicit operator Script (uint tag) => new Script (tag);
+
+               public override bool Equals (object obj) =>
+                       obj is Script script && tag.Equals (script.tag);
+
+               public bool Equals (Script other) => tag.Equals (other.tag);
+
+               public override int GetHashCode () => tag.GetHashCode ();
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Script.fields.cs b/src/XSF/HarfBuzzSharp/Script.fields.cs
new file mode 100644 (file)
index 0000000..87db8a2
--- /dev/null
@@ -0,0 +1,329 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public partial struct Script
+       {
+               public static readonly Script Invalid = new Script (Tag.None);
+
+               public static readonly Script MaxValue = new Script (Tag.Max);
+
+               public static readonly Script MaxValueSigned = new Script (Tag.MaxSigned);
+
+               // Special scripts
+
+               // 1.1
+               public static readonly Script Common = new Script (new Tag ('Z', 'y', 'y', 'y'));
+               // 1.1
+               public static readonly Script Inherited = new Script (new Tag ('Z', 'i', 'n', 'h'));
+               // 5.0
+               public static readonly Script Unknown = new Script (new Tag ('Z', 'z', 'z', 'z'));
+
+               // Scripts
+
+               // 1.1
+               public static readonly Script Arabic = new Script (new Tag ('A', 'r', 'a', 'b'));
+               // 1.1
+               public static readonly Script Armenian = new Script (new Tag ('A', 'r', 'm', 'n'));
+               // 1.1
+               public static readonly Script Bengali = new Script (new Tag ('B', 'e', 'n', 'g'));
+               // 1.1
+               public static readonly Script Cyrillic = new Script (new Tag ('C', 'y', 'r', 'l'));
+               // 1.1
+               public static readonly Script Devanagari = new Script (new Tag ('D', 'e', 'v', 'a'));
+               // 1.1
+               public static readonly Script Georgian = new Script (new Tag ('G', 'e', 'o', 'r'));
+               // 1.1
+               public static readonly Script Greek = new Script (new Tag ('G', 'r', 'e', 'k'));
+               // 1.1
+               public static readonly Script Gujarati = new Script (new Tag ('G', 'u', 'j', 'r'));
+               // 1.1
+               public static readonly Script Gurmukhi = new Script (new Tag ('G', 'u', 'r', 'u'));
+               // 1.1
+               public static readonly Script Hangul = new Script (new Tag ('H', 'a', 'n', 'g'));
+               // 1.1
+               public static readonly Script Han = new Script (new Tag ('H', 'a', 'n', 'i'));
+               // 1.1
+               public static readonly Script Hebrew = new Script (new Tag ('H', 'e', 'b', 'r'));
+               // 1.1
+               public static readonly Script Hiragana = new Script (new Tag ('H', 'i', 'r', 'a'));
+               // 1.1
+               public static readonly Script Kannada = new Script (new Tag ('K', 'n', 'd', 'a'));
+               // 1.1
+               public static readonly Script Katakana = new Script (new Tag ('K', 'a', 'n', 'a'));
+               // 1.1
+               public static readonly Script Lao = new Script (new Tag ('L', 'a', 'o', 'o'));
+               // 1.1
+               public static readonly Script Latin = new Script (new Tag ('L', 'a', 't', 'n'));
+               // 1.1
+               public static readonly Script Malayalam = new Script (new Tag ('M', 'l', 'y', 'm'));
+               // 1.1
+               public static readonly Script Oriya = new Script (new Tag ('O', 'r', 'y', 'a'));
+               // 1.1
+               public static readonly Script Tamil = new Script (new Tag ('T', 'a', 'm', 'l'));
+               // 1.1
+               public static readonly Script Telugu = new Script (new Tag ('T', 'e', 'l', 'u'));
+               // 1.1
+               public static readonly Script Thai = new Script (new Tag ('T', 'h', 'a', 'i'));
+               // 2.0
+               public static readonly Script Tibetan = new Script (new Tag ('T', 'i', 'b', 't'));
+               // 3.0
+               public static readonly Script Bopomofo = new Script (new Tag ('B', 'o', 'p', 'o'));
+               // 3.0
+               public static readonly Script Braille = new Script (new Tag ('B', 'r', 'a', 'i'));
+               // 3.0
+               public static readonly Script CanadianSyllabics = new Script (new Tag ('C', 'a', 'n', 's'));
+               // 3.0
+               public static readonly Script Cherokee = new Script (new Tag ('C', 'h', 'e', 'r'));
+               // 3.0
+               public static readonly Script Ethiopic = new Script (new Tag ('E', 't', 'h', 'i'));
+               // 3.0
+               public static readonly Script Khmer = new Script (new Tag ('K', 'h', 'm', 'r'));
+               // 3.0
+               public static readonly Script Mongolian = new Script (new Tag ('M', 'o', 'n', 'g'));
+               // 3.0
+               public static readonly Script Myanmar = new Script (new Tag ('M', 'y', 'm', 'r'));
+               // 3.0
+               public static readonly Script Ogham = new Script (new Tag ('O', 'g', 'a', 'm'));
+               // 3.0
+               public static readonly Script Runic = new Script (new Tag ('R', 'u', 'n', 'r'));
+               // 3.0
+               public static readonly Script Sinhala = new Script (new Tag ('S', 'i', 'n', 'h'));
+               // 3.0
+               public static readonly Script Syriac = new Script (new Tag ('S', 'y', 'r', 'c'));
+               // 3.0
+               public static readonly Script Thaana = new Script (new Tag ('T', 'h', 'a', 'a'));
+               // 3.0
+               public static readonly Script Yi = new Script (new Tag ('Y', 'i', 'i', 'i'));
+               // 3.1
+               public static readonly Script Deseret = new Script (new Tag ('D', 's', 'r', 't'));
+               // 3.1
+               public static readonly Script Gothic = new Script (new Tag ('G', 'o', 't', 'h'));
+               // 3.1
+               public static readonly Script OldItalic = new Script (new Tag ('I', 't', 'a', 'l'));
+               // 3.2
+               public static readonly Script Buhid = new Script (new Tag ('B', 'u', 'h', 'd'));
+               // 3.2
+               public static readonly Script Hanunoo = new Script (new Tag ('H', 'a', 'n', 'o'));
+               // 3.2
+               public static readonly Script Tagalog = new Script (new Tag ('T', 'g', 'l', 'g'));
+               // 3.2
+               public static readonly Script Tagbanwa = new Script (new Tag ('T', 'a', 'g', 'b'));
+               // 4.0
+               public static readonly Script Cypriot = new Script (new Tag ('C', 'p', 'r', 't'));
+               // 4.0
+               public static readonly Script Limbu = new Script (new Tag ('L', 'i', 'm', 'b'));
+               // 4.0
+               public static readonly Script LinearB = new Script (new Tag ('L', 'i', 'n', 'b'));
+               // 4.0
+               public static readonly Script Osmanya = new Script (new Tag ('O', 's', 'm', 'a'));
+               // 4.0
+               public static readonly Script Shavian = new Script (new Tag ('S', 'h', 'a', 'w'));
+               // 4.0
+               public static readonly Script TaiLe = new Script (new Tag ('T', 'a', 'l', 'e'));
+               // 4.0
+               public static readonly Script Ugaritic = new Script (new Tag ('U', 'g', 'a', 'r'));
+               // 4.1
+               public static readonly Script Buginese = new Script (new Tag ('B', 'u', 'g', 'i'));
+               // 4.1
+               public static readonly Script Coptic = new Script (new Tag ('C', 'o', 'p', 't'));
+               // 4.1
+               public static readonly Script Glagolitic = new Script (new Tag ('G', 'l', 'a', 'g'));
+               // 4.1
+               public static readonly Script Kharoshthi = new Script (new Tag ('K', 'h', 'a', 'r'));
+               // 4.1
+               public static readonly Script NewTaiLue = new Script (new Tag ('T', 'a', 'l', 'u'));
+               // 4.1
+               public static readonly Script OldPersian = new Script (new Tag ('X', 'p', 'e', 'o'));
+               // 4.1
+               public static readonly Script SylotiNagri = new Script (new Tag ('S', 'y', 'l', 'o'));
+               // 4.1
+               public static readonly Script Tifinagh = new Script (new Tag ('T', 'f', 'n', 'g'));
+               // 5.0
+               public static readonly Script Balinese = new Script (new Tag ('B', 'a', 'l', 'i'));
+               // 5.0
+               public static readonly Script Cuneiform = new Script (new Tag ('X', 's', 'u', 'x'));
+               // 5.0
+               public static readonly Script Nko = new Script (new Tag ('N', 'k', 'o', 'o'));
+               // 5.0
+               public static readonly Script PhagsPa = new Script (new Tag ('P', 'h', 'a', 'g'));
+               // 5.0
+               public static readonly Script Phoenician = new Script (new Tag ('P', 'h', 'n', 'x'));
+               // 5.1
+               public static readonly Script Carian = new Script (new Tag ('C', 'a', 'r', 'i'));
+               // 5.1
+               public static readonly Script Cham = new Script (new Tag ('C', 'h', 'a', 'm'));
+               // 5.1
+               public static readonly Script KayahLi = new Script (new Tag ('K', 'a', 'l', 'i'));
+               // 5.1
+               public static readonly Script Lepcha = new Script (new Tag ('L', 'e', 'p', 'c'));
+               // 5.1
+               public static readonly Script Lycian = new Script (new Tag ('L', 'y', 'c', 'i'));
+               // 5.1
+               public static readonly Script Lydian = new Script (new Tag ('L', 'y', 'd', 'i'));
+               // 5.1
+               public static readonly Script OlChiki = new Script (new Tag ('O', 'l', 'c', 'k'));
+               // 5.1
+               public static readonly Script Rejang = new Script (new Tag ('R', 'j', 'n', 'g'));
+               // 5.1
+               public static readonly Script Saurashtra = new Script (new Tag ('S', 'a', 'u', 'r'));
+               // 5.1
+               public static readonly Script Sundanese = new Script (new Tag ('S', 'u', 'n', 'd'));
+               // 5.1
+               public static readonly Script Vai = new Script (new Tag ('V', 'a', 'i', 'i'));
+               // 5.2
+               public static readonly Script Avestan = new Script (new Tag ('A', 'v', 's', 't'));
+               // 5.2
+               public static readonly Script Bamum = new Script (new Tag ('B', 'a', 'm', 'u'));
+               // 5.2
+               public static readonly Script EgyptianHieroglyphs = new Script (new Tag ('E', 'g', 'y', 'p'));
+               // 5.2
+               public static readonly Script ImperialAramaic = new Script (new Tag ('A', 'r', 'm', 'i'));
+               // 5.2
+               public static readonly Script InscriptionalPahlavi = new Script (new Tag ('P', 'h', 'l', 'i'));
+               // 5.2
+               public static readonly Script InscriptionalParthian = new Script (new Tag ('P', 'r', 't', 'i'));
+               // 5.2
+               public static readonly Script Javanese = new Script (new Tag ('J', 'a', 'v', 'a'));
+               // 5.2
+               public static readonly Script Kaithi = new Script (new Tag ('K', 't', 'h', 'i'));
+               // 5.2
+               public static readonly Script Lisu = new Script (new Tag ('L', 'i', 's', 'u'));
+               // 5.2
+               public static readonly Script MeeteiMayek = new Script (new Tag ('M', 't', 'e', 'i'));
+               // 5.2
+               public static readonly Script OldSouthArabian = new Script (new Tag ('S', 'a', 'r', 'b'));
+               // 5.2
+               public static readonly Script OldTurkic = new Script (new Tag ('O', 'r', 'k', 'h'));
+               // 5.2
+               public static readonly Script Samaritan = new Script (new Tag ('S', 'a', 'm', 'r'));
+               // 5.2
+               public static readonly Script TaiTham = new Script (new Tag ('L', 'a', 'n', 'a'));
+               // 5.2
+               public static readonly Script TaiViet = new Script (new Tag ('T', 'a', 'v', 't'));
+               // 6.0
+               public static readonly Script Batak = new Script (new Tag ('B', 'a', 't', 'k'));
+               // 6.0
+               public static readonly Script Brahmi = new Script (new Tag ('B', 'r', 'a', 'h'));
+               // 6.0
+               public static readonly Script Mandaic = new Script (new Tag ('M', 'a', 'n', 'd'));
+               // 6.1
+               public static readonly Script Chakma = new Script (new Tag ('C', 'a', 'k', 'm'));
+               // 6.1
+               public static readonly Script MeroiticCursive = new Script (new Tag ('M', 'e', 'r', 'c'));
+               // 6.1
+               public static readonly Script MeroiticHieroglyphs = new Script (new Tag ('M', 'e', 'r', 'o'));
+               // 6.1
+               public static readonly Script Miao = new Script (new Tag ('P', 'l', 'r', 'd'));
+               // 6.1
+               public static readonly Script Sharada = new Script (new Tag ('S', 'h', 'r', 'd'));
+               // 6.1
+               public static readonly Script SoraSompeng = new Script (new Tag ('S', 'o', 'r', 'a'));
+               // 6.1
+               public static readonly Script Takri = new Script (new Tag ('T', 'a', 'k', 'r'));
+
+               // Since: 0.9.30
+
+               // 7.0
+               public static readonly Script BassaVah = new Script (new Tag ('B', 'a', 's', 's'));
+               // 7.0
+               public static readonly Script CaucasianAlbanian = new Script (new Tag ('A', 'g', 'h', 'b'));
+               // 7.0
+               public static readonly Script Duployan = new Script (new Tag ('D', 'u', 'p', 'l'));
+               // 7.0
+               public static readonly Script Elbasan = new Script (new Tag ('E', 'l', 'b', 'a'));
+               // 7.0
+               public static readonly Script Grantha = new Script (new Tag ('G', 'r', 'a', 'n'));
+               // 7.0
+               public static readonly Script Khojki = new Script (new Tag ('K', 'h', 'o', 'j'));
+               // 7.0
+               public static readonly Script Khudawadi = new Script (new Tag ('S', 'i', 'n', 'd'));
+               // 7.0
+               public static readonly Script LinearA = new Script (new Tag ('L', 'i', 'n', 'a'));
+               // 7.0
+               public static readonly Script Mahajani = new Script (new Tag ('M', 'a', 'h', 'j'));
+               // 7.0
+               public static readonly Script Manichaean = new Script (new Tag ('M', 'a', 'n', 'i'));
+               // 7.0
+               public static readonly Script MendeKikakui = new Script (new Tag ('M', 'e', 'n', 'd'));
+               // 7.0
+               public static readonly Script Modi = new Script (new Tag ('M', 'o', 'd', 'i'));
+               // 7.0
+               public static readonly Script Mro = new Script (new Tag ('M', 'r', 'o', 'o'));
+               // 7.0
+               public static readonly Script Nabataean = new Script (new Tag ('N', 'b', 'a', 't'));
+               // 7.0
+               public static readonly Script OldNorthArabian = new Script (new Tag ('N', 'a', 'r', 'b'));
+               // 7.0
+               public static readonly Script OldPermic = new Script (new Tag ('P', 'e', 'r', 'm'));
+               // 7.0
+               public static readonly Script PahawhHmong = new Script (new Tag ('H', 'm', 'n', 'g'));
+               // 7.0
+               public static readonly Script Palmyrene = new Script (new Tag ('P', 'a', 'l', 'm'));
+               // 7.0
+               public static readonly Script PauCinHau = new Script (new Tag ('P', 'a', 'u', 'c'));
+               // 7.0
+               public static readonly Script PsalterPahlavi = new Script (new Tag ('P', 'h', 'l', 'p'));
+               // 7.0
+               public static readonly Script Siddham = new Script (new Tag ('S', 'i', 'd', 'd'));
+               // 7.0
+               public static readonly Script Tirhuta = new Script (new Tag ('T', 'i', 'r', 'h'));
+               // 7.0
+               public static readonly Script WarangCiti = new Script (new Tag ('W', 'a', 'r', 'a'));
+               // 8.0
+               public static readonly Script Ahom = new Script (new Tag ('A', 'h', 'o', 'm'));
+               // 8.0
+               public static readonly Script AnatolianHieroglyphs = new Script (new Tag ('H', 'l', 'u', 'w'));
+               // 8.0
+               public static readonly Script Hatran = new Script (new Tag ('H', 'a', 't', 'r'));
+               // 8.0
+               public static readonly Script Multani = new Script (new Tag ('M', 'u', 'l', 't'));
+               // 8.0
+               public static readonly Script OldHungarian = new Script (new Tag ('H', 'u', 'n', 'g'));
+               // 8.0
+               public static readonly Script Signwriting = new Script (new Tag ('S', 'g', 'n', 'w'));
+
+               // Since 1.3.0
+
+               // 9.0
+               public static readonly Script Adlam = new Script (new Tag ('A', 'd', 'l', 'm'));
+               // 9.0
+               public static readonly Script Bhaiksuki = new Script (new Tag ('B', 'h', 'k', 's'));
+               // 9.0
+               public static readonly Script Marchen = new Script (new Tag ('M', 'a', 'r', 'c'));
+               // 9.0
+               public static readonly Script Osage = new Script (new Tag ('O', 's', 'g', 'e'));
+               // 9.0
+               public static readonly Script Tangut = new Script (new Tag ('T', 'a', 'n', 'g'));
+               // 9.0
+               public static readonly Script Newa = new Script (new Tag ('N', 'e', 'w', 'a'));
+
+               // Since 1.6.0
+
+               // 10.0
+               public static readonly Script MasaramGondi = new Script (new Tag ('G', 'o', 'n', 'm'));
+               // 10.0
+               public static readonly Script Nushu = new Script (new Tag ('N', 's', 'h', 'u'));
+               // 10.0
+               public static readonly Script Soyombo = new Script (new Tag ('S', 'o', 'y', 'o'));
+               // 10.0
+               public static readonly Script ZanabazarSquare = new Script (new Tag ('Z', 'a', 'n', 'b'));
+
+               // Since 1.8.0
+
+               // 11.0
+               public static readonly Script Dogra = new Script (new Tag ('D', 'o', 'g', 'r'));
+               // 11.0
+               public static readonly Script GunjalaGondi = new Script (new Tag ('G', 'o', 'n', 'g'));
+               // 11.0
+               public static readonly Script HanifiRohingya = new Script (new Tag ('R', 'o', 'h', 'g'));
+               // 11.0
+               public static readonly Script Makasar = new Script (new Tag ('M', 'a', 'k', 'a'));
+               // 11.0
+               public static readonly Script Medefaidrin = new Script (new Tag ('M', 'e', 'd', 'f'));
+               // 11.0
+               public static readonly Script OldSogdian = new Script (new Tag ('S', 'o', 'g', 'o'));
+               // 11.0
+               public static readonly Script Sogdian = new Script (new Tag ('S', 'o', 'g', 'd'));
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/Tag.cs b/src/XSF/HarfBuzzSharp/Tag.cs
new file mode 100644 (file)
index 0000000..ccf3c3a
--- /dev/null
@@ -0,0 +1,75 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public struct Tag : IEquatable<Tag>
+       {
+               public static readonly Tag None = new Tag (0, 0, 0, 0);
+               public static readonly Tag Max = new Tag (byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
+               public static readonly Tag MaxSigned = new Tag ((byte)sbyte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
+
+               private readonly uint value;
+
+               private Tag (uint value)
+               {
+                       this.value = value;
+               }
+
+               private Tag (byte c1, byte c2, byte c3, byte c4)
+               {
+                       value = (uint)((c1 << 24) | (c2 << 16) | (c3 << 8) | c4);
+               }
+
+               public Tag (char c1, char c2, char c3, char c4)
+               {
+                       value = (uint)(((byte)c1 << 24) | ((byte)c2 << 16) | ((byte)c3 << 8) | (byte)c4);
+               }
+
+               public static Tag Parse (string tag)
+               {
+                       if (string.IsNullOrEmpty(tag))
+                               return None;
+
+                       var realTag = new char[4];
+
+                       var len = Math.Min (4, tag.Length);
+                       var i = 0;
+                       for (; i < len; i++)
+                               realTag[i] = tag[i];
+                       for (; i < 4; i++)
+                               realTag[i] = ' ';
+
+                       return new Tag (realTag[0], realTag[1], realTag[2], realTag[3]);
+               }
+
+               public override string ToString ()
+               {
+                       if (value == None) {
+                               return nameof (None);
+                       }
+                       if (value == Max) {
+                               return nameof (Max);
+                       }
+                       if (value == MaxSigned) {
+                               return nameof (MaxSigned);
+                       }
+
+                       return string.Concat (
+                               (char)(byte)(value >> 24),
+                               (char)(byte)(value >> 16),
+                               (char)(byte)(value >> 8),
+                               (char)(byte)value);
+               }
+
+               public static implicit operator uint (Tag tag) => tag.value;
+
+               public static implicit operator Tag (uint tag) => new Tag (tag);
+
+               public override bool Equals (object obj) =>
+                       obj is Tag tag && value.Equals (tag.value);
+
+               public bool Equals (Tag other) => value == other.value;
+
+               public override int GetHashCode () => (int)value;
+       }
+}
diff --git a/src/XSF/HarfBuzzSharp/UnicodeFunctions.cs b/src/XSF/HarfBuzzSharp/UnicodeFunctions.cs
new file mode 100644 (file)
index 0000000..620baaf
--- /dev/null
@@ -0,0 +1,166 @@
+using System;
+
+namespace HarfBuzzSharp
+{
+       public class UnicodeFunctions : NativeObject
+       {
+               private static readonly Lazy<UnicodeFunctions> defaultFunctions =
+                       new Lazy<UnicodeFunctions> (() => new StaticUnicodeFunctions (HarfBuzzApi.hb_unicode_funcs_get_default ()));
+
+               private static readonly Lazy<UnicodeFunctions> emptyFunctions =
+                       new Lazy<UnicodeFunctions> (() => new StaticUnicodeFunctions (HarfBuzzApi.hb_unicode_funcs_get_empty ()));
+
+               public static UnicodeFunctions Default => defaultFunctions.Value;
+
+               public static UnicodeFunctions Empty => emptyFunctions.Value;
+
+               internal UnicodeFunctions (IntPtr handle)
+                       : base (handle)
+               {
+               }
+
+               public UnicodeFunctions (UnicodeFunctions parent) : base (IntPtr.Zero)
+               {
+                       if (parent == null)
+                               throw new ArgumentNullException (nameof (parent));
+                       if (parent.Handle == IntPtr.Zero)
+                               throw new ArgumentException (nameof (parent.Handle));
+
+                       Parent = parent;
+                       Handle = HarfBuzzApi.hb_unicode_funcs_create (parent.Handle);
+               }
+
+               public UnicodeFunctions Parent { get; }
+
+               public bool IsImmutable => HarfBuzzApi.hb_unicode_funcs_is_immutable (Handle);
+
+               public void MakeImmutable () => HarfBuzzApi.hb_unicode_funcs_make_immutable (Handle);
+
+               public UnicodeCombiningClass GetCombiningClass (int unicode) => GetCombiningClass ((uint)unicode);
+
+               public UnicodeCombiningClass GetCombiningClass (uint unicode) =>
+                       HarfBuzzApi.hb_unicode_combining_class (Handle, unicode);
+
+               public UnicodeGeneralCategory GetGeneralCategory (int unicode) => GetGeneralCategory ((uint)unicode);
+
+               public UnicodeGeneralCategory GetGeneralCategory (uint unicode) =>
+                       HarfBuzzApi.hb_unicode_general_category (Handle, unicode);
+
+               public int GetMirroring (int unicode) => (int)GetMirroring ((uint)unicode);
+
+               public uint GetMirroring (uint unicode) => HarfBuzzApi.hb_unicode_mirroring (Handle, unicode);
+
+               public Script GetScript (int unicode) => GetScript ((uint)unicode);
+
+               public Script GetScript (uint unicode) => HarfBuzzApi.hb_unicode_script (Handle, unicode);
+
+               public bool TryCompose (int a, int b, out int ab)
+               {
+                       var result = TryCompose ((uint)a, (uint)b, out var composed);
+
+                       ab = (int)composed;
+
+                       return result;
+               }
+
+               public bool TryCompose (uint a, uint b, out uint ab) => HarfBuzzApi.hb_unicode_compose (Handle, a, b, out ab);
+
+               public bool TryDecompose (int ab, out int a, out int b)
+               {
+                       var result = TryDecompose ((uint)ab, out var decomposedA, out var decomposedB);
+
+                       a = (int)decomposedA;
+
+                       b = (int)decomposedB;
+
+                       return result;
+               }
+
+               public bool TryDecompose (uint ab, out uint a, out uint b) => HarfBuzzApi.hb_unicode_decompose (Handle, ab, out a, out b);
+
+               public void SetCombiningClassDelegate (CombiningClassDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_combining_class_func (
+                               Handle, DelegateProxies.CombiningClassProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetGeneralCategoryDelegate (GeneralCategoryDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_general_category_func (
+                               Handle, DelegateProxies.GeneralCategoryProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetMirroringDelegate (MirroringDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_mirroring_func (
+                               Handle, DelegateProxies.MirroringProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetScriptDelegate (ScriptDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_script_func (
+                               Handle, DelegateProxies.ScriptProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetComposeDelegate (ComposeDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_compose_func (
+                               Handle, DelegateProxies.ComposeProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               public void SetDecomposeDelegate (DecomposeDelegate del, ReleaseDelegate destroy = null)
+               {
+                       VerifyParameters (del);
+
+                       var ctx = DelegateProxies.CreateMultiUserData (del, destroy, this);
+                       HarfBuzzApi.hb_unicode_funcs_set_decompose_func (
+                               Handle, DelegateProxies.DecomposeProxy, ctx, DelegateProxies.ReleaseDelegateProxyForMulti);
+               }
+
+               private void VerifyParameters (Delegate del)
+               {
+                       _ = del ?? throw new ArgumentNullException (nameof (del));
+
+                       if (IsImmutable)
+                               throw new InvalidOperationException ($"{nameof (UnicodeFunctions)} is immutable and can't be changed.");
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeHandler ()
+               {
+                       if (Handle != IntPtr.Zero) {
+                               HarfBuzzApi.hb_unicode_funcs_destroy (Handle);
+                       }
+               }
+
+               private class StaticUnicodeFunctions : UnicodeFunctions
+               {
+                       public StaticUnicodeFunctions (IntPtr handle)
+                               : base (handle)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.HarfBuzz/BlobExtensions.cs b/src/XSF/SkiaSharp.HarfBuzz/BlobExtensions.cs
new file mode 100644 (file)
index 0000000..2067509
--- /dev/null
@@ -0,0 +1,38 @@
+using System;
+using System.Runtime.InteropServices;
+
+using HarfBuzzSharp;
+
+namespace SkiaSharp.HarfBuzz
+{
+       public static class BlobExtensions
+       {
+               public static Blob ToHarfBuzzBlob(this SKStreamAsset asset)
+               {
+                       if (asset == null)
+                       {
+                               throw new ArgumentNullException(nameof(asset));
+                       }
+
+                       var size = asset.Length;
+
+                       Blob blob;
+
+                       var memoryBase = asset.GetMemoryBase();
+                       if (memoryBase != IntPtr.Zero)
+                       {
+                               blob = new Blob(memoryBase, size, MemoryMode.ReadOnly, () => asset.Dispose());
+                       }
+                       else
+                       {
+                               var ptr = Marshal.AllocCoTaskMem(size);
+                               asset.Read(ptr, size);
+                               blob = new Blob(ptr, size, MemoryMode.ReadOnly, () => Marshal.FreeCoTaskMem(ptr));
+                       }
+
+                       blob.MakeImmutable();
+
+                       return blob;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.HarfBuzz/CanvasExtensions.cs b/src/XSF/SkiaSharp.HarfBuzz/CanvasExtensions.cs
new file mode 100644 (file)
index 0000000..84446d0
--- /dev/null
@@ -0,0 +1,39 @@
+using System;
+using System.Linq;
+
+using HarfBuzzSharp;
+
+namespace SkiaSharp.HarfBuzz
+{
+       public static class CanvasExtensions
+       {
+               public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, float x, float y, SKPaint paint)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException(nameof(canvas));
+                       if (shaper == null)
+                               throw new ArgumentNullException(nameof(shaper));
+                       if (text == null)
+                               throw new ArgumentNullException(nameof(text));
+                       if (paint == null)
+                               throw new ArgumentNullException(nameof(paint));
+
+                       if (string.IsNullOrEmpty(text))
+                               return;
+
+                       // shape the text
+                       var result = shaper.Shape(text, x, y, paint);
+
+                       // draw the text
+
+                       using (var paintClone = paint.Clone())
+                       {
+                               paintClone.TextEncoding = SKTextEncoding.GlyphId;
+                               paintClone.Typeface = shaper.Typeface;
+
+                               var bytes = result.Codepoints.Select(cp => BitConverter.GetBytes((ushort)cp)).SelectMany(b => b).ToArray();
+                               canvas.DrawPositionedText(bytes, result.Points, paintClone);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.HarfBuzz/FontExtensions.cs b/src/XSF/SkiaSharp.HarfBuzz/FontExtensions.cs
new file mode 100644 (file)
index 0000000..b4d660e
--- /dev/null
@@ -0,0 +1,30 @@
+using System;
+
+using HarfBuzzSharp;
+
+namespace SkiaSharp.HarfBuzz
+{
+       public static class FontExtensions
+       {
+               public static SKSizeI GetScale(this Font font)
+               {
+                       if (font == null)
+                       {
+                               throw new ArgumentNullException(nameof(font));
+                       }
+
+                       font.GetScale(out var scaleX, out var scaleY);
+                       return new SKSizeI(scaleX, scaleY);
+               }
+
+               public static void SetScale(this Font font, SKSizeI scale)
+               {
+                       if (font == null)
+                       {
+                               throw new ArgumentNullException(nameof(font));
+                       }
+
+                       font.SetScale(scale.Width, scale.Height);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.HarfBuzz/SKShaper.cs b/src/XSF/SkiaSharp.HarfBuzz/SKShaper.cs
new file mode 100644 (file)
index 0000000..9f8bcd7
--- /dev/null
@@ -0,0 +1,148 @@
+using System;
+
+using HarfBuzzSharp;
+using Buffer = HarfBuzzSharp.Buffer;
+
+namespace SkiaSharp.HarfBuzz
+{
+       public class SKShaper : IDisposable
+       {
+               internal const int FONT_SIZE_SCALE = 512;
+
+               private Font font;
+               private Buffer buffer;
+
+               public SKShaper(SKTypeface typeface)
+               {
+                       Typeface = typeface ?? throw new ArgumentNullException(nameof(typeface));
+
+                       int index;
+                       using (var blob = Typeface.OpenStream(out index).ToHarfBuzzBlob())
+                       using (var face = new Face(blob, index))
+                       {
+                               face.Index = index;
+                               face.UnitsPerEm = Typeface.UnitsPerEm;
+
+                               font = new Font(face);
+                               font.SetScale(FONT_SIZE_SCALE, FONT_SIZE_SCALE);
+
+                               font.SetFunctionsOpenType();
+                       }
+
+                       buffer = new Buffer();
+               }
+
+               public SKTypeface Typeface { get; private set; }
+
+               public void Dispose()
+               {
+                       font?.Dispose();
+                       buffer?.Dispose();
+               }
+
+               public Result Shape(Buffer buffer, SKPaint paint) =>
+                       Shape(buffer, 0, 0, paint);
+
+               public Result Shape(Buffer buffer, float xOffset, float yOffset, SKPaint paint)
+               {
+                       if (buffer == null)
+                       {
+                               throw new ArgumentNullException(nameof(buffer));
+                       }
+
+                       if (paint == null)
+                       {
+                               throw new ArgumentNullException(nameof(paint));
+                       }
+
+                       // do the shaping
+                       font.Shape(buffer);
+
+                       // get the shaping results
+                       var len = buffer.Length;
+                       var info = buffer.GlyphInfos;
+                       var pos = buffer.GlyphPositions;
+
+                       // get the sizes
+                       float textSizeY = paint.TextSize / FONT_SIZE_SCALE;
+                       float textSizeX = textSizeY * paint.TextScaleX;
+
+                       var points = new SKPoint[len];
+                       var clusters = new uint[len];
+                       var codepoints = new uint[len];
+
+                       for (var i = 0; i < len; i++)
+                       {
+                               codepoints[i] = info[i].Codepoint;
+
+                               clusters[i] = info[i].Cluster;
+
+                               points[i] = new SKPoint(
+                                       xOffset + pos[i].XOffset * textSizeX,
+                                       yOffset - pos[i].YOffset * textSizeY);
+
+                               // move the cursor
+                               xOffset += pos[i].XAdvance * textSizeX;
+                               yOffset += pos[i].YAdvance * textSizeY;
+                       }
+
+                       return new Result(codepoints, clusters, points);
+               }
+
+               public Result Shape(string text, SKPaint paint) =>
+                       Shape(text, 0, 0, paint);
+
+               public Result Shape(string text, float xOffset, float yOffset, SKPaint paint)
+               {
+                       if (string.IsNullOrEmpty(text))
+                       {
+                               return new Result();
+                       }
+
+                       using (var buffer = new Buffer())
+                       {
+                               switch (paint.TextEncoding)
+                               {
+                                       case SKTextEncoding.Utf8:
+                                               buffer.AddUtf8(text);
+                                               break;
+                                       case SKTextEncoding.Utf16:
+                                               buffer.AddUtf16(text);
+                                               break;
+                                       case SKTextEncoding.Utf32:
+                                               buffer.AddUtf32(text);
+                                               break;
+                                       default:
+                                               throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
+                               }
+
+                               buffer.GuessSegmentProperties();
+
+                               return Shape(buffer, xOffset, yOffset, paint);
+                       }
+               }
+
+               public class Result
+               {
+                       public Result()
+                       {
+                               Codepoints = new uint[0];
+                               Clusters = new uint[0];
+                               Points = new SKPoint[0];
+                       }
+
+                       public Result(uint[] codepoints, uint[] clusters, SKPoint[] points)
+                       {
+                               Codepoints = codepoints;
+                               Clusters = clusters;
+                               Points = points;
+                       }
+
+                       public uint[] Codepoints { get; private set; }
+
+                       public uint[] Clusters { get; private set; }
+
+                       public SKPoint[] Points { get; private set; }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/Extensions.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/Extensions.cs
new file mode 100644 (file)
index 0000000..d153f21
--- /dev/null
@@ -0,0 +1,68 @@
+using Xamarin.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       public static class Extensions
+       {
+               public static Point ToFormsPoint(this SKPointI point)
+               {
+                       return new Point(point.X, point.Y);
+               }
+
+               public static Point ToFormsPoint(this SKPoint point)
+               {
+                       return new Point(point.X, point.Y);
+               }
+
+               public static SKPoint ToSKPoint(this Point point)
+               {
+                       return new SKPoint((float)point.X, (float)point.Y);
+               }
+
+               // Xamarin.Forms.Point
+
+               public static Size ToFormsSize(this SKSizeI size)
+               {
+                       return new Size(size.Width, size.Height);
+               }
+
+               public static Size ToFormsSize(this SKSize size)
+               {
+                       return new Size(size.Width, size.Height);
+               }
+
+               public static SKSize ToSKSize(this Size size)
+               {
+                       return new SKSize((float)size.Width, (float)size.Height);
+               }
+
+               // Xamarin.Forms.Size
+
+               public static Rectangle ToFormsRect(this SKRectI rect)
+               {
+                       return new Rectangle(rect.Left, rect.Top, rect.Width, rect.Height);
+               }
+
+               public static Rectangle ToFormsRect(this SKRect rect)
+               {
+                       return new Rectangle(rect.Left, rect.Top, rect.Width, rect.Height);
+               }
+
+               public static SKRect ToSKRect(this Rectangle rect)
+               {
+                       return new SKRect((float)rect.Left, (float)rect.Top, (float)rect.Right, (float)rect.Bottom);
+               }
+
+               // Xamarin.Forms.Color
+
+               public static Color ToFormsColor(this SKColor color)
+               {
+                       return new Color(color.Red / 255.0, color.Green / 255.0, color.Blue / 255.0, color.Alpha / 255.0);
+               }
+
+               public static SKColor ToSKColor(this Color color)
+               {
+                       return new SKColor((byte)(color.R * 255), (byte)(color.G * 255), (byte)(color.B * 255), (byte)(color.A * 255));
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/Registrar.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/Registrar.cs
new file mode 100644 (file)
index 0000000..d84dd03
--- /dev/null
@@ -0,0 +1,51 @@
+using System;
+
+namespace SkiaSharp.Views.Forms
+{
+       internal class Registrar
+       {
+               internal static void EnsureRegistered(Type type, Type handler)
+               {
+                       var registered = Xamarin.Forms.Internals.Registrar.Registered;
+                       if (registered.GetHandlerType(type) != null)
+                               return;
+                       registered.Register(type, handler);
+               }
+       }
+
+       public sealed partial class SKImageImageSource
+       {
+               static SKImageImageSource() =>
+                       Registrar.EnsureRegistered(typeof(SKImageImageSource), typeof(SKImageSourceHandler));
+       }
+
+       public sealed partial class SKBitmapImageSource
+       {
+               static SKBitmapImageSource() =>
+                       Registrar.EnsureRegistered(typeof(SKBitmapImageSource), typeof(SKImageSourceHandler));
+       }
+
+       public sealed partial class SKPixmapImageSource
+       {
+               static SKPixmapImageSource() =>
+                       Registrar.EnsureRegistered(typeof(SKPixmapImageSource), typeof(SKImageSourceHandler));
+       }
+
+       public sealed partial class SKPictureImageSource
+       {
+               static SKPictureImageSource() =>
+                       Registrar.EnsureRegistered(typeof(SKPictureImageSource), typeof(SKImageSourceHandler));
+       }
+
+       public partial class SKCanvasView
+       {
+               static SKCanvasView() =>
+                       Registrar.EnsureRegistered(typeof(SKCanvasView), typeof(SKCanvasViewRenderer));
+       }
+
+       public partial class SKGLView
+       {
+               static SKGLView() =>
+                       Registrar.EnsureRegistered(typeof(SKGLView), typeof(SKGLViewRenderer));
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/RendererTypes.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/RendererTypes.cs
new file mode 100644 (file)
index 0000000..5591d86
--- /dev/null
@@ -0,0 +1,9 @@
+using System;
+
+namespace SkiaSharp.Views.Forms
+{
+       public class GetPropertyValueEventArgs<T> : EventArgs
+       {
+               public T Value { get; set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasView.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasView.cs
new file mode 100644 (file)
index 0000000..1e181d5
--- /dev/null
@@ -0,0 +1,110 @@
+using System;
+using Xamarin.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       [RenderWith(typeof(SKCanvasViewRenderer))]
+       public partial class SKCanvasView : View, ISKCanvasViewController
+       {
+               public static readonly BindableProperty IgnorePixelScalingProperty =
+                       BindableProperty.Create(nameof(IgnorePixelScaling), typeof(bool), typeof(SKCanvasView), false);
+
+               public static readonly BindableProperty EnableTouchEventsProperty =
+                       BindableProperty.Create(nameof(EnableTouchEvents), typeof(bool), typeof(SKCanvasView), false);
+
+               // the user can subscribe to repaint
+               public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
+
+               // the user can subscribe to touch events
+               public event EventHandler<SKTouchEventArgs> Touch;
+
+               // the native listens to this event
+               private event EventHandler SurfaceInvalidated;
+               private event EventHandler<GetPropertyValueEventArgs<SKSize>> GetCanvasSize;
+
+               // the user asks the for the size
+               public SKSize CanvasSize
+               {
+                       get
+                       {
+                               // send a mesage to the native view
+                               var args = new GetPropertyValueEventArgs<SKSize>();
+                               GetCanvasSize?.Invoke(this, args);
+                               return args.Value;
+                       }
+               }
+
+               public bool IgnorePixelScaling
+               {
+                       get { return (bool)GetValue(IgnorePixelScalingProperty); }
+                       set { SetValue(IgnorePixelScalingProperty, value); }
+               }
+
+               public bool EnableTouchEvents
+               {
+                       get { return (bool)GetValue(EnableTouchEventsProperty); }
+                       set { SetValue(EnableTouchEventsProperty, value); }
+               }
+
+               // the user asks to repaint
+               public void InvalidateSurface()
+               {
+                       // send a mesage to the native view
+                       SurfaceInvalidated?.Invoke(this, EventArgs.Empty);
+               }
+
+               // the native view tells the user to repaint
+               protected virtual void OnPaintSurface(SKPaintSurfaceEventArgs e)
+               {
+                       PaintSurface?.Invoke(this, e);
+               }
+
+               // the native view responds to a touch
+               protected virtual void OnTouch(SKTouchEventArgs e)
+               {
+                       Touch?.Invoke(this, e);
+               }
+
+               // ISKViewController implementation
+
+               event EventHandler ISKCanvasViewController.SurfaceInvalidated
+               {
+                       add { SurfaceInvalidated += value; }
+                       remove { SurfaceInvalidated -= value; }
+               }
+
+               event EventHandler<GetPropertyValueEventArgs<SKSize>> ISKCanvasViewController.GetCanvasSize
+               {
+                       add { GetCanvasSize += value; }
+                       remove { GetCanvasSize -= value; }
+               }
+
+               void ISKCanvasViewController.OnPaintSurface(SKPaintSurfaceEventArgs e)
+               {
+                       OnPaintSurface(e);
+               }
+
+               void ISKCanvasViewController.OnTouch(SKTouchEventArgs e)
+               {
+                       OnTouch(e);
+               }
+
+               protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
+               {
+                       return new SizeRequest(new Size(40.0, 40.0));
+               }
+       }
+
+       public interface ISKCanvasViewController : IViewController
+       {
+               // the native listens to this event
+               event EventHandler SurfaceInvalidated;
+               event EventHandler<GetPropertyValueEventArgs<SKSize>> GetCanvasSize;
+
+               // the native view tells the user to repaint
+               void OnPaintSurface(SKPaintSurfaceEventArgs e);
+
+               // the native view responds to a touch
+               void OnTouch(SKTouchEventArgs e);
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRenderer.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRenderer.cs
new file mode 100644 (file)
index 0000000..8cd8a59
--- /dev/null
@@ -0,0 +1,13 @@
+using Xamarin.Forms;
+
+using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
+using SKNativeView = SkiaSharp.Views.Tizen.SKCanvasView;
+
+[assembly: ExportRenderer(typeof(SKFormsView), typeof(SkiaSharp.Views.Forms.SKCanvasViewRenderer))]
+
+namespace SkiaSharp.Views.Forms
+{
+       public class SKCanvasViewRenderer : SKCanvasViewRendererBase<SKFormsView, SKNativeView>, IRegisterable
+       {
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRendererBase.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKCanvasViewRendererBase.cs
new file mode 100644 (file)
index 0000000..984e5fb
--- /dev/null
@@ -0,0 +1,147 @@
+using System;
+using System.ComponentModel;
+using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
+using Xamarin.Forms.Platform.Tizen;
+using SKNativeView = SkiaSharp.Views.Tizen.SKCanvasView;
+using SKNativePaintSurfaceEventArgs = SkiaSharp.Views.Tizen.SKPaintSurfaceEventArgs;
+using TForms = Xamarin.Forms.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       public abstract class SKCanvasViewRendererBase<TFormsView, TNativeView> : ViewRenderer<TFormsView, TNativeView>
+               where TFormsView : SKFormsView
+               where TNativeView : SKNativeView
+       {
+               private SKTouchHandler touchHandler;
+
+               protected SKCanvasViewRendererBase()
+               {
+                       Initialize();
+               }
+
+               private void Initialize()
+               {
+                       touchHandler = new SKTouchHandler(
+                               args => ((ISKCanvasViewController)Element).OnTouch(args),
+                               (x, y) => GetScaledCoord(x, y));
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
+               {
+                       if (e.OldElement != null)
+                       {
+                               var oldController = (ISKCanvasViewController)e.OldElement;
+
+                               // unsubscribe from events
+                               oldController.SurfaceInvalidated -= OnSurfaceInvalidated;
+                               oldController.GetCanvasSize -= OnGetCanvasSize;
+                       }
+
+                       if (e.NewElement != null)
+                       {
+                               var newController = (ISKCanvasViewController)e.NewElement;
+
+                               // create the native view
+                               if (Control == null)
+                               {
+                                       var view = CreateNativeControl();
+                                       view.PaintSurface += OnPaintSurface;
+                                       SetNativeControl(view);
+                               }
+
+                               // set the initial values
+                               touchHandler.SetEnabled(Control, e.NewElement.EnableTouchEvents);
+
+                               // TODO: implement this if it is actually supported
+                               Control.IgnorePixelScaling = e.NewElement.IgnorePixelScaling;
+
+                               // subscribe to events from the user
+                               newController.SurfaceInvalidated += OnSurfaceInvalidated;
+                               newController.GetCanvasSize += OnGetCanvasSize;
+
+                               // paint for the first time
+                               OnSurfaceInvalidated(newController, EventArgs.Empty);
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected virtual TNativeView CreateNativeControl()
+               {
+                       TNativeView ret = (TNativeView)Activator.CreateInstance(typeof(TNativeView), new[] { TForms.NativeParent });
+                       return ret;
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       if (e.PropertyName == SKFormsView.IgnorePixelScalingProperty.PropertyName)
+                       {
+                               // TODO: implement this if it is actually supported
+                               Control.IgnorePixelScaling = Element.IgnorePixelScaling;
+                       }
+                       else if (e.PropertyName == SKFormsView.EnableTouchEventsProperty.PropertyName)
+                       {
+                               touchHandler.SetEnabled(Control, Element.EnableTouchEvents);
+                       }
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       // detach all events before disposing
+                       var controller = (ISKCanvasViewController)Element;
+                       if (controller != null)
+                       {
+                               controller.SurfaceInvalidated -= OnSurfaceInvalidated;
+                               controller.GetCanvasSize -= OnGetCanvasSize;
+                       }
+
+                       var control = Control;
+                       if (control != null)
+                       {
+                               control.PaintSurface -= OnPaintSurface;
+                       }
+
+                       // detach, regardless of state
+                       touchHandler.Detach(control);
+
+                       base.Dispose(disposing);
+               }
+
+               private SKPoint GetScaledCoord(double x, double y)
+               {
+                       if (Element.IgnorePixelScaling)
+                       {
+                               x = Tizen.ScalingInfo.FromPixel(x);
+                               y = Tizen.ScalingInfo.FromPixel(y);
+                       }
+                       else
+                       {
+                               // Tizen and Android are the reverse of the other platforms
+                       }
+
+                       return new SKPoint((float)x, (float)y);
+               }
+
+               private void OnPaintSurface(object sender, SKNativePaintSurfaceEventArgs e)
+               {
+                       var controller = Element as ISKCanvasViewController;
+
+                       // the control is being repainted, let the user know
+                       controller?.OnPaintSurface(new SKPaintSurfaceEventArgs(e.Surface, e.Info));
+               }
+
+               private void OnSurfaceInvalidated(object sender, EventArgs eventArgs)
+               {
+                       // repaint the native control
+                       Control.Invalidate();
+               }
+
+               // the user asked for the size
+               private void OnGetCanvasSize(object sender, GetPropertyValueEventArgs<SKSize> e)
+               {
+                       e.Value = Control?.CanvasSize ?? SKSize.Empty;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLView.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLView.cs
new file mode 100644 (file)
index 0000000..9bbb5a2
--- /dev/null
@@ -0,0 +1,130 @@
+using System;
+using Xamarin.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       [RenderWith(typeof(SKGLViewRenderer))]
+       public partial class SKGLView : View, ISKGLViewController
+       {
+               public static readonly BindableProperty HasRenderLoopProperty =
+                       BindableProperty.Create(nameof(HasRenderLoop), typeof(bool), typeof(SKGLView), false);
+
+               public static readonly BindableProperty EnableTouchEventsProperty =
+                       BindableProperty.Create(nameof(EnableTouchEvents), typeof(bool), typeof(SKGLView), false);
+
+               public bool HasRenderLoop
+               {
+                       get { return (bool)GetValue(HasRenderLoopProperty); }
+                       set { SetValue(HasRenderLoopProperty, value); }
+               }
+
+               public bool EnableTouchEvents
+               {
+                       get { return (bool)GetValue(EnableTouchEventsProperty); }
+                       set { SetValue(EnableTouchEventsProperty, value); }
+               }
+
+               // the user can subscribe to repaint
+               public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface;
+
+               // the user can subscribe to touch events
+               public event EventHandler<SKTouchEventArgs> Touch;
+
+               // the native listens to this event
+               private event EventHandler SurfaceInvalidated;
+               private event EventHandler<GetPropertyValueEventArgs<SKSize>> GetCanvasSize;
+               private event EventHandler<GetPropertyValueEventArgs<GRContext>> GetGRContext;
+
+               // the user asks the for the size
+               public SKSize CanvasSize
+               {
+                       get
+                       {
+                               // send a mesage to the native view
+                               var args = new GetPropertyValueEventArgs<SKSize>();
+                               GetCanvasSize?.Invoke(this, args);
+                               return args.Value;
+                       }
+               }
+
+               // the user asks the for the current GRContext
+               public GRContext GRContext
+               {
+                       get
+                       {
+                               // send a mesage to the native view
+                               var args = new GetPropertyValueEventArgs<GRContext>();
+                               GetGRContext?.Invoke(this, args);
+                               return args.Value;
+                       }
+               }
+
+               // the user asks to repaint
+               public void InvalidateSurface()
+               {
+                       // send a mesage to the native view
+                       SurfaceInvalidated?.Invoke(this, EventArgs.Empty);
+               }
+
+               // the native view tells the user to repaint
+               protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e)
+               {
+                       PaintSurface?.Invoke(this, e);
+               }
+
+               // the native view responds to a touch
+               protected virtual void OnTouch(SKTouchEventArgs e)
+               {
+                       Touch?.Invoke(this, e);
+               }
+
+               // ISKViewController implementation
+
+               event EventHandler ISKGLViewController.SurfaceInvalidated
+               {
+                       add { SurfaceInvalidated += value; }
+                       remove { SurfaceInvalidated -= value; }
+               }
+
+               event EventHandler<GetPropertyValueEventArgs<SKSize>> ISKGLViewController.GetCanvasSize
+               {
+                       add { GetCanvasSize += value; }
+                       remove { GetCanvasSize -= value; }
+               }
+
+               event EventHandler<GetPropertyValueEventArgs<GRContext>> ISKGLViewController.GetGRContext
+               {
+                       add { GetGRContext += value; }
+                       remove { GetGRContext -= value; }
+               }
+
+               void ISKGLViewController.OnPaintSurface(SKPaintGLSurfaceEventArgs e)
+               {
+                       OnPaintSurface(e);
+               }
+
+               void ISKGLViewController.OnTouch(SKTouchEventArgs e)
+               {
+                       OnTouch(e);
+               }
+
+               protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
+               {
+                       return new SizeRequest(new Size(40.0, 40.0));
+               }
+       }
+
+       public interface ISKGLViewController : IViewController
+       {
+               // the native listens to this event
+               event EventHandler SurfaceInvalidated;
+               event EventHandler<GetPropertyValueEventArgs<SKSize>> GetCanvasSize;
+               event EventHandler<GetPropertyValueEventArgs<GRContext>> GetGRContext;
+
+               // the native view tells the user to repaint
+               void OnPaintSurface(SKPaintGLSurfaceEventArgs e);
+
+               // the native view responds to a touch
+               void OnTouch(SKTouchEventArgs e);
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRenderer.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRenderer.cs
new file mode 100644 (file)
index 0000000..734db3e
--- /dev/null
@@ -0,0 +1,23 @@
+using SkiaSharp.Views.Tizen;
+using Xamarin.Forms;
+
+using SKFormsView = SkiaSharp.Views.Forms.SKGLView;
+using SKNativeView = SkiaSharp.Views.Tizen.SKGLSurfaceView;
+
+[assembly: ExportRenderer(typeof(SKFormsView), typeof(SkiaSharp.Views.Forms.SKGLViewRenderer))]
+
+namespace SkiaSharp.Views.Forms
+{
+       public class SKGLViewRenderer : SKGLViewRendererBase<SKFormsView, SKNativeView>
+       {
+               protected override void SetupRenderLoop(bool oneShot)
+               {
+                       if (oneShot)
+                       {
+                               Control.Invalidate();
+                       }
+
+                       Control.RenderingMode = Element.HasRenderLoop ? RenderingMode.Continuously : RenderingMode.WhenDirty;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRendererBase.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKGLViewRendererBase.cs
new file mode 100644 (file)
index 0000000..b1ca97e
--- /dev/null
@@ -0,0 +1,153 @@
+using System;
+using System.ComponentModel;
+using Xamarin.Forms;
+using SKFormsView = SkiaSharp.Views.Forms.SKGLView;
+using Xamarin.Forms.Platform.Tizen;
+using SKNativeView = SkiaSharp.Views.Tizen.SKGLSurfaceView;
+using SKNativePaintGLSurfaceEventArgs = SkiaSharp.Views.Tizen.SKPaintGLSurfaceEventArgs;
+using TForms = Xamarin.Forms.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       public abstract class SKGLViewRendererBase<TFormsView, TNativeView> : ViewRenderer<TFormsView, TNativeView>
+               where TFormsView : SKFormsView
+               where TNativeView : SKNativeView
+       {
+               private SKTouchHandler touchHandler;
+
+               protected SKGLViewRendererBase()
+               {
+                       Initialize();
+               }
+
+               private void Initialize()
+               {
+                       touchHandler = new SKTouchHandler(
+                               args => ((ISKGLViewController)Element).OnTouch(args),
+                               (x, y) => GetScaledCoord(x, y));
+               }
+
+               public GRContext GRContext => Control.GRContext;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
+               {
+                       if (e.OldElement != null)
+                       {
+                               var oldController = (ISKGLViewController)e.OldElement;
+
+                               // unsubscribe from events
+                               oldController.SurfaceInvalidated -= OnSurfaceInvalidated;
+                               oldController.GetCanvasSize -= OnGetCanvasSize;
+                               oldController.GetGRContext -= OnGetGRContext;
+                       }
+
+                       if (e.NewElement != null)
+                       {
+                               var newController = (ISKGLViewController)e.NewElement;
+
+                               // create the native view
+                               if (Control == null)
+                               {
+                                       var view = CreateNativeControl();
+                                       view.PaintSurface += OnPaintSurface;
+                                       SetNativeControl(view);
+                               }
+
+                               touchHandler.SetEnabled(Control, e.NewElement.EnableTouchEvents);
+
+                               // subscribe to events from the user
+                               newController.SurfaceInvalidated += OnSurfaceInvalidated;
+                               newController.GetCanvasSize += OnGetCanvasSize;
+                               newController.GetGRContext += OnGetGRContext;
+
+                               // start the rendering
+                               SetupRenderLoop(false);
+                       }
+
+                       base.OnElementChanged(e);
+               }
+
+               protected virtual TNativeView CreateNativeControl()
+               {
+                       TNativeView ret = (TNativeView)Activator.CreateInstance(typeof(TNativeView), new[] { TForms.NativeParent });
+                       return ret;
+               }
+
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       base.OnElementPropertyChanged(sender, e);
+
+                       // refresh the render loop
+                       if (e.PropertyName == SKFormsView.HasRenderLoopProperty.PropertyName)
+                       {
+                               SetupRenderLoop(false);
+                       }
+                       else if (e.PropertyName == SKFormsView.EnableTouchEventsProperty.PropertyName)
+                       {
+                               touchHandler.SetEnabled(Control, Element.EnableTouchEvents);
+                       }
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       // detach all events before disposing
+                       var controller = (ISKGLViewController)Element;
+                       if (controller != null)
+                       {
+                               controller.SurfaceInvalidated -= OnSurfaceInvalidated;
+                               controller.GetCanvasSize -= OnGetCanvasSize;
+                               controller.GetGRContext -= OnGetGRContext;
+                       }
+
+                       var control = Control;
+                       if (control != null)
+                       {
+                               control.PaintSurface -= OnPaintSurface;
+                       }
+
+                       // detach, regardless of state
+                       touchHandler.Detach(control);
+
+                       base.Dispose(disposing);
+               }
+
+               protected abstract void SetupRenderLoop(bool oneShot);
+
+               private SKPoint GetScaledCoord(double x, double y)
+               {
+                       // Android and Tizen are the reverse of the other platforms
+                       return new SKPoint((float)x, (float)y);
+               }
+
+
+               // the user asked to repaint
+               private void OnSurfaceInvalidated(object sender, EventArgs eventArgs)
+               {
+                       // if we aren't in a loop, then refresh once
+                       if (!Element.HasRenderLoop)
+                       {
+                               SetupRenderLoop(true);
+                       }
+               }
+
+               // the user asked for the size
+               private void OnGetCanvasSize(object sender, GetPropertyValueEventArgs<SKSize> e)
+               {
+                       e.Value = Control?.CanvasSize ?? SKSize.Empty;
+               }
+
+               // the user asked for the current GRContext
+               private void OnGetGRContext(object sender, GetPropertyValueEventArgs<GRContext> e)
+               {
+                       e.Value = Control?.GRContext;
+               }
+
+               private void OnPaintSurface(object sender, SKNativePaintGLSurfaceEventArgs e)
+               {
+                       var controller = Element as ISKGLViewController;
+
+                       // the control is being repainted, let the user know
+                       controller?.OnPaintSurface(new SKPaintGLSurfaceEventArgs(e.Surface, e.BackendRenderTarget));
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSource.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSource.cs
new file mode 100644 (file)
index 0000000..6d87914
--- /dev/null
@@ -0,0 +1,149 @@
+using System.Threading.Tasks;
+using Xamarin.Forms;
+
+namespace SkiaSharp.Views.Forms
+{
+       public sealed partial class SKImageImageSource : ImageSource
+       {
+               public static readonly BindableProperty ImageProperty = BindableProperty.Create(nameof(Image), typeof(SKImage), typeof(SKImageImageSource), default(SKImage));
+
+               public SKImage Image
+               {
+                       get { return (SKImage)GetValue(ImageProperty); }
+                       set { SetValue(ImageProperty, value); }
+               }
+
+               public override Task<bool> Cancel()
+               {
+                       return Task.FromResult(false);
+               }
+
+               public static implicit operator SKImageImageSource(SKImage image)
+               {
+                       return new SKImageImageSource
+                       {
+                               Image = image
+                       };
+               }
+
+               public static implicit operator SKImage(SKImageImageSource source)
+               {
+                       return source?.Image;
+               }
+
+               protected override void OnPropertyChanged(string propertyName = null)
+               {
+                       if (propertyName == ImageProperty.PropertyName)
+                               OnSourceChanged();
+                       base.OnPropertyChanged(propertyName);
+               }
+       }
+
+       public sealed partial class SKBitmapImageSource : ImageSource
+       {
+               public static readonly BindableProperty BitmapProperty = BindableProperty.Create(nameof(Bitmap), typeof(SKBitmap), typeof(SKBitmapImageSource), default(SKBitmap));
+
+               public SKBitmap Bitmap
+               {
+                       get { return (SKBitmap)GetValue(BitmapProperty); }
+                       set { SetValue(BitmapProperty, value); }
+               }
+
+               public override Task<bool> Cancel()
+               {
+                       return Task.FromResult(false);
+               }
+
+               public static implicit operator SKBitmapImageSource(SKBitmap bitmap)
+               {
+                       return new SKBitmapImageSource
+                       {
+                               Bitmap = bitmap
+                       };
+               }
+
+               public static implicit operator SKBitmap(SKBitmapImageSource source)
+               {
+                       return source?.Bitmap;
+               }
+
+               protected override void OnPropertyChanged(string propertyName = null)
+               {
+                       if (propertyName == BitmapProperty.PropertyName)
+                               OnSourceChanged();
+                       base.OnPropertyChanged(propertyName);
+               }
+       }
+
+       public sealed partial class SKPixmapImageSource : ImageSource
+       {
+               public static readonly BindableProperty PixmapProperty = BindableProperty.Create(nameof(Pixmap), typeof(SKPixmap), typeof(SKPixmapImageSource), default(SKPixmap));
+
+               public SKPixmap Pixmap
+               {
+                       get { return (SKPixmap)GetValue(PixmapProperty); }
+                       set { SetValue(PixmapProperty, value); }
+               }
+
+               public override Task<bool> Cancel()
+               {
+                       return Task.FromResult(false);
+               }
+
+               public static implicit operator SKPixmapImageSource(SKPixmap pixmap)
+               {
+                       return new SKPixmapImageSource
+                       {
+                               Pixmap = pixmap
+                       };
+               }
+
+               public static implicit operator SKPixmap(SKPixmapImageSource source)
+               {
+                       return source?.Pixmap;
+               }
+
+               protected override void OnPropertyChanged(string propertyName = null)
+               {
+                       if (propertyName == PixmapProperty.PropertyName)
+                               OnSourceChanged();
+                       base.OnPropertyChanged(propertyName);
+               }
+       }
+
+       public sealed partial class SKPictureImageSource : ImageSource
+       {
+               public static readonly BindableProperty PictureProperty = BindableProperty.Create(nameof(Picture), typeof(SKPicture), typeof(SKPictureImageSource), default(SKPicture));
+
+               public static readonly BindableProperty DimensionsProperty = BindableProperty.Create(nameof(Dimensions), typeof(SKSizeI), typeof(SKPictureImageSource), default(SKSizeI));
+
+               public SKPicture Picture
+               {
+                       get { return (SKPicture)GetValue(PictureProperty); }
+                       set { SetValue(PictureProperty, value); }
+               }
+
+               public SKSizeI Dimensions
+               {
+                       get { return (SKSizeI)GetValue(DimensionsProperty); }
+                       set { SetValue(DimensionsProperty, value); }
+               }
+
+               public override Task<bool> Cancel()
+               {
+                       return Task.FromResult(false);
+               }
+
+               public static explicit operator SKPicture(SKPictureImageSource source)
+               {
+                       return source?.Picture;
+               }
+
+               protected override void OnPropertyChanged(string propertyName = null)
+               {
+                       if (propertyName == PictureProperty.PropertyName)
+                               OnSourceChanged();
+                       base.OnPropertyChanged(propertyName);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSourceHandler.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKImageSourceHandler.cs
new file mode 100644 (file)
index 0000000..05f0b17
--- /dev/null
@@ -0,0 +1,48 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Tizen;
+
+using ElmImage = ElmSharp.Image;
+
+[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKImageImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKBitmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPixmapImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(SkiaSharp.Views.Forms.SKPictureImageSource), typeof(SkiaSharp.Views.Forms.SKImageSourceHandler))]
+
+namespace SkiaSharp.Views.Forms
+{
+       public sealed class SKImageSourceHandler : IImageSourceHandler
+       {
+               private StreamImageSourceHandler handler = new StreamImageSourceHandler();
+
+               public Task<bool> LoadImageAsync(ElmImage image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+               {
+                       ImageSource newSource = null;
+
+                       switch (imageSource)
+                       {
+                               case SKImageImageSource imageImageSource:
+                                       newSource = ImageSource.FromStream(() => ToStream(imageImageSource.Image));
+                                       break;
+                               case SKBitmapImageSource bitmapImageSource:
+                                       newSource = ImageSource.FromStream(() => ToStream(SKImage.FromBitmap(bitmapImageSource.Bitmap)));
+                                       break;
+                               case SKPixmapImageSource pixmapImageSource:
+                                       newSource = ImageSource.FromStream(() => ToStream(SKImage.FromPixels(pixmapImageSource.Pixmap)));
+                                       break;
+                               case SKPictureImageSource pictureImageSource:
+                                       newSource = ImageSource.FromStream(() => ToStream(SKImage.FromPicture(pictureImageSource.Picture, pictureImageSource.Dimensions)));
+                                       break;
+                       }
+
+                       return handler.LoadImageAsync(image, newSource, cancelationToken);
+               }
+
+               private static Stream ToStream(SKImage skiaImage)
+               {
+                       return skiaImage.Encode().AsStream();
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintGLSurfaceEventArgs.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintGLSurfaceEventArgs.cs
new file mode 100644 (file)
index 0000000..f6ef83a
--- /dev/null
@@ -0,0 +1,74 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp.Views.Forms
+{
+       public class SKPaintGLSurfaceEventArgs : EventArgs
+       {
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               private GRBackendRenderTargetDesc? rtDesc;
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete("Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, SKColorType, GRSurfaceOrigin) instead.")]
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTargetDesc renderTarget)
+               {
+                       Surface = surface;
+                       rtDesc = renderTarget;
+                       BackendRenderTarget = new GRBackendRenderTarget(GRBackend.OpenGL, renderTarget);
+                       ColorType = renderTarget.Config.ToColorType();
+                       Origin = renderTarget.Origin;
+               }
+
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget)
+                       : this(surface, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888)
+               {
+               }
+
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType)
+               {
+                       Surface = surface;
+                       BackendRenderTarget = renderTarget;
+                       ColorType = colorType;
+                       Origin = origin;
+               }
+
+               public SKSurface Surface { get; private set; }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete("Use BackendRenderTarget instead.")]
+               public GRBackendRenderTargetDesc RenderTarget
+               {
+                       get
+                       {
+                               if (!rtDesc.HasValue)
+                               {
+                                       var rth = IntPtr.Zero;
+                                       if (BackendRenderTarget.GetGlFramebufferInfo(out var glInfo))
+                                       {
+                                               rth = (IntPtr)glInfo.FramebufferObjectId;
+                                       }
+
+                                       rtDesc = new GRBackendRenderTargetDesc
+                                       {
+                                               Width = BackendRenderTarget.Width,
+                                               Height = BackendRenderTarget.Height,
+                                               RenderTargetHandle = rth,
+                                               SampleCount = BackendRenderTarget.SampleCount,
+                                               StencilBits = BackendRenderTarget.StencilBits,
+                                               Config = ColorType.ToPixelConfig(),
+                                               Origin = Origin,
+                                       };
+                               }
+
+                               return rtDesc.Value;
+                       }
+               }
+
+               public GRBackendRenderTarget BackendRenderTarget { get; private set; }
+
+               public SKColorType ColorType { get; private set; }
+
+               public GRSurfaceOrigin Origin { get; private set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintSurfaceEventArgs.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKPaintSurfaceEventArgs.cs
new file mode 100644 (file)
index 0000000..4716c38
--- /dev/null
@@ -0,0 +1,17 @@
+using System;
+
+namespace SkiaSharp.Views.Forms
+{
+       public class SKPaintSurfaceEventArgs : EventArgs
+       {
+               public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info)
+               {
+                       Surface = surface;
+                       Info = info;
+               }
+
+               public SKSurface Surface { get; private set; }
+
+               public SKImageInfo Info { get; private set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchEventArgs.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchEventArgs.cs
new file mode 100644 (file)
index 0000000..55e3baf
--- /dev/null
@@ -0,0 +1,84 @@
+using System;
+
+namespace SkiaSharp.Views.Forms
+{
+       public class SKTouchEventArgs : EventArgs
+       {
+               public SKTouchEventArgs(long id, SKTouchAction type, SKPoint location, bool inContact)
+                       : this(id, type, SKMouseButton.Left, SKTouchDeviceType.Touch, location, inContact, 0, 1)
+               {
+               }
+
+               public SKTouchEventArgs(long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SKPoint location, bool inContact)
+                       : this(id, type, mouseButton, deviceType, location, inContact, 0, 1)
+               {
+               }
+
+               public SKTouchEventArgs(long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SKPoint location, bool inContact, int wheelDelta)
+                       : this(id, type, mouseButton, deviceType, location, inContact, wheelDelta, 1)
+               {
+               }
+
+               public SKTouchEventArgs(long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SKPoint location, bool inContact, int wheelDelta, float pressure)
+               {
+                       Id = id;
+                       ActionType = type;
+                       DeviceType = deviceType;
+                       MouseButton = mouseButton;
+                       Location = location;
+                       InContact = inContact;
+                       WheelDelta = wheelDelta;
+                       Pressure = pressure;
+               }
+
+               public bool Handled { get; set; }
+
+               public long Id { get; private set; }
+
+               public SKTouchAction ActionType { get; private set; }
+
+               public SKTouchDeviceType DeviceType { get; private set; }
+
+               public SKMouseButton MouseButton { get; private set; }
+
+               public SKPoint Location { get; private set; }
+
+               public bool InContact { get; private set; }
+
+               public int WheelDelta { get; private set; }
+
+               public float Pressure { get; private set; }
+
+               public override string ToString()
+               {
+                       return $"{{ActionType={ActionType}, DeviceType={DeviceType}, Handled={Handled}, Id={Id}, InContact={InContact}, Location={Location}, MouseButton={MouseButton}, WheelDelta={WheelDelta}, Pressure={Pressure}}}";
+               }
+       }
+
+       public enum SKTouchAction
+       {
+               Entered,
+               Pressed,
+               Moved,
+               Released,
+               Cancelled,
+               Exited,
+               WheelChanged,
+       }
+
+       public enum SKTouchDeviceType
+       {
+               Touch,
+               Mouse,
+               Pen
+       }
+
+       public enum SKMouseButton
+       {
+               Unknown,
+
+               Left,
+               Middle,
+               Right
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchHandler.cs b/src/XSF/SkiaSharp.Views.Forms.Tizen/SKTouchHandler.cs
new file mode 100644 (file)
index 0000000..20124bb
--- /dev/null
@@ -0,0 +1,121 @@
+using System;
+using ElmSharp;
+
+namespace SkiaSharp.Views.Forms
+{
+       internal class SKTouchHandler
+       {
+               private readonly MomentumHandler momentumHandler;
+               private Action<SKTouchEventArgs> onTouchAction;
+               private Func<double, double, SKPoint> scalePixels;
+               private GestureLayer gestureLayer;
+
+               public SKTouchHandler(Action<SKTouchEventArgs> onTouchAction, Func<double, double, SKPoint> scalePixels)
+               {
+                       this.onTouchAction = onTouchAction;
+                       this.scalePixels = scalePixels;
+
+                       momentumHandler = new MomentumHandler(this);
+               }
+
+               public void SetEnabled(EvasObject view, bool enableTouchEvents)
+               {
+                       if (view != null)
+                       {
+                               if (enableTouchEvents)
+                                       CreateGestureLayer(view);
+                               else
+                                       DestroyGestureLayer();
+                       }
+               }
+
+               public void Detach(EvasObject view)
+               {
+                       // clean the view
+                       SetEnabled(view, false);
+
+                       // remove references
+                       onTouchAction = null;
+                       scalePixels = null;
+               }
+
+               private void CreateGestureLayer(EvasObject parent)
+               {
+                       if (gestureLayer == null)
+                       {
+                               gestureLayer = new GestureLayer(parent);
+                               gestureLayer.Attach(parent);
+                               gestureLayer.Deleted += (s, e) =>
+                               {
+                                       gestureLayer = null;
+                                       DestroyGestureLayer();
+                               };
+                               gestureLayer.IsEnabled = true;
+
+                               AddMomentumGesture();
+                       }
+               }
+
+               private void DestroyGestureLayer()
+               {
+                       if (gestureLayer != null)
+                       {
+                               gestureLayer.IsEnabled = false;
+                               gestureLayer.Unrealize();
+                               gestureLayer = null;
+                       }
+               }
+
+               private void AddMomentumGesture()
+               {
+                       gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Start, (data) => { momentumHandler.OnStarted(); });
+                       gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Move, (data) => { momentumHandler.OnMoved(); });
+                       gestureLayer.SetMomentumCallback(GestureLayer.GestureState.End, (data) => { momentumHandler.OnFinished(); });
+                       gestureLayer.SetMomentumCallback(GestureLayer.GestureState.Abort, (data) => { momentumHandler.OnAborted(); });
+               }
+
+               private class MomentumHandler
+               {
+                       private readonly SKTouchHandler handler;
+                       private int currentId = 0;
+
+                       public MomentumHandler(SKTouchHandler h)
+                       {
+                               handler = h;
+                       }
+
+                       public void OnStarted()
+                       {
+                               ++currentId;
+                               PostEvent(SKTouchAction.Pressed);
+                       }
+
+                       public void OnMoved()
+                       {
+                               PostEvent(SKTouchAction.Moved);
+                       }
+
+                       public void OnFinished()
+                       {
+                               PostEvent(SKTouchAction.Released);
+                       }
+
+                       public void OnAborted()
+                       {
+                               PostEvent(SKTouchAction.Cancelled);
+                       }
+
+                       private void PostEvent(SKTouchAction action)
+                       {
+                               if (handler.onTouchAction == null || handler.scalePixels == null)
+                                       return;
+
+                               var p = handler.gestureLayer.EvasCanvas.Pointer;
+                               var coords = handler.scalePixels(p.X, p.Y);
+                               var inContact = (action == SKTouchAction.Pressed || action == SKTouchAction.Moved) ? true : false;
+
+                               handler.onTouchAction(new SKTouchEventArgs(currentId, action, coords, inContact));
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/CustomRenderingView.cs b/src/XSF/SkiaSharp.Views.Tizen/CustomRenderingView.cs
new file mode 100644 (file)
index 0000000..ccafd29
--- /dev/null
@@ -0,0 +1,150 @@
+using System;
+using ElmSharp;
+using SkiaSharp.Views.Tizen.Interop;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public abstract class CustomRenderingView : Widget
+       {
+               private readonly Evas.ImagePixelsSetCallback redrawCallback;
+
+               private IntPtr animator;
+               private RenderingMode renderingMode = RenderingMode.WhenDirty;
+
+               protected IntPtr evasImage;
+
+               public CustomRenderingView(EvasObject parent)
+                       : base(parent)
+               {
+                       Resized += (sender, e) => OnResized();
+                       redrawCallback = (d, o) => OnDrawFrame();
+               }
+
+               public SKSize CanvasSize => GetSurfaceSize();
+
+               public RenderingMode RenderingMode
+               {
+                       get { return renderingMode; }
+                       set
+                       {
+                               if (renderingMode != value)
+                               {
+                                       renderingMode = value;
+
+                                       if (renderingMode == RenderingMode.Continuously)
+                                               CreateAnimator();
+                                       else
+                                               DestroyAnimator();
+                               }
+                       }
+               }
+
+               public void Invalidate()
+               {
+                       if (RenderingMode == RenderingMode.WhenDirty)
+                               Evas.evas_object_image_pixels_dirty_set(evasImage, true);
+               }
+
+               protected virtual void CreateNativeResources(EvasObject parent)
+               {
+                       // empty on purpose
+               }
+
+               protected virtual void DestroyNativeResources()
+               {
+                       // empty on purpose
+               }
+
+               protected abstract void OnDrawFrame();
+
+               protected abstract bool UpdateSurfaceSize(Rect geometry);
+
+               protected abstract SKSizeI GetSurfaceSize();
+
+               protected virtual void CreateDrawingSurface()
+               {
+                       // empty on purpose
+               }
+
+               protected virtual void DestroyDrawingSurface()
+               {
+                       // empty on purpose
+               }
+
+               protected sealed override IntPtr CreateHandle(EvasObject parent)
+               {
+                       var handle = Interop.Elementary.elm_layout_add(parent);
+                       Interop.Elementary.elm_layout_theme_set(handle, "layout", "background", "default");
+
+                       evasImage = Evas.evas_object_image_filled_add(Interop.Evas.evas_object_evas_get(handle));
+                       Evas.evas_object_image_colorspace_set(evasImage, Evas.Colorspace.ARGB8888);
+                       Evas.evas_object_image_smooth_scale_set(evasImage, true);
+                       Evas.evas_object_image_alpha_set(evasImage, true);
+
+                       Interop.Elementary.elm_object_part_content_set(handle, "elm.swallow.content", evasImage);
+
+                       CreateNativeResources(parent);
+
+                       return handle;
+               }
+
+               protected sealed override void OnUnrealize()
+               {
+                       DestroyAnimator();
+                       DestroyDrawingSurface();
+                       DestroyNativeResources();
+
+                       base.OnUnrealize();
+               }
+
+               protected void OnResized()
+               {
+                       var geometry = Geometry;
+
+                       // control is not yet fully initialized
+                       if (geometry.Width <= 0 || geometry.Height <= 0)
+                               return;
+
+                       if (UpdateSurfaceSize(geometry))
+                       {
+                               // disconnect the callback
+                               Evas.evas_object_image_native_surface_set(evasImage, IntPtr.Zero);
+
+                               // recreate the drawing surface to match the new size
+                               DestroyDrawingSurface();
+
+                               var size = GetSurfaceSize();
+                               Evas.evas_object_image_size_set(evasImage, size.Width, size.Height);
+
+                               CreateDrawingSurface();
+
+                               // set the image callback; will be invoked when image is marked as dirty
+                               Evas.evas_object_image_pixels_get_callback_set(evasImage, redrawCallback, IntPtr.Zero);
+
+                               // repaint
+                               Invalidate();
+                       }
+               }
+
+               private void CreateAnimator()
+               {
+                       if (animator == IntPtr.Zero)
+                       {
+                               animator = EcoreAnimator.AddAnimator(() =>
+                               {
+                                       Evas.evas_object_image_pixels_dirty_set(evasImage, true);
+                                       return true;
+                               });
+                       }
+               }
+
+               private void DestroyAnimator()
+               {
+                       if (animator != IntPtr.Zero)
+                       {
+                               EcoreAnimator.RemoveAnimator(animator);
+                               animator = IntPtr.Zero;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/Extensions.cs b/src/XSF/SkiaSharp.Views.Tizen/Extensions.cs
new file mode 100644 (file)
index 0000000..ed8783b
--- /dev/null
@@ -0,0 +1,27 @@
+using System;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public static class Extensions
+       {
+               private static readonly Lazy<bool> isValidEnvironment = new Lazy<bool>(() =>
+               {
+                       try
+                       {
+                               // test an operation that requires the native library
+                               SKPMColor.PreMultiply(SKColors.Black);
+                               return true;
+                       }
+                       catch (DllNotFoundException)
+                       {
+                               // If we can't load the native library,
+                               // we may be in some designer.
+                               // We can make this assumption since any other member will fail
+                               // at some point in the draw operation.
+                               return false;
+                       }
+               });
+
+               internal static bool IsValidEnvironment => isValidEnvironment.Value;
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/GlesInterop/Gles.cs b/src/XSF/SkiaSharp.Views.Tizen/GlesInterop/Gles.cs
new file mode 100644 (file)
index 0000000..4411e84
--- /dev/null
@@ -0,0 +1,95 @@
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp.Views.GlesInterop
+{
+       internal static class Gles
+       {
+               private const string libGLESv2 = "libGLESv2.so";
+
+               public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
+               public const int GL_RENDERBUFFER_BINDING = 0x8CA7;
+
+               public const int GL_BGRA8_EXT = 0x93A1;
+               public const int GL_VERSION = 0x1F02;
+               public const int GL_EXTENSIONS = 0x1F03;
+
+               // GetPName
+               public const int GL_SUBPIXEL_BITS = 0x0D50;
+               public const int GL_RED_BITS = 0x0D52;
+               public const int GL_GREEN_BITS = 0x0D53;
+               public const int GL_BLUE_BITS = 0x0D54;
+               public const int GL_ALPHA_BITS = 0x0D55;
+               public const int GL_DEPTH_BITS = 0x0D56;
+               public const int GL_STENCIL_BITS = 0x0D57;
+               public const int GL_SAMPLES = 0x80A9;
+
+               // ClearBufferMask
+               public const int GL_DEPTH_BUFFER_BIT = 0x00000100;
+               public const int GL_STENCIL_BUFFER_BIT = 0x00000400;
+               public const int GL_COLOR_BUFFER_BIT = 0x00004000;
+
+               public const int GL_NEAREST = 0x2600;
+
+               public const int GL_READ_FRAMEBUFFER_ANGLE = 0x8CA8;
+               public const int GL_DRAW_FRAMEBUFFER_ANGLE = 0x8CA9;
+               public const int GL_DRAW_FRAMEBUFFER_BINDING_ANGLE = 0x8CA6;
+               public const int GL_READ_FRAMEBUFFER_BINDING_ANGLE = 0x8CAA;
+
+               // Framebuffer Object
+               public const int GL_FRAMEBUFFER = 0x8D40;
+               public const int GL_RENDERBUFFER = 0x8D41;
+
+               public const int GL_RENDERBUFFER_WIDTH = 0x8D42;
+               public const int GL_RENDERBUFFER_HEIGHT = 0x8D43;
+               public const int GL_RENDERBUFFER_INTERNAL_FORMAT = 0x8D44;
+               public const int GL_RENDERBUFFER_RED_SIZE = 0x8D50;
+               public const int GL_RENDERBUFFER_GREEN_SIZE = 0x8D51;
+               public const int GL_RENDERBUFFER_BLUE_SIZE = 0x8D52;
+               public const int GL_RENDERBUFFER_ALPHA_SIZE = 0x8D53;
+               public const int GL_RENDERBUFFER_DEPTH_SIZE = 0x8D54;
+               public const int GL_RENDERBUFFER_STENCIL_SIZE = 0x8D55;
+               public const int GL_COLOR_ATTACHMENT0 = 0x8CE0;
+               public const int GL_DEPTH_ATTACHMENT = 0x8D00;
+               public const int GL_STENCIL_ATTACHMENT = 0x8D20;
+
+               public const int GL_DEPTH_COMPONENT16 = 0x81A5;
+               public const int GL_DEPTH_STENCIL_OES = 0x84F9;
+               public const int GL_UNSIGNED_INT_24_8_OES = 0x84FA;
+               public const int GL_DEPTH24_STENCIL8_OES = 0x88F0;
+
+               [DllImport(libGLESv2)]
+               public static extern void glGenRenderbuffers(int n, [In, Out] uint[] buffers);
+               [DllImport(libGLESv2)]
+               public static extern void glGenRenderbuffers(int n, ref uint buffer);
+               [DllImport(libGLESv2)]
+               public static extern void glGenFramebuffers(int n, [In, Out] uint[] buffers);
+               [DllImport(libGLESv2)]
+               public static extern void glGenFramebuffers(int n, ref uint buffer);
+               [DllImport(libGLESv2)]
+               public static extern void glGetIntegerv(uint pname, out int data);
+               [DllImport(libGLESv2)]
+               public static extern void glGetRenderbufferParameteriv(uint target, int pname, out int param);
+               [DllImport(libGLESv2)]
+               public static extern void glBindRenderbuffer(uint target, uint buffer);
+               [DllImport(libGLESv2)]
+               public static extern void glViewport(int x, int y, int width, int height);
+               [DllImport(libGLESv2)]
+               public static extern void glClearColor(float red, float green, float blue, float alpha);
+               [DllImport(libGLESv2)]
+               public static extern void glClear(uint mask);
+               [DllImport(libGLESv2)]
+               public static extern void glBindFramebuffer(uint target, uint framebuffer);
+               [DllImport(libGLESv2)]
+               public static extern void glDeleteFramebuffers(int n, [In, Out] uint[] framebuffers);
+               [DllImport(libGLESv2)]
+               public static extern void glDeleteFramebuffers(int n, ref uint framebuffer);
+               [DllImport(libGLESv2)]
+               public static extern void glDeleteRenderbuffers(int n, [In, Out] uint[] renderbuffers);
+               [DllImport(libGLESv2)]
+               public static extern void glDeleteRenderbuffers(int n, ref uint renderbuffer);
+               [DllImport(libGLESv2)]
+               public static extern void glFramebufferRenderbuffer(uint target, uint attachment, uint renderbuffertarget, uint renderbuffer);
+               [DllImport(libGLESv2)]
+               public static extern System.IntPtr glGetString(uint value);
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/SkiaSharp.Views.Tizen/Interop/Elementary.cs b/src/XSF/SkiaSharp.Views.Tizen/Interop/Elementary.cs
new file mode 100644 (file)
index 0000000..d49026b
--- /dev/null
@@ -0,0 +1,23 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp.Views.Tizen.Interop
+{
+       internal static class Elementary
+       {
+               [DllImport(Libraries.Elementary)]
+               internal static extern IntPtr elm_layout_add(IntPtr obj);
+
+               [DllImport(Libraries.Elementary)]
+               internal static extern bool elm_layout_theme_set(IntPtr obj, string klass, string group, string style);
+
+               [DllImport(Libraries.Elementary)]
+               internal static extern IntPtr elm_object_part_content_get(IntPtr obj, string part);
+
+               [DllImport(Libraries.Elementary)]
+               internal static extern void elm_object_part_content_set(IntPtr obj, string part, IntPtr content);
+
+               [DllImport(Libraries.Elementary)]
+               internal static extern IntPtr elm_image_object_get(IntPtr obj);
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/Interop/Evas.cs b/src/XSF/SkiaSharp.Views.Tizen/Interop/Evas.cs
new file mode 100644 (file)
index 0000000..cc4fafa
--- /dev/null
@@ -0,0 +1,195 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp.Views.Tizen.Interop
+{
+       internal static class Evas
+       {
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_object_evas_get(IntPtr obj);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_gl_new(IntPtr evas);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_gl_free(IntPtr evas_gl);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_gl_context_create(IntPtr evas_gl, IntPtr share_ctx);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_gl_context_destroy(IntPtr evas_gl, IntPtr ctx);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_gl_surface_create(IntPtr evas_gl, IntPtr config, int width, int height);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_gl_surface_destroy(IntPtr evas_gl, IntPtr surf);
+
+               [DllImport(Libraries.Evas)]
+               [return: MarshalAs(UnmanagedType.U1)]
+               internal static extern bool evas_gl_native_surface_get(IntPtr evas_gl, IntPtr surf, out NativeSurfaceOpenGL ns);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_gl_proc_address_get(IntPtr evas_gl, string name);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_gl_api_get(IntPtr evas_gl);
+
+               [DllImport(Libraries.Evas)]
+               [return: MarshalAs(UnmanagedType.U1)]
+               internal static extern bool evas_gl_make_current(IntPtr evas_gl, IntPtr surf, IntPtr ctx);
+
+               internal struct Config
+               {
+                       public ColorFormat color_format;
+                       public DepthBits depth_bits;
+                       public StencilBits stencil_bits;
+                       public OptionsBits options_bits;
+                       public MultisampleBits multisample_bits;
+                       public ContextVersion gles_version;
+               }
+
+               // This structure is used to move data from one entity into another.
+               internal struct NativeSurfaceOpenGL
+               {
+                       public uint texture_id;
+                       public uint framebuffer_id;
+                       public uint internal_format;
+                       public uint format;
+                       public uint x;
+                       public uint y;
+                       public uint w;
+                       public uint h;
+               }
+
+               internal enum ColorFormat
+               {
+                       RGB_888 = 0,
+                       RGBA_8888 = 1,
+                       NO_FBO = 2
+               }
+
+               internal enum DepthBits
+               {
+                       NONE = 0,
+                       BIT_8 = 1,
+                       BIT_16 = 2,
+                       BIT_24 = 3,
+                       BIT_32 = 4
+               }
+
+               internal enum StencilBits
+               {
+                       NONE = 0,
+                       BIT_1 = 1,
+                       BIT_2 = 2,
+                       BIT_4 = 3,
+                       BIT_8 = 4,
+                       BIT_16 = 5
+               }
+
+               internal enum OptionsBits
+               {
+                       NONE = 0,
+                       DIRECT = (1 << 0),
+                       CLIENT_SIDE_ROTATION = (1 << 1),
+                       THREAD = (1 << 2)
+               }
+
+               internal enum MultisampleBits
+               {
+                       NONE = 0,
+                       LOW = 1,
+                       MED = 2,
+                       HIGH = 3
+               }
+
+               internal enum ContextVersion
+               {
+                       GLES_1_X = 1,
+                       GLES_2_X = 2,
+                       GLES_3_X = 3,
+                       DEBUG = 0x1000
+               }
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_object_image_add(IntPtr obj);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_object_image_filled_add(IntPtr obj);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_size_get(IntPtr obj, IntPtr x, out int y);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_size_get(IntPtr obj, out int x, IntPtr y);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_size_get(IntPtr obj, out int x, out int y);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_size_set(IntPtr obj, int w, int h);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern IntPtr evas_object_image_data_get(IntPtr obj, bool for_writing);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_data_set(IntPtr obj, IntPtr data);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_data_update_add(IntPtr obj, int x, int y, int w, int h);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_colorspace_set(IntPtr obj, Colorspace cspace);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_fill_set(IntPtr obj, int x, int y, int w, int h);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_native_surface_set(IntPtr obj, ref NativeSurfaceOpenGL surf);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_native_surface_set(IntPtr obj, IntPtr zero);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_pixels_dirty_set(IntPtr obj, bool dirty);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_pixels_get_callback_set(IntPtr obj, ImagePixelsSetCallback func, IntPtr data);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_pixels_get_callback_set(IntPtr obj, IntPtr zero, IntPtr data);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_smooth_scale_set(IntPtr obj, bool smooth_scale);
+
+               [DllImport(Libraries.Evas)]
+               internal static extern void evas_object_image_alpha_set(IntPtr obj, bool has_alpha);
+
+               public delegate void ImagePixelsSetCallback(IntPtr data, IntPtr o);
+
+               internal enum Colorspace
+               {
+                       ARGB8888,
+                       YCBCR422P601_PL,
+                       YCBCR422P709_PL,
+                       RGB565_A5P,
+                       GRY8 = 4,
+                       YCBCR422601_PL,
+                       YCBCR420NV12601_PL,
+                       YCBCR420TM12601_PL,
+                       AGRY88 = 8,
+                       ETC1 = 9,
+                       RGB8_ETC2 = 10,
+                       RGBA8_ETC2_EAC = 11,
+                       ETC1_ALPHA = 12,
+                       RGB_S3TC_DXT1 = 13,
+                       RGBA_S3TC_DXT1 = 14,
+                       RGBA_S3TC_DXT2 = 15,
+                       RGBA_S3TC_DXT3 = 16,
+                       RGBA_S3TC_DXT4 = 17,
+                       RGBA_S3TC_DXT5 = 18,
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/Interop/Libraries.cs b/src/XSF/SkiaSharp.Views.Tizen/Interop/Libraries.cs
new file mode 100644 (file)
index 0000000..53c2ff6
--- /dev/null
@@ -0,0 +1,14 @@
+
+namespace SkiaSharp.Views.Tizen.Interop
+{
+       internal static class Libraries
+       {
+               internal const string Libc = "libc.so.6";
+               internal const string Evas = "libevas.so.1";
+               internal const string Elementary = "libelementary.so.1";
+               internal const string Eina = "libeina.so.1";
+               internal const string Ecore = "libecore.so.1";
+               internal const string Eo = "libeo.so.1";
+               internal const string Eext = "libefl-extension.so.0";
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/RenderingMode.cs b/src/XSF/SkiaSharp.Views.Tizen/RenderingMode.cs
new file mode 100644 (file)
index 0000000..69ae349
--- /dev/null
@@ -0,0 +1,8 @@
+namespace SkiaSharp.Views.Tizen
+{
+       public enum RenderingMode
+       {
+               Continuously,
+               WhenDirty,
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/SKCanvasView.cs b/src/XSF/SkiaSharp.Views.Tizen/SKCanvasView.cs
new file mode 100644 (file)
index 0000000..dfdcd7a
--- /dev/null
@@ -0,0 +1,73 @@
+using System;
+using ElmSharp;
+using SkiaSharp.Views.Tizen.Interop;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public class SKCanvasView : CustomRenderingView
+       {
+               private bool ignorePixelScaling;
+               private SKImageInfo info;
+
+               public SKCanvasView(EvasObject parent)
+                       : base(parent)
+               {
+                       info = new SKImageInfo(0, 0, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
+               }
+
+               public bool IgnorePixelScaling
+               {
+                       get { return ignorePixelScaling; }
+                       set
+                       {
+                               if (ignorePixelScaling != value)
+                               {
+                                       ignorePixelScaling = value;
+                                       OnResized();
+                               }
+                       }
+               }
+
+               public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
+
+               protected override SKSizeI GetSurfaceSize() => info.Size;
+
+               protected virtual void OnDrawFrame(SKPaintSurfaceEventArgs e)
+               {
+                       PaintSurface?.Invoke(this, e);
+               }
+
+               protected sealed override void OnDrawFrame()
+               {
+                       if (info.Width == 0 || info.Height == 0)
+                               return;
+
+                       // draw directly into the EFL image data
+                       using (var surface = SKSurface.Create(info, Evas.evas_object_image_data_get(evasImage, true), info.RowBytes))
+                       {
+                               // draw using SkiaSharp
+                               OnDrawFrame(new SKPaintSurfaceEventArgs(surface, info));
+                               surface.Canvas.Flush();
+                       }
+               }
+
+               protected sealed override bool UpdateSurfaceSize(Rect geometry)
+               {
+                       var w = info.Width;
+                       var h = info.Height;
+
+                       if (IgnorePixelScaling)
+                       {
+                               info.Width = (int)ScalingInfo.FromPixel(geometry.Width);
+                               info.Height = (int)ScalingInfo.FromPixel(geometry.Height);
+                       }
+                       else
+                       {
+                               info.Width = geometry.Width;
+                               info.Height = geometry.Height;
+                       }
+
+                       return (w != info.Width || h != info.Height);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/SKGLSurfaceView.cs b/src/XSF/SkiaSharp.Views.Tizen/SKGLSurfaceView.cs
new file mode 100644 (file)
index 0000000..e116be0
--- /dev/null
@@ -0,0 +1,186 @@
+using System;
+using System.Runtime.InteropServices;
+using ElmSharp;
+using SkiaSharp.Views.GlesInterop;
+using SkiaSharp.Views.Tizen.Interop;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public class SKGLSurfaceView : CustomRenderingView
+       {
+               private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;
+               private static readonly SKColorType colorType = SKImageInfo.PlatformColorType;
+
+               private readonly Evas.Config glConfig;
+
+               private IntPtr glConfigPtr;
+               private IntPtr glEvas;
+               private IntPtr glContext;
+               private IntPtr glSurface;
+
+               private GRContext context;
+               private GRGlFramebufferInfo glInfo;
+               private GRBackendRenderTarget renderTarget;
+               private SKSurface surface;
+               private SKCanvas canvas;
+               private SKSizeI surfaceSize;
+
+               public SKGLSurfaceView(EvasObject parent)
+                       : base(parent)
+               {
+                       glConfig = new Evas.Config
+                       {
+                               color_format = Evas.ColorFormat.RGBA_8888,
+                               depth_bits = Evas.DepthBits.BIT_24,
+                               stencil_bits = Evas.StencilBits.BIT_8,
+                               options_bits = Evas.OptionsBits.NONE,
+                               multisample_bits = Evas.MultisampleBits.HIGH,
+                               gles_version = default(int)
+                       };
+               }
+
+               public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface;
+
+               public GRContext GRContext => context;
+
+               protected override SKSizeI GetSurfaceSize() => surfaceSize;
+
+               protected virtual void OnDrawFrame(SKPaintGLSurfaceEventArgs e)
+               {
+                       PaintSurface?.Invoke(this, e);
+               }
+
+               protected sealed override void CreateNativeResources(EvasObject parent)
+               {
+                       if (glEvas == IntPtr.Zero)
+                       {
+                               // initialize the OpenGL (the EFL way)
+                               glEvas = Evas.evas_gl_new(Evas.evas_object_evas_get(parent));
+
+                               // copy the configuration to the native side
+                               glConfigPtr = Marshal.AllocHGlobal(Marshal.SizeOf(glConfig));
+                               Marshal.StructureToPtr(glConfig, glConfigPtr, false);
+
+                               // initialize the context
+                               glContext = Evas.evas_gl_context_create(glEvas, IntPtr.Zero);
+                       }
+               }
+
+               protected sealed override void DestroyNativeResources()
+               {
+                       if (glEvas != IntPtr.Zero)
+                       {
+                               // destroy the context
+                               Evas.evas_gl_context_destroy(glEvas, glContext);
+                               glContext = IntPtr.Zero;
+
+                               // release the unmanaged memory
+                               Marshal.FreeHGlobal(glConfigPtr);
+                               glConfigPtr = IntPtr.Zero;
+
+                               // destroy the EFL wrapper
+                               Evas.evas_gl_free(glEvas);
+                               glEvas = IntPtr.Zero;
+                       }
+               }
+
+               protected sealed override void OnDrawFrame()
+               {
+                       if (glSurface != IntPtr.Zero)
+                       {
+                               Gles.glClear(Gles.GL_COLOR_BUFFER_BIT | Gles.GL_DEPTH_BUFFER_BIT | Gles.GL_STENCIL_BUFFER_BIT);
+
+                               if (surface != null && renderTarget != null && context != null)
+                               {
+                                       using (new SKAutoCanvasRestore(canvas, true))
+                                       {
+                                               // draw using SkiaSharp
+                                               OnDrawFrame(new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo));
+                                       }
+
+                                       // flush the SkiaSharp contents to GL
+                                       canvas.Flush();
+                                       context.Flush();
+                               }
+                       }
+               }
+
+               protected sealed override bool UpdateSurfaceSize(Rect geometry)
+               {
+                       var changed =
+                               geometry.Width != surfaceSize.Width ||
+                               geometry.Height != surfaceSize.Height;
+
+                       if (changed)
+                       {
+                               // size has changed, update geometry
+                               surfaceSize.Width = geometry.Width;
+                               surfaceSize.Height = geometry.Height;
+                       }
+
+                       return changed;
+               }
+
+               protected sealed override void CreateDrawingSurface()
+               {
+                       if (glSurface == IntPtr.Zero)
+                       {
+                               // create the surface
+                               glSurface = Evas.evas_gl_surface_create(glEvas, glConfigPtr, surfaceSize.Width, surfaceSize.Height);
+
+                               // copy the native surface to the image
+                               Evas.evas_gl_native_surface_get(glEvas, glSurface, out var nativeSurface);
+                               Evas.evas_object_image_native_surface_set(evasImage, ref nativeSurface);
+
+                               // switch to the current OpenGL context
+                               Evas.evas_gl_make_current(glEvas, glSurface, glContext);
+
+                               // resize the viewport
+                               Gles.glViewport(0, 0, surfaceSize.Width, surfaceSize.Height);
+
+                               // create the interface using the function pointers provided by the EFL
+                               var glInterface = GRGlInterface.CreateNativeEvasInterface(glEvas);
+                               context?.Dispose();
+                               context = GRContext.Create(GRBackend.OpenGL, glInterface);
+
+                               // create the render target
+                               renderTarget?.Dispose();
+                               Gles.glGetIntegerv(Gles.GL_FRAMEBUFFER_BINDING, out var framebuffer);
+                               Gles.glGetIntegerv(Gles.GL_STENCIL_BITS, out var stencil);
+                               Gles.glGetIntegerv(Gles.GL_SAMPLES, out var samples);
+                               var maxSamples = context.GetMaxSurfaceSampleCount(colorType);
+                               if (samples > maxSamples)
+                                       samples = maxSamples;
+                               glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat());
+                               renderTarget = new GRBackendRenderTarget(surfaceSize.Width, surfaceSize.Height, samples, stencil, glInfo);
+
+                               // create the surface
+                               surface?.Dispose();
+                               surface = SKSurface.Create(context, renderTarget, surfaceOrigin, colorType);
+                               canvas = surface.Canvas;
+                       }
+               }
+
+               protected sealed override void DestroyDrawingSurface()
+               {
+                       if (glSurface != IntPtr.Zero)
+                       {
+                               // dispose the unmanaged memory
+                               canvas = null;
+                               surface?.Dispose();
+                               surface = null;
+                               renderTarget?.Dispose();
+                               renderTarget = null;
+                               context?.Dispose();
+                               context = null;
+
+                               // disconnect the surface from the image
+                               Evas.evas_object_image_native_surface_set(evasImage, IntPtr.Zero);
+
+                               // destroy the surface
+                               Evas.evas_gl_surface_destroy(glEvas, glSurface);
+                               glSurface = IntPtr.Zero;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/SKPaintGLSurfaceEventArgs.cs b/src/XSF/SkiaSharp.Views.Tizen/SKPaintGLSurfaceEventArgs.cs
new file mode 100644 (file)
index 0000000..2a3535f
--- /dev/null
@@ -0,0 +1,72 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public class SKPaintGLSurfaceEventArgs : EventArgs
+       {
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               [Obsolete]
+               private GRBackendRenderTargetDesc? rtDesc;
+
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               [Obsolete("Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, SKColorType, GRSurfaceOrigin) instead.")]
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTargetDesc renderTarget)
+               {
+                       Surface = surface;
+                       rtDesc = renderTarget;
+                       BackendRenderTarget = new GRBackendRenderTarget(GRBackend.OpenGL, renderTarget);
+                       ColorType = renderTarget.Config.ToColorType();
+                       Origin = renderTarget.Origin;
+               }
+
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget)
+                       : this(surface, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888)
+               {
+               }
+
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType)
+               {
+                       Surface = surface;
+                       BackendRenderTarget = renderTarget;
+                       ColorType = colorType;
+                       Origin = origin;
+               }
+
+               public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType, GRGlFramebufferInfo glInfo)
+               {
+                       Surface = surface;
+                       BackendRenderTarget = renderTarget;
+                       ColorType = colorType;
+                       Origin = origin;
+#pragma warning disable CS0612 // Type or member is obsolete
+                       rtDesc = CreateDesc(glInfo);
+#pragma warning restore CS0612 // Type or member is obsolete
+               }
+
+               public SKSurface Surface { get; private set; }
+
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               [Obsolete("Use BackendRenderTarget instead.")]
+               public GRBackendRenderTargetDesc RenderTarget => rtDesc ??= CreateDesc(BackendRenderTarget.GetGlFramebufferInfo());
+
+               [Obsolete]
+               private GRBackendRenderTargetDesc CreateDesc(GRGlFramebufferInfo glInfo) =>
+                       new GRBackendRenderTargetDesc
+                       {
+                               Width = BackendRenderTarget.Width,
+                               Height = BackendRenderTarget.Height,
+                               RenderTargetHandle = (IntPtr)glInfo.FramebufferObjectId,
+                               SampleCount = BackendRenderTarget.SampleCount,
+                               StencilBits = BackendRenderTarget.StencilBits,
+                               Config = ColorType.ToPixelConfig(),
+                               Origin = Origin,
+                       };
+
+               public GRBackendRenderTarget BackendRenderTarget { get; private set; }
+
+               public SKColorType ColorType { get; private set; }
+
+               public GRSurfaceOrigin Origin { get; private set; }
+       }
+}
\ No newline at end of file
diff --git a/src/XSF/SkiaSharp.Views.Tizen/SKPaintSurfaceEventArgs.cs b/src/XSF/SkiaSharp.Views.Tizen/SKPaintSurfaceEventArgs.cs
new file mode 100644 (file)
index 0000000..098724d
--- /dev/null
@@ -0,0 +1,17 @@
+using System;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public class SKPaintSurfaceEventArgs : EventArgs
+       {
+               public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info)
+               {
+                       Surface = surface;
+                       Info = info;
+               }
+
+               public SKSurface Surface { get; private set; }
+
+               public SKImageInfo Info { get; private set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/ScalingInfo.cs b/src/XSF/SkiaSharp.Views.Tizen/ScalingInfo.cs
new file mode 100644 (file)
index 0000000..95dcc29
--- /dev/null
@@ -0,0 +1,36 @@
+using System;
+using ElmSharp;
+using Tizen.System;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public static class ScalingInfo
+       {
+               private static readonly Lazy<string> profile = new Lazy<string>(() => Elementary.GetProfile());
+
+               private static readonly Lazy<int> dpi = new Lazy<int>(() =>
+               {
+                       // TV has fixed DPI value (72)
+                       if (Profile == "tv")
+                               return 72;
+
+#pragma warning disable CS0618 // Type or member is obsolete
+                       SystemInfo.TryGetValue("http://tizen.org/feature/screen.dpi", out int dpi);
+#pragma warning restore CS0618 // Type or member is obsolete
+                       return dpi;
+               });
+
+               // allows to convert pixels to Android-style device-independent pixels
+               private static readonly Lazy<double> scalingFactor = new Lazy<double>(() => dpi.Value / 160.0);
+
+               public static string Profile => profile.Value;
+
+               public static int Dpi => dpi.Value;
+
+               public static double ScalingFactor => scalingFactor.Value;
+
+               public static double FromPixel(double v) => v / ScalingFactor;
+
+               public static double ToPixel(double v) => v * ScalingFactor;
+       }
+}
diff --git a/src/XSF/SkiaSharp.Views.Tizen/TizenExtensions.cs b/src/XSF/SkiaSharp.Views.Tizen/TizenExtensions.cs
new file mode 100644 (file)
index 0000000..fa8c333
--- /dev/null
@@ -0,0 +1,85 @@
+using ElmSharp;
+
+namespace SkiaSharp.Views.Tizen
+{
+       public static class TizenExtensions
+       {
+               // Point
+
+               public static SKPoint ToSKPoint(this Point point)
+               {
+                       return new SKPoint(point.X, point.Y);
+               }
+
+               public static SKPointI ToSKPointI(this Point point)
+               {
+                       return new SKPointI(point.X, point.Y);
+               }
+
+               public static Point ToPoint(this SKPoint point)
+               {
+                       return new Point { X = (int)point.X, Y = (int)point.Y };
+               }
+
+               public static Point ToPoint(this SKPointI point)
+               {
+                       return new Point { X = point.X, Y = point.Y };
+               }
+
+               // Size
+
+               public static SKSize ToSKSize(this Size size)
+               {
+                       return new SKSize(size.Width, size.Height);
+               }
+
+               public static SKSizeI ToSKSizeI(this Size size)
+               {
+                       return new SKSizeI(size.Width, size.Height);
+               }
+
+               public static Size ToSize(this SKSize size)
+               {
+                       return new Size((int)size.Width, (int)size.Height);
+               }
+
+               public static Size ToSize(this SKSizeI size)
+               {
+                       return new Size(size.Width, size.Height);
+               }
+
+               // Rectangle
+
+               public static SKRect ToSKRect(this Rect rect)
+               {
+                       return new SKRect(rect.Left, rect.Top, rect.Right, rect.Bottom);
+               }
+
+               public static SKRectI ToSKRectI(this Rect rect)
+               {
+                       return new SKRectI(rect.Left, rect.Top, rect.Right, rect.Bottom);
+               }
+
+               public static Rect ToRect(this SKRect rect)
+               {
+                       return new Rect((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
+               }
+
+               public static Rect ToRect(this SKRectI rect)
+               {
+                       return new Rect(rect.Left, rect.Top, rect.Right, rect.Bottom);
+               }
+
+               // Color
+
+               public static SKColor ToSKColor(this Color color)
+               {
+                       return new SKColor((byte)color.R, (byte)color.G, (byte)color.B, (byte)color.A);
+               }
+
+               public static Color ToColor(this SKColor color)
+               {
+                       return Color.FromRgba(color.Red, color.Green, color.Blue, color.Alpha);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/Definitions.cs b/src/XSF/SkiaSharp/Definitions.cs
new file mode 100644 (file)
index 0000000..7fadf22
--- /dev/null
@@ -0,0 +1,554 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use SKEncodedOrigin instead.")]
+       public enum SKCodecOrigin
+       {
+               TopLeft = 1,
+               TopRight = 2,
+               BottomRight = 3,
+               BottomLeft = 4,
+               LeftTop = 5,
+               RightTop = 6,
+               RightBottom = 7,
+               LeftBottom = 8,
+       }
+
+       public enum SKFontStyleWeight
+       {
+               Invisible = 0,
+               Thin = 100,
+               ExtraLight = 200,
+               Light = 300,
+               Normal = 400,
+               Medium = 500,
+               SemiBold = 600,
+               Bold = 700,
+               ExtraBold = 800,
+               Black = 900,
+               ExtraBlack = 1000,
+       }
+
+       public enum SKFontStyleWidth
+       {
+               UltraCondensed = 1,
+               ExtraCondensed = 2,
+               Condensed = 3,
+               SemiCondensed = 4,
+               Normal = 5,
+               SemiExpanded = 6,
+               Expanded = 7,
+               ExtraExpanded = 8,
+               UltraExpanded = 9,
+       }
+
+       public static partial class SkiaExtensions
+       {
+               public static bool IsBgr (this SKPixelGeometry pg) =>
+                       pg == SKPixelGeometry.BgrHorizontal || pg == SKPixelGeometry.BgrVertical;
+
+               public static bool IsRgb (this SKPixelGeometry pg) =>
+                       pg == SKPixelGeometry.RgbHorizontal || pg == SKPixelGeometry.RgbVertical;
+
+               public static bool IsVertical (this SKPixelGeometry pg) =>
+                       pg == SKPixelGeometry.BgrVertical || pg == SKPixelGeometry.RgbVertical;
+
+               public static bool IsHorizontal (this SKPixelGeometry pg) =>
+                       pg == SKPixelGeometry.BgrHorizontal || pg == SKPixelGeometry.RgbHorizontal;
+
+               public static SKTextEncoding ToTextEncoding (this SKEncoding encoding) =>
+                       encoding switch
+                       {
+                               SKEncoding.Utf8 => SKTextEncoding.Utf8,
+                               SKEncoding.Utf16 => SKTextEncoding.Utf16,
+                               SKEncoding.Utf32 => SKTextEncoding.Utf32,
+                               _ => throw new ArgumentOutOfRangeException (nameof (encoding)),
+                       };
+
+               internal static SKEncoding ToEncoding (this SKTextEncoding encoding) =>
+                       encoding switch
+                       {
+                               SKTextEncoding.Utf8 => SKEncoding.Utf8,
+                               SKTextEncoding.Utf16 => SKEncoding.Utf16,
+                               SKTextEncoding.Utf32 => SKEncoding.Utf32,
+                               _ => throw new ArgumentOutOfRangeException (nameof (encoding)),
+                       };
+
+               public static int GetBytesPerPixel (this SKColorType colorType) =>
+                       colorType switch
+                       {
+                               SKColorType.Unknown => 0,
+                               SKColorType.Alpha8 => 1,
+                               SKColorType.Gray8 => 1,
+                               SKColorType.Rgb565 => 2,
+                               SKColorType.Argb4444 => 2,
+                               SKColorType.Bgra8888 => 4,
+                               SKColorType.Rgba8888 => 4,
+                               SKColorType.Rgb888x => 4,
+                               SKColorType.Rgba1010102 => 4,
+                               SKColorType.Rgb101010x => 4,
+                               SKColorType.RgbaF16 => 8,
+                               _ => throw new ArgumentOutOfRangeException (nameof (colorType)),
+                       };
+
+               public static SKAlphaType GetAlphaType (this SKColorType colorType, SKAlphaType alphaType = SKAlphaType.Premul) =>
+                       colorType switch
+                       {
+                               SKColorType.Unknown => SKAlphaType.Unknown,
+                               SKColorType.Alpha8 => SKAlphaType.Premul,
+                               SKColorType.Gray8 => SKAlphaType.Opaque,
+                               SKColorType.Rgb565 => SKAlphaType.Opaque,
+                               SKColorType.Argb4444 => alphaType,
+                               SKColorType.Bgra8888 => alphaType,
+                               SKColorType.Rgba8888 => alphaType,
+                               SKColorType.Rgb888x => SKAlphaType.Opaque,
+                               SKColorType.Rgba1010102 => alphaType,
+                               SKColorType.Rgb101010x => SKAlphaType.Opaque,
+                               SKColorType.RgbaF16 => alphaType,
+                               _ => throw new ArgumentOutOfRangeException (nameof (colorType)),
+                       };
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use SKSurfaceProperties instead.")]
+       public struct SKSurfaceProps : IEquatable<SKSurfaceProps>
+       {
+               public SKPixelGeometry PixelGeometry { get; set; }
+               public SKSurfacePropsFlags Flags { get; set; }
+
+               public readonly bool Equals (SKSurfaceProps obj) =>
+                       PixelGeometry == obj.PixelGeometry &&
+                       Flags == obj.Flags;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKSurfaceProps f && Equals (f);
+
+               public static bool operator == (SKSurfaceProps left, SKSurfaceProps right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKSurfaceProps left, SKSurfaceProps right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (PixelGeometry);
+                       hash.Add (Flags);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       public struct SKCodecOptions : IEquatable<SKCodecOptions>
+       {
+               public static readonly SKCodecOptions Default;
+
+               static SKCodecOptions ()
+               {
+                       Default = new SKCodecOptions (SKZeroInitialized.No);
+               }
+
+               public SKCodecOptions (SKZeroInitialized zeroInitialized)
+               {
+                       ZeroInitialized = zeroInitialized;
+                       Subset = null;
+                       FrameIndex = 0;
+                       PriorFrame = -1;
+                       PremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+               public SKCodecOptions (SKZeroInitialized zeroInitialized, SKRectI subset)
+               {
+                       ZeroInitialized = zeroInitialized;
+                       Subset = subset;
+                       FrameIndex = 0;
+                       PriorFrame = -1;
+                       PremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+               public SKCodecOptions (SKRectI subset)
+               {
+                       ZeroInitialized = SKZeroInitialized.No;
+                       Subset = subset;
+                       FrameIndex = 0;
+                       PriorFrame = -1;
+                       PremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+               public SKCodecOptions (int frameIndex)
+               {
+                       ZeroInitialized = SKZeroInitialized.No;
+                       Subset = null;
+                       FrameIndex = frameIndex;
+                       PriorFrame = -1;
+                       PremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+               public SKCodecOptions (int frameIndex, int priorFrame)
+               {
+                       ZeroInitialized = SKZeroInitialized.No;
+                       Subset = null;
+                       FrameIndex = frameIndex;
+                       PriorFrame = priorFrame;
+                       PremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+
+               public SKZeroInitialized ZeroInitialized { get; set; }
+               public SKRectI? Subset { get; set; }
+               public readonly bool HasSubset => Subset != null;
+               public int FrameIndex { get; set; }
+               public int PriorFrame { get; set; }
+               public SKTransferFunctionBehavior PremulBehavior { get; set; }
+
+               public readonly bool Equals (SKCodecOptions obj) =>
+                       ZeroInitialized == obj.ZeroInitialized &&
+                       Subset == obj.Subset &&
+                       FrameIndex == obj.FrameIndex &&
+                       PriorFrame == obj.PriorFrame &&
+                       PremulBehavior == obj.PremulBehavior;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKCodecOptions f && Equals (f);
+
+               public static bool operator == (SKCodecOptions left, SKCodecOptions right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKCodecOptions left, SKCodecOptions right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (ZeroInitialized);
+                       hash.Add (Subset);
+                       hash.Add (FrameIndex);
+                       hash.Add (PriorFrame);
+                       hash.Add (PremulBehavior);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       public partial struct SKFontMetrics
+       {
+               private const uint flagsUnderlineThicknessIsValid = (1U << 0);
+               private const uint flagsUnderlinePositionIsValid = (1U << 1);
+               private const uint flagsStrikeoutThicknessIsValid = (1U << 2);
+               private const uint flagsStrikeoutPositionIsValid = (1U << 3);
+
+               public readonly float Top => fTop;
+
+               public readonly float Ascent => fAscent;
+
+               public readonly float Descent => fDescent;
+
+               public readonly float Bottom => fBottom;
+
+               public readonly float Leading => fLeading;
+
+               public readonly float AverageCharacterWidth => fAvgCharWidth;
+
+               public readonly float MaxCharacterWidth => fMaxCharWidth;
+
+               public readonly float XMin => fXMin;
+
+               public readonly float XMax => fXMax;
+
+               public readonly float XHeight => fXHeight;
+
+               public readonly float CapHeight => fCapHeight;
+
+               public readonly float? UnderlineThickness => GetIfValid (fUnderlineThickness, flagsUnderlineThicknessIsValid);
+               public readonly float? UnderlinePosition => GetIfValid (fUnderlinePosition, flagsUnderlinePositionIsValid);
+               public readonly float? StrikeoutThickness => GetIfValid (fStrikeoutThickness, flagsStrikeoutThicknessIsValid);
+               public readonly float? StrikeoutPosition => GetIfValid (fStrikeoutPosition, flagsStrikeoutPositionIsValid);
+
+               private readonly float? GetIfValid (float value, uint flag) =>
+                       (fFlags & flag) == flag ? value : (float?)null;
+       }
+
+       public struct SKLattice : IEquatable<SKLattice>
+       {
+               public int[] XDivs { get; set; }
+               public int[] YDivs { get; set; }
+               public SKLatticeRectType[] RectTypes { get; set; }
+               public SKRectI? Bounds { get; set; }
+               public SKColor[] Colors { get; set; }
+
+               public readonly bool Equals (SKLattice obj) =>
+                       XDivs == obj.XDivs &&
+                       YDivs == obj.YDivs &&
+                       RectTypes == obj.RectTypes &&
+                       Bounds == obj.Bounds &&
+                       Colors == obj.Colors;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKLattice f && Equals (f);
+
+               public static bool operator == (SKLattice left, SKLattice right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKLattice left, SKLattice right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (XDivs);
+                       hash.Add (YDivs);
+                       hash.Add (RectTypes);
+                       hash.Add (Bounds);
+                       hash.Add (Colors);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       internal partial struct SKTimeDateTimeInternal
+       {
+               public static SKTimeDateTimeInternal Create (DateTime datetime)
+               {
+                       var zone = datetime.Hour - datetime.ToUniversalTime ().Hour;
+                       return new SKTimeDateTimeInternal {
+                               fTimeZoneMinutes = (Int16)(zone * 60),
+                               fYear = (UInt16)datetime.Year,
+                               fMonth = (Byte)datetime.Month,
+                               fDayOfWeek = (Byte)datetime.DayOfWeek,
+                               fDay = (Byte)datetime.Day,
+                               fHour = (Byte)datetime.Hour,
+                               fMinute = (Byte)datetime.Minute,
+                               fSecond = (Byte)datetime.Second
+                       };
+               }
+       }
+
+       public struct SKDocumentPdfMetadata : IEquatable<SKDocumentPdfMetadata>
+       {
+               public const float DefaultRasterDpi = SKDocument.DefaultRasterDpi;
+               public const int DefaultEncodingQuality = 101;
+
+               public static readonly SKDocumentPdfMetadata Default;
+
+               static SKDocumentPdfMetadata ()
+               {
+                       Default = new SKDocumentPdfMetadata () {
+                               RasterDpi = DefaultRasterDpi,
+                               PdfA = false,
+                               EncodingQuality = 101,
+                       };
+               }
+
+               public SKDocumentPdfMetadata (float rasterDpi)
+               {
+                       Title = null;
+                       Author = null;
+                       Subject = null;
+                       Keywords = null;
+                       Creator = null;
+                       Producer = null;
+                       Creation = null;
+                       Modified = null;
+                       RasterDpi = rasterDpi;
+                       PdfA = false;
+                       EncodingQuality = DefaultEncodingQuality;
+               }
+
+               public SKDocumentPdfMetadata (int encodingQuality)
+               {
+                       Title = null;
+                       Author = null;
+                       Subject = null;
+                       Keywords = null;
+                       Creator = null;
+                       Producer = null;
+                       Creation = null;
+                       Modified = null;
+                       RasterDpi = DefaultRasterDpi;
+                       PdfA = false;
+                       EncodingQuality = encodingQuality;
+               }
+
+               public SKDocumentPdfMetadata (float rasterDpi, int encodingQuality)
+               {
+                       Title = null;
+                       Author = null;
+                       Subject = null;
+                       Keywords = null;
+                       Creator = null;
+                       Producer = null;
+                       Creation = null;
+                       Modified = null;
+                       RasterDpi = rasterDpi;
+                       PdfA = false;
+                       EncodingQuality = encodingQuality;
+               }
+
+               public string Title { get; set; }
+               public string Author { get; set; }
+               public string Subject { get; set; }
+               public string Keywords { get; set; }
+               public string Creator { get; set; }
+               public string Producer { get; set; }
+               public DateTime? Creation { get; set; }
+               public DateTime? Modified { get; set; }
+               public float RasterDpi { get; set; }
+               public bool PdfA { get; set; }
+               public int EncodingQuality { get; set; }
+
+               public readonly bool Equals (SKDocumentPdfMetadata obj) =>
+                       Title == obj.Title &&
+                       Author == obj.Author &&
+                       Subject == obj.Subject &&
+                       Keywords == obj.Keywords &&
+                       Creator == obj.Creator &&
+                       Producer == obj.Producer &&
+                       Creation == obj.Creation &&
+                       Modified == obj.Modified &&
+                       RasterDpi == obj.RasterDpi &&
+                       PdfA == obj.PdfA &&
+                       EncodingQuality == obj.EncodingQuality;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKDocumentPdfMetadata f && Equals (f);
+
+               public static bool operator == (SKDocumentPdfMetadata left, SKDocumentPdfMetadata right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKDocumentPdfMetadata left, SKDocumentPdfMetadata right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (Title);
+                       hash.Add (Author);
+                       hash.Add (Subject);
+                       hash.Add (Keywords);
+                       hash.Add (Creator);
+                       hash.Add (Producer);
+                       hash.Add (Creation);
+                       hash.Add (Modified);
+                       hash.Add (RasterDpi);
+                       hash.Add (PdfA);
+                       hash.Add (EncodingQuality);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete]
+       [Flags]
+       public enum SKColorSpaceFlags
+       {
+               None = 0,
+               NonLinearBlending = 0x1,
+       }
+
+       public partial struct SKHighContrastConfig
+       {
+               public static readonly SKHighContrastConfig Default;
+
+               static SKHighContrastConfig ()
+               {
+                       Default = new SKHighContrastConfig (false, SKHighContrastConfigInvertStyle.NoInvert, 0.0f);
+               }
+
+               public SKHighContrastConfig (bool grayscale, SKHighContrastConfigInvertStyle invertStyle, float contrast)
+               {
+                       fGrayscale = grayscale ? (byte)1 : (byte)0;
+                       fInvertStyle = invertStyle;
+                       fContrast = contrast;
+               }
+
+               public readonly bool IsValid =>
+                       (int)fInvertStyle >= (int)SKHighContrastConfigInvertStyle.NoInvert &&
+                       (int)fInvertStyle <= (int)SKHighContrastConfigInvertStyle.InvertLightness &&
+                       fContrast >= -1.0 &&
+                       fContrast <= 1.0;
+       }
+
+       public unsafe partial struct SKPngEncoderOptions
+       {
+               public static readonly SKPngEncoderOptions Default;
+
+               static SKPngEncoderOptions ()
+               {
+                       Default = new SKPngEncoderOptions (SKPngEncoderFilterFlags.AllFilters, 6, SKTransferFunctionBehavior.Respect);
+               }
+
+               public SKPngEncoderOptions (SKPngEncoderFilterFlags filterFlags, int zLibLevel)
+               {
+                       fFilterFlags = filterFlags;
+                       fZLibLevel = zLibLevel;
+                       fUnpremulBehavior = SKTransferFunctionBehavior.Respect;
+                       fComments = null;
+               }
+
+               public SKPngEncoderOptions (SKPngEncoderFilterFlags filterFlags, int zLibLevel, SKTransferFunctionBehavior unpremulBehavior)
+               {
+                       fFilterFlags = filterFlags;
+                       fZLibLevel = zLibLevel;
+                       fUnpremulBehavior = unpremulBehavior;
+                       fComments = null;
+               }
+
+               public SKPngEncoderFilterFlags FilterFlags {
+                       readonly get => fFilterFlags;
+                       set => fFilterFlags = value;
+               }
+               public int ZLibLevel {
+                       readonly get => fZLibLevel;
+                       set => fZLibLevel = value;
+               }
+               public SKTransferFunctionBehavior UnpremulBehavior {
+                       readonly get => fUnpremulBehavior;
+                       set => fUnpremulBehavior = value;
+               }
+       }
+
+       public partial struct SKJpegEncoderOptions
+       {
+               public static readonly SKJpegEncoderOptions Default;
+
+               static SKJpegEncoderOptions ()
+               {
+                       Default = new SKJpegEncoderOptions (100, SKJpegEncoderDownsample.Downsample420, SKJpegEncoderAlphaOption.Ignore, SKTransferFunctionBehavior.Respect);
+               }
+
+               public SKJpegEncoderOptions (int quality, SKJpegEncoderDownsample downsample, SKJpegEncoderAlphaOption alphaOption)
+               {
+                       fQuality = quality;
+                       fDownsample = downsample;
+                       fAlphaOption = alphaOption;
+                       fBlendBehavior = SKTransferFunctionBehavior.Respect;
+               }
+
+               public SKJpegEncoderOptions (int quality, SKJpegEncoderDownsample downsample, SKJpegEncoderAlphaOption alphaOption, SKTransferFunctionBehavior blendBehavior)
+               {
+                       fQuality = quality;
+                       fDownsample = downsample;
+                       fAlphaOption = alphaOption;
+                       fBlendBehavior = blendBehavior;
+               }
+       }
+
+       public partial struct SKWebpEncoderOptions
+       {
+               public static readonly SKWebpEncoderOptions Default;
+
+               static SKWebpEncoderOptions ()
+               {
+                       Default = new SKWebpEncoderOptions (SKWebpEncoderCompression.Lossy, 100, SKTransferFunctionBehavior.Respect);
+               }
+
+               public SKWebpEncoderOptions (SKWebpEncoderCompression compression, float quality)
+               {
+                       fCompression = compression;
+                       fQuality = quality;
+                       fUnpremulBehavior = SKTransferFunctionBehavior.Respect;
+               }
+
+               public SKWebpEncoderOptions (SKWebpEncoderCompression compression, float quality, SKTransferFunctionBehavior unpremulBehavior)
+               {
+                       fCompression = compression;
+                       fQuality = quality;
+                       fUnpremulBehavior = unpremulBehavior;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/DelegateProxies.cs b/src/XSF/SkiaSharp/DelegateProxies.cs
new file mode 100644 (file)
index 0000000..fdeb53b
--- /dev/null
@@ -0,0 +1,101 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       // public delegates
+
+       public delegate void SKBitmapReleaseDelegate (IntPtr address, object context);
+
+       public delegate void SKDataReleaseDelegate (IntPtr address, object context);
+
+       public delegate void SKImageRasterReleaseDelegate (IntPtr pixels, object context);
+
+       public delegate void SKImageTextureReleaseDelegate (object context);
+
+       public delegate void SKSurfaceReleaseDelegate (IntPtr address, object context);
+
+       public delegate IntPtr GRGlGetProcDelegate (object context, string name);
+
+       internal unsafe static partial class DelegateProxies
+       {
+               // references to the proxy implementations
+               public static readonly SKBitmapReleaseProxyDelegate SKBitmapReleaseDelegateProxy = SKBitmapReleaseDelegateProxyImplementation;
+               public static readonly SKDataReleaseProxyDelegate SKDataReleaseDelegateProxy = SKDataReleaseDelegateProxyImplementation;
+               public static readonly SKImageRasterReleaseProxyDelegate SKImageRasterReleaseDelegateProxy = SKImageRasterReleaseDelegateProxyImplementation;
+               public static readonly SKImageRasterReleaseProxyDelegate SKImageRasterReleaseDelegateProxyForCoTaskMem = SKImageRasterReleaseDelegateProxyImplementationForCoTaskMem;
+               public static readonly SKImageTextureReleaseProxyDelegate SKImageTextureReleaseDelegateProxy = SKImageTextureReleaseDelegateProxyImplementation;
+               public static readonly SKSurfaceRasterReleaseProxyDelegate SKSurfaceReleaseDelegateProxy = SKSurfaceReleaseDelegateProxyImplementation;
+               public static readonly GRGlGetProcProxyDelegate GRGlGetProcDelegateProxy = GRGlGetProcDelegateProxyImplementation;
+
+               // internal proxy implementations
+
+               [MonoPInvokeCallback (typeof (SKBitmapReleaseProxyDelegate))]
+               private static void SKBitmapReleaseDelegateProxyImplementation (void* address, void* context)
+               {
+                       var del = Get<SKBitmapReleaseDelegate> ((IntPtr)context, out var gch);
+                       try {
+                               del.Invoke ((IntPtr)address, null);
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (SKDataReleaseProxyDelegate))]
+               private static void SKDataReleaseDelegateProxyImplementation (void* address, void* context)
+               {
+                       var del = Get<SKDataReleaseDelegate> ((IntPtr)context, out var gch);
+                       try {
+                               del.Invoke ((IntPtr)address, null);
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (SKImageRasterReleaseProxyDelegate))]
+               private static void SKImageRasterReleaseDelegateProxyImplementationForCoTaskMem (void* pixels, void* context)
+               {
+                       Marshal.FreeCoTaskMem ((IntPtr)pixels);
+               }
+
+               [MonoPInvokeCallback (typeof (SKImageRasterReleaseProxyDelegate))]
+               private static void SKImageRasterReleaseDelegateProxyImplementation (void* pixels, void* context)
+               {
+                       var del = Get<SKImageRasterReleaseDelegate> ((IntPtr)context, out var gch);
+                       try {
+                               del.Invoke ((IntPtr)pixels, null);
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (SKImageTextureReleaseProxyDelegate))]
+               private static void SKImageTextureReleaseDelegateProxyImplementation (void* context)
+               {
+                       var del = Get<SKImageTextureReleaseDelegate> ((IntPtr)context, out var gch);
+                       try {
+                               del.Invoke (null);
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (SKSurfaceRasterReleaseProxyDelegate))]
+               private static void SKSurfaceReleaseDelegateProxyImplementation (void* address, void* context)
+               {
+                       var del = Get<SKSurfaceReleaseDelegate> ((IntPtr)context, out var gch);
+                       try {
+                               del.Invoke ((IntPtr)address, null);
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (GRGlGetProcProxyDelegate))]
+               private static IntPtr GRGlGetProcDelegateProxyImplementation (void* context, string name)
+               {
+                       var del = Get<GRGlGetProcDelegate> ((IntPtr)context, out _);
+                       return del.Invoke (null, name);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/DelegateProxies.shared.cs b/src/XSF/SkiaSharp/DelegateProxies.shared.cs
new file mode 100644 (file)
index 0000000..122e11c
--- /dev/null
@@ -0,0 +1,329 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+#if THROW_OBJECT_EXCEPTIONS
+       using GCHandle = GCHandleProxy;
+#endif
+
+       // helper delegates
+
+       internal delegate Delegate GetMultiDelegateDelegate (Type index);
+
+       internal delegate object UserDataDelegate ();
+
+       internal static partial class DelegateProxies
+       {
+               // normal delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T Create<T> (object managedDel, T nativeDel, out GCHandle gch, out IntPtr contextPtr)
+               {
+                       if (managedDel == null) {
+                               gch = default (GCHandle);
+                               contextPtr = IntPtr.Zero;
+                               return default (T);
+                       }
+
+                       gch = GCHandle.Alloc (managedDel);
+                       contextPtr = GCHandle.ToIntPtr (gch);
+                       return nativeDel;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void Create (object managedDel, out GCHandle gch, out IntPtr contextPtr)
+               {
+                       if (managedDel == null) {
+                               gch = default (GCHandle);
+                               contextPtr = IntPtr.Zero;
+                               return;
+                       }
+
+                       gch = GCHandle.Alloc (managedDel);
+                       contextPtr = GCHandle.ToIntPtr (gch);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T Get<T> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       if (contextPtr == IntPtr.Zero) {
+                               gch = default (GCHandle);
+                               return default (T);
+                       }
+
+                       gch = GCHandle.FromIntPtr (contextPtr);
+                       return (T)gch.Target;
+               }
+
+               // user data delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateUserData (object userData, bool makeWeak = false)
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var del = new UserDataDelegate (() => userData);
+                       Create (del, out _, out var ctx);
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T GetUserData<T> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       var del = Get<UserDataDelegate> (contextPtr, out gch);
+                       var value = del.Invoke ();
+                       return value is WeakReference weak ? (T)weak.Target : (T)value;
+               }
+
+               // multi-value delegates
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMulti<T1, T2> (T1 wrappedDelegate1, T2 wrappedDelegate2)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMulti<T1, T2, T3> (T1 wrappedDelegate1, T2 wrappedDelegate2, T3 wrappedDelegate3)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (T3))
+                                       return wrappedDelegate3;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static T GetMulti<T> (IntPtr contextPtr, out GCHandle gch)
+                       where T : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       return (T)multi.Invoke (typeof (T));
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMulti<T1, T2> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMulti<T1, T2, T3> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out T3 wrappedDelegate3, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       wrappedDelegate3 = (T3)multi.Invoke (typeof (T3));
+               }
+
+               // multi-value delegate with user data
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T> (T wrappedDelegate, object userData, bool makeWeak = false)
+                       where T : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T))
+                                       return wrappedDelegate;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T1, T2> (T1 wrappedDelegate1, T2 wrappedDelegate2, object userData, bool makeWeak = false)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static IntPtr CreateMultiUserData<T1, T2, T3> (T1 wrappedDelegate1, T2 wrappedDelegate2, T3 wrappedDelegate3, object userData, bool makeWeak = false)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       userData = makeWeak ? new WeakReference (userData) : userData;
+                       var userDataDelegate = new UserDataDelegate (() => userData);
+
+                       var del = new GetMultiDelegateDelegate ((type) => {
+                               if (type == typeof (T1))
+                                       return wrappedDelegate1;
+                               if (type == typeof (T2))
+                                       return wrappedDelegate2;
+                               if (type == typeof (T3))
+                                       return wrappedDelegate3;
+                               if (type == typeof (UserDataDelegate))
+                                       return userDataDelegate;
+                               throw new ArgumentOutOfRangeException (nameof (type));
+                       });
+
+                       Create (del, out _, out var ctx);
+
+                       return ctx;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static TUserData GetMultiUserData<TUserData> (IntPtr contextPtr, out GCHandle gch)
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       return GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T, TUserData> (IntPtr contextPtr, out T wrappedDelegate, out TUserData userData, out GCHandle gch)
+                       where T : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate = (T)multi.Invoke (typeof (T));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T1, T2, TUserData> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out TUserData userData, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               public static void GetMultiUserData<T1, T2, T3, TUserData> (IntPtr contextPtr, out T1 wrappedDelegate1, out T2 wrappedDelegate2, out T3 wrappedDelegate3, out TUserData userData, out GCHandle gch)
+                       where T1 : Delegate
+                       where T2 : Delegate
+                       where T3 : Delegate
+               {
+                       var multi = Get<GetMultiDelegateDelegate> (contextPtr, out gch);
+                       wrappedDelegate1 = (T1)multi.Invoke (typeof (T1));
+                       wrappedDelegate2 = (T2)multi.Invoke (typeof (T2));
+                       wrappedDelegate3 = (T3)multi.Invoke (typeof (T3));
+                       userData = GetUserData<TUserData> (multi);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static TUserData GetUserData<TUserData> (GetMultiDelegateDelegate multi)
+               {
+                       var userDataDelegate = (UserDataDelegate)multi.Invoke (typeof (UserDataDelegate));
+                       var value = userDataDelegate.Invoke ();
+                       return value is WeakReference weak ? (TUserData)weak.Target : (TUserData)value;
+               }
+       }
+
+#if THROW_OBJECT_EXCEPTIONS
+       // an internal, debug-only proxy that we can use to make sure we are not
+       // leaking GC handles by accident
+       internal struct GCHandleProxy
+       {
+               internal static readonly ConcurrentDictionary<IntPtr, WeakReference> allocatedHandles = new ConcurrentDictionary<IntPtr, WeakReference> ();
+
+               private System.Runtime.InteropServices.GCHandle gch;
+
+               public GCHandleProxy (System.Runtime.InteropServices.GCHandle gcHandle)
+               {
+                       gch = gcHandle;
+               }
+
+               public bool IsAllocated => gch.IsAllocated;
+
+               public object Target => gch.Target;
+
+               public void Free ()
+               {
+                       if (!allocatedHandles.TryRemove (ToIntPtr (this), out _))
+                               throw new InvalidOperationException ($"Allocated GC handle has already been freed.");
+
+                       gch.Free ();
+               }
+
+               internal static GCHandleProxy Alloc (object value)
+               {
+                       var gch = new GCHandleProxy (System.Runtime.InteropServices.GCHandle.Alloc (value));
+
+                       var weak = new WeakReference (value);
+                       var oldWeak = allocatedHandles.GetOrAdd (ToIntPtr (gch), weak);
+                       if (weak != oldWeak)
+                               throw new InvalidOperationException (
+                                       $"GC handle has already been allocated for this memory location. " +
+                                       $"Old: {oldWeak.Target} New: {value}");
+
+                       return gch;
+               }
+
+               internal static GCHandleProxy FromIntPtr (IntPtr value) =>
+                       new GCHandleProxy (System.Runtime.InteropServices.GCHandle.FromIntPtr (value));
+
+               internal static IntPtr ToIntPtr (GCHandleProxy value) =>
+                       System.Runtime.InteropServices.GCHandle.ToIntPtr (value.gch);
+       }
+#endif
+
+       [AttributeUsage (AttributeTargets.Method)]
+       internal sealed class MonoPInvokeCallbackAttribute : Attribute
+       {
+               public MonoPInvokeCallbackAttribute (Type type)
+               {
+                       Type = type;
+               }
+
+               public Type Type { get; private set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp/GRBackendRenderTarget.cs b/src/XSF/SkiaSharp/GRBackendRenderTarget.cs
new file mode 100644 (file)
index 0000000..b6e6e9a
--- /dev/null
@@ -0,0 +1,73 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class GRBackendRenderTarget : SKObject
+       {
+               [Preserve]
+               internal GRBackendRenderTarget (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GRBackendRenderTarget(int, int, int, int, GRGlFramebufferInfo) instead.")]
+               public GRBackendRenderTarget (GRBackend backend, GRBackendRenderTargetDesc desc)
+                       : this (IntPtr.Zero, true)
+               {
+                       switch (backend) {
+                               case GRBackend.Metal:
+                                       throw new NotSupportedException ();
+                               case GRBackend.OpenGL:
+                                       var glInfo = new GRGlFramebufferInfo ((uint)desc.RenderTargetHandle, desc.Config.ToGlSizedFormat ());
+                                       CreateGl (desc.Width, desc.Height, desc.SampleCount, desc.StencilBits, glInfo);
+                                       break;
+                               case GRBackend.Vulkan:
+                                       throw new NotSupportedException ();
+                               default:
+                                       throw new ArgumentOutOfRangeException (nameof (backend));
+                       }
+               }
+
+               public GRBackendRenderTarget (int width, int height, int sampleCount, int stencilBits, GRGlFramebufferInfo glInfo)
+                       : this (IntPtr.Zero, true)
+               {
+                       CreateGl (width, height, sampleCount, stencilBits, glInfo);
+               }
+
+               private void CreateGl (int width, int height, int sampleCount, int stencilBits, GRGlFramebufferInfo glInfo)
+               {
+                       Handle = SkiaApi.gr_backendrendertarget_new_gl (width, height, sampleCount, stencilBits, &glInfo);
+
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new GRBackendRenderTarget instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.gr_backendrendertarget_delete (Handle);
+
+               public bool IsValid => SkiaApi.gr_backendrendertarget_is_valid (Handle);
+               public int Width => SkiaApi.gr_backendrendertarget_get_width (Handle);
+               public int Height => SkiaApi.gr_backendrendertarget_get_height (Handle);
+               public int SampleCount => SkiaApi.gr_backendrendertarget_get_samples (Handle);
+               public int StencilBits => SkiaApi.gr_backendrendertarget_get_stencils (Handle);
+               public GRBackend Backend => SkiaApi.gr_backendrendertarget_get_backend (Handle);
+               public SKSizeI Size => new SKSizeI (Width, Height);
+               public SKRectI Rect => new SKRectI (0, 0, Width, Height);
+
+               public GRGlFramebufferInfo GetGlFramebufferInfo () =>
+                       GetGlFramebufferInfo (out var info) ? info : default;
+
+               public bool GetGlFramebufferInfo (out GRGlFramebufferInfo glInfo)
+               {
+                       fixed (GRGlFramebufferInfo* g = &glInfo) {
+                               return SkiaApi.gr_backendrendertarget_get_gl_framebufferinfo (Handle, g);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/GRBackendTexture.cs b/src/XSF/SkiaSharp/GRBackendTexture.cs
new file mode 100644 (file)
index 0000000..dd3c494
--- /dev/null
@@ -0,0 +1,86 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public unsafe class GRBackendTexture : SKObject
+       {
+               [Preserve]
+               internal GRBackendTexture (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GRBackendTexture(int, int, bool, GRGlTextureInfo) instead.")]
+               public GRBackendTexture (GRGlBackendTextureDesc desc)
+                       : this (IntPtr.Zero, true)
+               {
+                       var handle = desc.TextureHandle;
+                       if (handle.Format == 0) {
+                               handle.Format = desc.Config.ToGlSizedFormat ();
+                       }
+                       CreateGl (desc.Width, desc.Height, false, handle);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GRBackendTexture(int, int, bool, GRGlTextureInfo) instead.")]
+               public GRBackendTexture (GRBackendTextureDesc desc)
+                       : this (IntPtr.Zero, true)
+               {
+                       var handlePtr = desc.TextureHandle;
+                       var oldHandle = PtrToStructure<GRTextureInfoObsolete> (handlePtr);
+
+                       var handle = new GRGlTextureInfo (oldHandle.fTarget, oldHandle.fID, desc.Config.ToGlSizedFormat ());
+                       CreateGl (desc.Width, desc.Height, false, handle);
+               }
+
+               public GRBackendTexture (int width, int height, bool mipmapped, GRGlTextureInfo glInfo)
+                       : this (IntPtr.Zero, true)
+               {
+                       CreateGl (width, height, mipmapped, glInfo);
+               }
+
+               private void CreateGl (int width, int height, bool mipmapped, GRGlTextureInfo glInfo)
+               {
+                       Handle = SkiaApi.gr_backendtexture_new_gl (width, height, mipmapped, &glInfo);
+
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new GRBackendTexture instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.gr_backendtexture_delete (Handle);
+
+               public bool IsValid => SkiaApi.gr_backendtexture_is_valid (Handle);
+               public int Width => SkiaApi.gr_backendtexture_get_width (Handle);
+               public int Height => SkiaApi.gr_backendtexture_get_height (Handle);
+               public bool HasMipMaps => SkiaApi.gr_backendtexture_has_mipmaps (Handle);
+               public GRBackend Backend => SkiaApi.gr_backendtexture_get_backend (Handle);
+               public SKSizeI Size => new SKSizeI (Width, Height);
+               public SKRectI Rect => new SKRectI (0, 0, Width, Height);
+
+               public GRGlTextureInfo GetGlTextureInfo () =>
+                       GetGlTextureInfo (out var info) ? info : default;
+
+               public bool GetGlTextureInfo (out GRGlTextureInfo glInfo)
+               {
+                       fixed (GRGlTextureInfo* g = &glInfo) {
+                               return SkiaApi.gr_backendtexture_get_gl_textureinfo (Handle, g);
+                       }
+               }
+
+               [Obsolete]
+               [StructLayout (LayoutKind.Sequential)]
+               internal struct GRTextureInfoObsolete
+               {
+                       public uint fTarget;
+                       public uint fID;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/GRContext.cs b/src/XSF/SkiaSharp/GRContext.cs
new file mode 100644 (file)
index 0000000..49e57c2
--- /dev/null
@@ -0,0 +1,108 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class GRContext : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal GRContext (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // Create
+
+               public static GRContext Create (GRBackend backend) =>
+                       backend switch
+                       {
+                               GRBackend.Metal => throw new NotSupportedException (),
+                               GRBackend.OpenGL => CreateGl (),
+                               GRBackend.Vulkan => throw new NotSupportedException (),
+                               _ => throw new ArgumentOutOfRangeException (nameof (backend)),
+                       };
+
+               public static GRContext Create (GRBackend backend, GRGlInterface backendContext) =>
+                       backend switch
+                       {
+                               GRBackend.Metal => throw new NotSupportedException (),
+                               GRBackend.OpenGL => CreateGl (backendContext),
+                               GRBackend.Vulkan => throw new NotSupportedException (),
+                               _ => throw new ArgumentOutOfRangeException (nameof (backend)),
+                       };
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRBackend, GRGlInterface) instead.")]
+               public static GRContext Create (GRBackend backend, IntPtr backendContext) =>
+                       backend switch
+                       {
+                               GRBackend.Metal => throw new NotSupportedException (),
+                               GRBackend.OpenGL => GetObject<GRContext> (SkiaApi.gr_context_make_gl (backendContext)),
+                               GRBackend.Vulkan => throw new NotSupportedException (),
+                               _ => throw new ArgumentOutOfRangeException (nameof (backend)),
+                       };
+
+               // CreateGl
+
+               public static GRContext CreateGl () =>
+                       CreateGl (null);
+
+               public static GRContext CreateGl (GRGlInterface backendContext) =>
+                       GetObject<GRContext> (SkiaApi.gr_context_make_gl (backendContext == null ? IntPtr.Zero : backendContext.Handle));
+
+               //
+
+               public GRBackend Backend => SkiaApi.gr_context_get_backend (Handle);
+
+               public void AbandonContext (bool releaseResources = false)
+               {
+                       if (releaseResources)
+                               SkiaApi.gr_context_release_resources_and_abandon_context (Handle);
+                       else
+                               SkiaApi.gr_context_abandon_context (Handle);
+               }
+
+               public void GetResourceCacheLimits (out int maxResources, out long maxResourceBytes)
+               {
+                       IntPtr maxResBytes;
+                       fixed (int* maxRes = &maxResources) {
+                               SkiaApi.gr_context_get_resource_cache_limits (Handle, maxRes, &maxResBytes);
+                       }
+                       maxResourceBytes = (long)maxResBytes;
+               }
+
+               public void SetResourceCacheLimits (int maxResources, long maxResourceBytes) =>
+                       SkiaApi.gr_context_set_resource_cache_limits (Handle, maxResources, (IntPtr)maxResourceBytes);
+
+               public void GetResourceCacheUsage (out int maxResources, out long maxResourceBytes)
+               {
+                       IntPtr maxResBytes;
+                       fixed (int* maxRes = &maxResources) {
+                               SkiaApi.gr_context_get_resource_cache_usage (Handle, maxRes, &maxResBytes);
+                       }
+                       maxResourceBytes = (long)maxResBytes;
+               }
+
+               public void ResetContext (GRGlBackendState state) =>
+                       ResetContext ((uint)state);
+
+               public void ResetContext (GRBackendState state = GRBackendState.All) =>
+                       ResetContext ((uint)state);
+
+               public void ResetContext (uint state) =>
+                       SkiaApi.gr_context_reset_context (Handle, state);
+
+               public void Flush () =>
+                       SkiaApi.gr_context_flush (Handle);
+
+               public int GetMaxSurfaceSampleCount (SKColorType colorType) =>
+                       SkiaApi.gr_context_get_max_surface_sample_count_for_color_type (Handle, colorType);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public int GetRecommendedSampleCount (GRPixelConfig config, float dpi) => 0;
+       }
+}
diff --git a/src/XSF/SkiaSharp/GRDefinitions.cs b/src/XSF/SkiaSharp/GRDefinitions.cs
new file mode 100644 (file)
index 0000000..88bd900
--- /dev/null
@@ -0,0 +1,408 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+using GRBackendObject = System.IntPtr;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use GRBackendRenderTarget instead.")]
+       public struct GRBackendRenderTargetDesc : IEquatable<GRBackendRenderTargetDesc>
+       {
+               public int Width { get; set; }
+               public int Height { get; set; }
+               public GRPixelConfig Config { get; set; }
+               public GRSurfaceOrigin Origin { get; set; }
+               public int SampleCount { get; set; }
+               public int StencilBits { get; set; }
+               public GRBackendObject RenderTargetHandle { get; set; }
+               public readonly SKSizeI Size => new SKSizeI (Width, Height);
+               public readonly SKRectI Rect => new SKRectI (0, 0, Width, Height);
+
+               public readonly bool Equals (GRBackendRenderTargetDesc obj) =>
+                       Width == obj.Width &&
+                       Height == obj.Height &&
+                       Config == obj.Config &&
+                       Origin == obj.Origin &&
+                       SampleCount == obj.SampleCount &&
+                       StencilBits == obj.StencilBits &&
+                       RenderTargetHandle == obj.RenderTargetHandle;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is GRBackendRenderTargetDesc f && Equals (f);
+
+               public static bool operator == (GRBackendRenderTargetDesc left, GRBackendRenderTargetDesc right) =>
+                       left.Equals (right);
+
+               public static bool operator != (GRBackendRenderTargetDesc left, GRBackendRenderTargetDesc right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (Width);
+                       hash.Add (Height);
+                       hash.Add (Config);
+                       hash.Add (Origin);
+                       hash.Add (SampleCount);
+                       hash.Add (StencilBits);
+                       hash.Add (RenderTargetHandle);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       [Flags]
+       public enum GRGlBackendState : UInt32
+       {
+               None = 0,
+               RenderTarget = 1 << 0,
+               TextureBinding = 1 << 1,
+               View = 1 << 2, // scissor and viewport
+               Blend = 1 << 3,
+               MSAAEnable = 1 << 4,
+               Vertex = 1 << 5,
+               Stencil = 1 << 6,
+               PixelStore = 1 << 7,
+               Program = 1 << 8,
+               FixedFunction = 1 << 9,
+               Misc = 1 << 10,
+               PathRendering = 1 << 11,
+               All = 0xffff
+       }
+
+       [Flags]
+       public enum GRBackendState : UInt32
+       {
+               None = 0,
+               All = 0xffffffff,
+       }
+
+       public partial struct GRGlFramebufferInfo
+       {
+               public GRGlFramebufferInfo (uint fboId)
+               {
+                       fFBOID = fboId;
+                       fFormat = 0;
+               }
+
+               public GRGlFramebufferInfo (uint fboId, uint format)
+               {
+                       fFBOID = fboId;
+                       fFormat = format;
+               }
+       }
+
+       public partial struct GRGlTextureInfo
+       {
+               public GRGlTextureInfo (uint target, uint id)
+               {
+                       fTarget = target;
+                       fID = id;
+                       fFormat = 0;
+               }
+
+               public GRGlTextureInfo (uint target, uint id, uint format)
+               {
+                       fTarget = target;
+                       fID = id;
+                       fFormat = format;
+               }
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Flags]
+       [Obsolete]
+       public enum GRBackendTextureDescFlags
+       {
+               None = 0,
+               RenderTarget = 1,
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use GRBackendTexture instead.")]
+       [StructLayout (LayoutKind.Sequential)]
+       public struct GRBackendTextureDesc : IEquatable<GRBackendTextureDesc>
+       {
+               public GRBackendTextureDescFlags Flags { get; set; }
+               public GRSurfaceOrigin Origin { get; set; }
+               public int Width { get; set; }
+               public int Height { get; set; }
+               public GRPixelConfig Config { get; set; }
+               public int SampleCount { get; set; }
+               public GRBackendObject TextureHandle { get; set; }
+               public readonly SKSizeI Size => new SKSizeI (Width, Height);
+               public readonly SKRectI Rect => new SKRectI (0, 0, Width, Height);
+
+               public readonly bool Equals (GRBackendTextureDesc obj) =>
+                       Flags == obj.Flags &&
+                       Origin == obj.Origin &&
+                       Width == obj.Width &&
+                       Height == obj.Height &&
+                       Config == obj.Config &&
+                       SampleCount == obj.SampleCount &&
+                       TextureHandle == obj.TextureHandle;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is GRBackendTextureDesc f && Equals (f);
+
+               public static bool operator == (GRBackendTextureDesc left, GRBackendTextureDesc right) =>
+                       left.Equals (right);
+
+               public static bool operator != (GRBackendTextureDesc left, GRBackendTextureDesc right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (Flags);
+                       hash.Add (Origin);
+                       hash.Add (Width);
+                       hash.Add (Height);
+                       hash.Add (Config);
+                       hash.Add (SampleCount);
+                       hash.Add (TextureHandle);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("Use GRBackendTexture instead.")]
+       public struct GRGlBackendTextureDesc : IEquatable<GRGlBackendTextureDesc>
+       {
+               public GRBackendTextureDescFlags Flags { get; set; }
+               public GRSurfaceOrigin Origin { get; set; }
+               public int Width { get; set; }
+               public int Height { get; set; }
+               public GRPixelConfig Config { get; set; }
+               public int SampleCount { get; set; }
+               public GRGlTextureInfo TextureHandle { get; set; }
+               public readonly SKSizeI Size => new SKSizeI (Width, Height);
+               public readonly SKRectI Rect => new SKRectI (0, 0, Width, Height);
+
+               public readonly bool Equals (GRGlBackendTextureDesc obj) =>
+                       Flags == obj.Flags &&
+                       Origin == obj.Origin &&
+                       Width == obj.Width &&
+                       Height == obj.Height &&
+                       Config == obj.Config &&
+                       SampleCount == obj.SampleCount &&
+                       TextureHandle == obj.TextureHandle;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is GRGlBackendTextureDesc f && Equals (f);
+
+               public static bool operator == (GRGlBackendTextureDesc left, GRGlBackendTextureDesc right) =>
+                       left.Equals (right);
+
+               public static bool operator != (GRGlBackendTextureDesc left, GRGlBackendTextureDesc right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (Flags);
+                       hash.Add (Origin);
+                       hash.Add (Width);
+                       hash.Add (Height);
+                       hash.Add (Config);
+                       hash.Add (SampleCount);
+                       hash.Add (TextureHandle);
+                       return hash.ToHashCode ();
+               }
+       }
+
+       public static partial class SkiaExtensions
+       {
+               public static uint ToGlSizedFormat (this SKColorType colorType) =>
+                       colorType switch
+                       {
+                               SKColorType.Unknown => 0,
+                               SKColorType.Alpha8 => GRGlSizedFormat.ALPHA8,
+                               SKColorType.Rgb565 => GRGlSizedFormat.RGB565,
+                               SKColorType.Argb4444 => GRGlSizedFormat.RGBA4,
+                               SKColorType.Rgba8888 => GRGlSizedFormat.RGBA8,
+                               SKColorType.Rgb888x => GRGlSizedFormat.RGB8,
+                               SKColorType.Bgra8888 => GRGlSizedFormat.BGRA8,
+                               SKColorType.Rgba1010102 => GRGlSizedFormat.RGB10_A2,
+                               SKColorType.Rgb101010x => 0,
+                               SKColorType.Gray8 => GRGlSizedFormat.LUMINANCE8,
+                               SKColorType.RgbaF16 => GRGlSizedFormat.RGBA16F,
+                               _ => throw new ArgumentOutOfRangeException (nameof (colorType)),
+                       };
+
+               public static uint ToGlSizedFormat (this GRPixelConfig config) =>
+                       config switch
+                       {
+                               GRPixelConfig.Alpha8 => GRGlSizedFormat.ALPHA8,
+                               GRPixelConfig.Gray8 => GRGlSizedFormat.LUMINANCE8,
+                               GRPixelConfig.Rgb565 => GRGlSizedFormat.RGB565,
+                               GRPixelConfig.Rgba4444 => GRGlSizedFormat.RGBA4,
+                               GRPixelConfig.Rgba8888 => GRGlSizedFormat.RGBA8,
+                               GRPixelConfig.Rgb888 => GRGlSizedFormat.RGB8,
+                               GRPixelConfig.Bgra8888 => GRGlSizedFormat.BGRA8,
+                               GRPixelConfig.Srgba8888 => GRGlSizedFormat.SRGB8_ALPHA8,
+                               GRPixelConfig.Sbgra8888 => GRGlSizedFormat.SRGB8_ALPHA8,
+                               GRPixelConfig.Rgba1010102 => GRGlSizedFormat.RGB10_A2,
+                               GRPixelConfig.RgbaFloat => GRGlSizedFormat.RGBA32F,
+                               GRPixelConfig.RgFloat => GRGlSizedFormat.RG32F,
+                               GRPixelConfig.AlphaHalf => GRGlSizedFormat.R16F,
+                               GRPixelConfig.RgbaHalf => GRGlSizedFormat.RGBA16F,
+                               GRPixelConfig.Unknown => 0,
+                               _ => throw new ArgumentOutOfRangeException (nameof (config)),
+                       };
+
+               public static GRPixelConfig ToPixelConfig (this SKColorType colorType) =>
+                       colorType switch
+                       {
+                               SKColorType.Unknown => GRPixelConfig.Unknown,
+                               SKColorType.Alpha8 => GRPixelConfig.Alpha8,
+                               SKColorType.Gray8 => GRPixelConfig.Gray8,
+                               SKColorType.Rgb565 => GRPixelConfig.Rgb565,
+                               SKColorType.Argb4444 => GRPixelConfig.Rgba4444,
+                               SKColorType.Rgba8888 => GRPixelConfig.Rgba8888,
+                               SKColorType.Rgb888x => GRPixelConfig.Rgb888,
+                               SKColorType.Bgra8888 => GRPixelConfig.Bgra8888,
+                               SKColorType.Rgba1010102 => GRPixelConfig.Rgba1010102,
+                               SKColorType.RgbaF16 => GRPixelConfig.RgbaHalf,
+                               SKColorType.Rgb101010x => GRPixelConfig.Unknown,
+                               _ => throw new ArgumentOutOfRangeException (nameof (colorType)),
+                       };
+
+               public static SKColorType ToColorType (this GRPixelConfig config) =>
+                       config switch
+                       {
+                               GRPixelConfig.Unknown => SKColorType.Unknown,
+                               GRPixelConfig.Alpha8 => SKColorType.Alpha8,
+                               GRPixelConfig.Gray8 => SKColorType.Gray8,
+                               GRPixelConfig.Rgb565 => SKColorType.Rgb565,
+                               GRPixelConfig.Rgba4444 => SKColorType.Argb4444,
+                               GRPixelConfig.Rgba8888 => SKColorType.Rgba8888,
+                               GRPixelConfig.Rgb888 => SKColorType.Rgb888x,
+                               GRPixelConfig.Bgra8888 => SKColorType.Bgra8888,
+                               GRPixelConfig.Srgba8888 => SKColorType.Rgba8888,
+                               GRPixelConfig.Sbgra8888 => SKColorType.Bgra8888,
+                               GRPixelConfig.Rgba1010102 => SKColorType.Rgba1010102,
+                               GRPixelConfig.RgbaFloat => SKColorType.Unknown,
+                               GRPixelConfig.RgFloat => SKColorType.Unknown,
+                               GRPixelConfig.AlphaHalf => SKColorType.Unknown,
+                               GRPixelConfig.RgbaHalf => SKColorType.RgbaF16,
+                               _ => throw new ArgumentOutOfRangeException (nameof (config)),
+                       };
+       }
+
+       internal static class GRGlSizedFormat
+       {
+               // Unsized formats
+               internal const uint STENCIL_INDEX = 0x1901;
+               internal const uint DEPTH_COMPONENT = 0x1902;
+               internal const uint DEPTH_STENCIL = 0x84F9;
+               internal const uint RED = 0x1903;
+               internal const uint RED_INTEGER = 0x8D94;
+               internal const uint GREEN = 0x1904;
+               internal const uint BLUE = 0x1905;
+               internal const uint ALPHA = 0x1906;
+               internal const uint LUMINANCE = 0x1909;
+               internal const uint LUMINANCE_ALPHA = 0x190A;
+               internal const uint RG_INTEGER = 0x8228;
+               internal const uint RGB = 0x1907;
+               internal const uint RGB_INTEGER = 0x8D98;
+               internal const uint SRGB = 0x8C40;
+               internal const uint RGBA = 0x1908;
+               internal const uint RG = 0x8227;
+               internal const uint SRGB_ALPHA = 0x8C42;
+               internal const uint RGBA_INTEGER = 0x8D99;
+               internal const uint BGRA = 0x80E1;
+
+               // Stencil index sized formats
+               internal const uint STENCIL_INDEX4 = 0x8D47;
+               internal const uint STENCIL_INDEX8 = 0x8D48;
+               internal const uint STENCIL_INDEX16 = 0x8D49;
+
+               // Depth component sized formats
+               internal const uint DEPTH_COMPONENT16 = 0x81A5;
+
+               // Depth stencil sized formats
+               internal const uint DEPTH24_STENCIL8 = 0x88F0;
+
+               // Red sized formats
+               internal const uint R8 = 0x8229;
+               internal const uint R16 = 0x822A;
+               internal const uint R16F = 0x822D;
+               internal const uint R32F = 0x822E;
+
+               // Red integer sized formats
+               internal const uint R8I = 0x8231;
+               internal const uint R8UI = 0x8232;
+               internal const uint R16I = 0x8233;
+               internal const uint R16UI = 0x8234;
+               internal const uint R32I = 0x8235;
+               internal const uint R32UI = 0x8236;
+
+               // Luminance sized formats
+               internal const uint LUMINANCE8 = 0x8040;
+
+               // Alpha sized formats
+               internal const uint ALPHA8 = 0x803C;
+               internal const uint ALPHA16 = 0x803E;
+               internal const uint ALPHA16F = 0x881C;
+               internal const uint ALPHA32F = 0x8816;
+
+               // Alpha integer sized formats
+               internal const uint ALPHA8I = 0x8D90;
+               internal const uint ALPHA8UI = 0x8D7E;
+               internal const uint ALPHA16I = 0x8D8A;
+               internal const uint ALPHA16UI = 0x8D78;
+               internal const uint ALPHA32I = 0x8D84;
+               internal const uint ALPHA32UI = 0x8D72;
+
+               // RG sized formats
+               internal const uint RG8 = 0x822B;
+               internal const uint RG16 = 0x822C;
+               //internal const uint R16F = 0x822D;
+               //internal const uint R32F = 0x822E;
+
+               // RG sized integer formats
+               internal const uint RG8I = 0x8237;
+               internal const uint RG8UI = 0x8238;
+               internal const uint RG16I = 0x8239;
+               internal const uint RG16UI = 0x823A;
+               internal const uint RG32I = 0x823B;
+               internal const uint RG32UI = 0x823C;
+
+               // RGB sized formats
+               internal const uint RGB5 = 0x8050;
+               internal const uint RGB565 = 0x8D62;
+               internal const uint RGB8 = 0x8051;
+               internal const uint SRGB8 = 0x8C41;
+
+               // RGB integer sized formats
+               internal const uint RGB8I = 0x8D8F;
+               internal const uint RGB8UI = 0x8D7D;
+               internal const uint RGB16I = 0x8D89;
+               internal const uint RGB16UI = 0x8D77;
+               internal const uint RGB32I = 0x8D83;
+               internal const uint RGB32UI = 0x8D71;
+
+               // RGBA sized formats
+               internal const uint RGBA4 = 0x8056;
+               internal const uint RGB5_A1 = 0x8057;
+               internal const uint RGBA8 = 0x8058;
+               internal const uint RGB10_A2 = 0x8059;
+               internal const uint SRGB8_ALPHA8 = 0x8C43;
+               internal const uint RGBA16F = 0x881A;
+               internal const uint RGBA32F = 0x8814;
+               internal const uint RG32F = 0x8230;
+
+               // RGBA integer sized formats
+               internal const uint RGBA8I = 0x8D8E;
+               internal const uint RGBA8UI = 0x8D7C;
+               internal const uint RGBA16I = 0x8D88;
+               internal const uint RGBA16UI = 0x8D76;
+               internal const uint RGBA32I = 0x8D82;
+               internal const uint RGBA32UI = 0x8D70;
+
+               // BGRA sized formats
+               internal const uint BGRA8 = 0x93A1;
+       }
+}
diff --git a/src/XSF/SkiaSharp/GRGlInterface.cs b/src/XSF/SkiaSharp/GRGlInterface.cs
new file mode 100644 (file)
index 0000000..98f1fd2
--- /dev/null
@@ -0,0 +1,853 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public unsafe class GRGlInterface : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal GRGlInterface (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static GRGlInterface CreateDefaultInterface ()
+               {
+                       // first try ANGLE, then fall back to the OpenGL-based
+                       return CreateNativeAngleInterface () ?? CreateNativeGlInterface ();
+               }
+
+               public static GRGlInterface CreateNativeGlInterface ()
+               {
+                       // the native code will automatically return null on non-OpenGL platforms, such as UWP
+                       return GetObject<GRGlInterface> (SkiaApi.gr_glinterface_create_native_interface ());
+               }
+               
+               public static GRGlInterface CreateNativeAngleInterface ()
+               {
+                       if (PlatformConfiguration.IsWindows) {
+                               return AssembleAngleInterface (AngleLoader.GetProc);
+                       } else {
+                               // return null on non-DirectX platforms: everything except Windows
+                               return null;
+                       }
+               }
+
+               public static GRGlInterface CreateNativeEvasInterface (IntPtr evas)
+               {
+                       var evasLoader = new EvasGlLoader(evas);
+                       return AssembleGlesInterface((ctx, name) => evasLoader.GetFunctionPointer(name));
+               }
+
+               public static GRGlInterface AssembleInterface (GRGlGetProcDelegate get)
+               {
+                       return AssembleInterface (null, get);
+               }
+
+               public static GRGlInterface AssembleInterface (object context, GRGlGetProcDelegate get)
+               {
+                       // if on Windows, try ANGLE
+                       if (PlatformConfiguration.IsWindows) {
+                               var angle = AssembleAngleInterface (context, get);
+                               if (angle != null) {
+                                       return angle;
+                               }
+                       }
+
+                       // try the native default
+                       var del = get != null && context != null
+                               ? new GRGlGetProcDelegate ((_, name) => get (context, name))
+                               : get;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.GRGlGetProcDelegateProxy, out var gch, out var ctx);
+                       try {
+                               return GetObject<GRGlInterface> (SkiaApi.gr_glinterface_assemble_interface ((void*)ctx, proxy));
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               public static GRGlInterface AssembleAngleInterface (GRGlGetProcDelegate get)
+               {
+                       return AssembleAngleInterface (null, get);
+               }
+
+               public static GRGlInterface AssembleAngleInterface (object context, GRGlGetProcDelegate get)
+               {
+                       // ANGLE is just a GLES v2 over DX v9+
+                       return AssembleGlesInterface (context, get);
+               }
+
+               public static GRGlInterface AssembleGlInterface (GRGlGetProcDelegate get)
+               {
+                       return AssembleGlInterface (null, get);
+               }
+
+               public static GRGlInterface AssembleGlInterface (object context, GRGlGetProcDelegate get)
+               {
+                       var del = get != null && context != null
+                               ? new GRGlGetProcDelegate ((_, name) => get (context, name))
+                               : get;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.GRGlGetProcDelegateProxy, out var gch, out var ctx);
+                       try {
+                               return GetObject<GRGlInterface> (SkiaApi.gr_glinterface_assemble_gl_interface ((void*)ctx, proxy));
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               public static GRGlInterface AssembleGlesInterface (GRGlGetProcDelegate get)
+               {
+                       return AssembleGlesInterface (null, get);
+               }
+
+               public static GRGlInterface AssembleGlesInterface (object context, GRGlGetProcDelegate get)
+               {
+                       var del = get != null && context != null
+                               ? new GRGlGetProcDelegate ((_, name) => get (context, name))
+                               : get;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.GRGlGetProcDelegateProxy, out var gch, out var ctx);
+                       try {
+                               return GetObject<GRGlInterface> (SkiaApi.gr_glinterface_assemble_gles_interface ((void*)ctx, proxy));
+                       } finally {
+                               gch.Free ();
+                       }
+               }
+
+               public bool Validate ()
+               {
+                       return SkiaApi.gr_glinterface_validate (Handle);
+               }
+
+               public bool HasExtension (string extension)
+               {
+                       return SkiaApi.gr_glinterface_has_extension (Handle, extension);
+               }
+
+               private static class AngleLoader
+               {
+                       private static readonly IntPtr libEGL;
+                       private static readonly IntPtr libGLESv2;
+
+                       [DllImport ("Kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
+                       private static extern IntPtr LoadLibrary ([MarshalAs (UnmanagedType.LPStr)] string lpFileName);
+
+                       [DllImport ("Kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
+                       private static extern IntPtr GetProcAddress (IntPtr hModule, [MarshalAs (UnmanagedType.LPStr)] string lpProcName);
+
+                       [DllImport ("libEGL.dll")]
+                       private static extern IntPtr eglGetProcAddress ([MarshalAs (UnmanagedType.LPStr)] string procname);
+
+                       static AngleLoader()
+                       {
+                               // this is not supported at all on non-Windows platforms
+                               if (!PlatformConfiguration.IsWindows) {
+                                       return;
+                               }
+
+                               libEGL = LoadLibrary ("libEGL.dll");
+                               if (Marshal.GetLastWin32Error () != 0 || libEGL == IntPtr.Zero)
+                                       throw new DllNotFoundException ("Unable to load libEGL.dll.");
+
+                               libGLESv2 = LoadLibrary ("libGLESv2.dll");
+                               if (Marshal.GetLastWin32Error () != 0 || libGLESv2 == IntPtr.Zero)
+                                       throw new DllNotFoundException ("Unable to load libGLESv2.dll.");
+                       }
+
+                       // function to assemble the ANGLE interface
+                       public static IntPtr GetProc (object context, string name)
+                       {
+                               // this is not supported at all on non-Windows platforms
+                               if (!PlatformConfiguration.IsWindows) {
+                                       return IntPtr.Zero;
+                               }
+
+                               IntPtr proc = GetProcAddress (libGLESv2, name);
+                               if (proc == IntPtr.Zero)
+                               {
+                                       proc = GetProcAddress (libEGL, name);
+                               }
+                               if (proc == IntPtr.Zero)
+                               {
+                                       proc = eglGetProcAddress (name);
+                               }
+                               return proc;
+                       }
+               }
+
+               private class EvasGlLoader
+               {
+                       private IntPtr glEvas;
+                       private EvasGlApi api;
+
+                       [DllImport ("libevas.so.1")]
+                       internal static extern IntPtr evas_gl_api_get (IntPtr evas_gl);
+
+                       [DllImport ("libevas.so.1")]
+                       internal static extern IntPtr evas_gl_proc_address_get (IntPtr evas_gl, string name);
+
+                       public EvasGlLoader (IntPtr evas)
+                       {
+                               glEvas = evas;
+
+                               var unmanagedGlApi = evas_gl_api_get (glEvas);
+                               api = Marshal.PtrToStructure<EvasGlApi> (unmanagedGlApi);
+                       }
+
+                       public IntPtr GetFunctionPointer(string name)
+                       {
+                               var ret = evas_gl_proc_address_get (glEvas, name);
+
+                               if (ret == IntPtr.Zero) {
+                                       var field = typeof (EvasGlApi).GetField (name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+                                       if (field?.FieldType == typeof (IntPtr))
+                                               ret = (IntPtr) field.GetValue (api);
+                               }
+
+                               return ret;
+                       }
+               }
+
+               // this structure is initialized from a native pointer
+               [Preserve (AllMembers = true)]
+               private struct EvasGlApi
+               {
+                       // DO NOT change the order, needs to be as specified in struct _Evas_GL_API (/platform/upstream/efl/src/lib/evas/Evas_GL.h)
+                       // DO NOT change the names, they need to match the OpenGL API
+#pragma warning disable 0169
+                       private int version;
+                       private IntPtr glActiveTexture;
+                       private IntPtr glAttachShader;
+                       private IntPtr glBindAttribLocation;
+                       private IntPtr glBindBuffer;
+                       private IntPtr glBindFramebuffer;
+                       private IntPtr glBindRenderbuffer;
+                       private IntPtr glBindTexture;
+                       private IntPtr glBlendColor;
+                       private IntPtr glBlendEquation;
+                       private IntPtr glBlendEquationSeparate;
+                       private IntPtr glBlendFunc;
+                       private IntPtr glBlendFuncSeparate;
+                       private IntPtr glBufferData;
+                       private IntPtr glBufferSubData;
+                       private IntPtr glCheckFramebufferStatus;
+                       private IntPtr glClear;
+                       private IntPtr glClearColor;
+                       private IntPtr glClearDepthf;
+                       private IntPtr glClearStencil;
+                       private IntPtr glColorMask;
+                       private IntPtr glCompileShader;
+                       private IntPtr glCompressedTexImage2D;
+                       private IntPtr glCompressedTexSubImage2D;
+                       private IntPtr glCopyTexImage2D;
+                       private IntPtr glCopyTexSubImage2D;
+                       private IntPtr glCreateProgram;
+                       private IntPtr glCreateShader;
+                       private IntPtr glCullFace;
+                       private IntPtr glDeleteBuffers;
+                       private IntPtr glDeleteFramebuffers;
+                       private IntPtr glDeleteProgram;
+                       private IntPtr glDeleteRenderbuffers;
+                       private IntPtr glDeleteShader;
+                       private IntPtr glDeleteTextures;
+                       private IntPtr glDepthFunc;
+                       private IntPtr glDepthMask;
+                       private IntPtr glDepthRangef;
+                       private IntPtr glDetachShader;
+                       private IntPtr glDisable;
+                       private IntPtr glDisableVertexAttribArray;
+                       private IntPtr glDrawArrays;
+                       private IntPtr glDrawElements;
+                       private IntPtr glEnable;
+                       private IntPtr glEnableVertexAttribArray;
+                       private IntPtr glFinish;
+                       private IntPtr glFlush;
+                       private IntPtr glFramebufferRenderbuffer;
+                       private IntPtr glFramebufferTexture2D;
+                       private IntPtr glFrontFace;
+                       private IntPtr glGenBuffers;
+                       private IntPtr glGenerateMipmap;
+                       private IntPtr glGenFramebuffers;
+                       private IntPtr glGenRenderbuffers;
+                       private IntPtr glGenTextures;
+                       private IntPtr glGetActiveAttrib;
+                       private IntPtr glGetActiveUniform;
+                       private IntPtr glGetAttachedShaders;
+                       private IntPtr glGetAttribLocation;
+                       private IntPtr glGetBooleanv;
+                       private IntPtr glGetBufferParameteriv;
+                       private IntPtr glGetError;
+                       private IntPtr glGetFloatv;
+                       private IntPtr glGetFramebufferAttachmentParameteriv;
+                       private IntPtr glGetIntegerv;
+                       private IntPtr glGetProgramiv;
+                       private IntPtr glGetProgramInfoLog;
+                       private IntPtr glGetRenderbufferParameteriv;
+                       private IntPtr glGetShaderiv;
+                       private IntPtr glGetShaderInfoLog;
+                       private IntPtr glGetShaderPrecisionFormat;
+                       private IntPtr glGetShaderSource;
+                       private IntPtr glGetString;
+                       private IntPtr glGetTexParameterfv;
+                       private IntPtr glGetTexParameteriv;
+                       private IntPtr glGetUniformfv;
+                       private IntPtr glGetUniformiv;
+                       private IntPtr glGetUniformLocation;
+                       private IntPtr glGetVertexAttribfv;
+                       private IntPtr glGetVertexAttribiv;
+                       private IntPtr glGetVertexAttribPointerv;
+                       private IntPtr glHint;
+                       private IntPtr glIsBuffer;
+                       private IntPtr glIsEnabled;
+                       private IntPtr glIsFramebuffer;
+                       private IntPtr glIsProgram;
+                       private IntPtr glIsRenderbuffer;
+                       private IntPtr glIsShader;
+                       private IntPtr glIsTexture;
+                       private IntPtr glLineWidth;
+                       private IntPtr glLinkProgram;
+                       private IntPtr glPixelStorei;
+                       private IntPtr glPolygonOffset;
+                       private IntPtr glReadPixels;
+                       private IntPtr glReleaseShaderCompiler;
+                       private IntPtr glRenderbufferStorage;
+                       private IntPtr glSampleCoverage;
+                       private IntPtr glScissor;
+                       private IntPtr glShaderBinary;
+                       private IntPtr glShaderSource;
+                       private IntPtr glStencilFunc;
+                       private IntPtr glStencilFuncSeparate;
+                       private IntPtr glStencilMask;
+                       private IntPtr glStencilMaskSeparate;
+                       private IntPtr glStencilOp;
+                       private IntPtr glStencilOpSeparate;
+                       private IntPtr glTexImage2D;
+                       private IntPtr glTexParameterf;
+                       private IntPtr glTexParameterfv;
+                       private IntPtr glTexParameteri;
+                       private IntPtr glTexParameteriv;
+                       private IntPtr glTexSubImage2D;
+                       private IntPtr glUniform1f;
+                       private IntPtr glUniform1fv;
+                       private IntPtr glUniform1i;
+                       private IntPtr glUniform1iv;
+                       private IntPtr glUniform2f;
+                       private IntPtr glUniform2fv;
+                       private IntPtr glUniform2i;
+                       private IntPtr glUniform2iv;
+                       private IntPtr glUniform3f;
+                       private IntPtr glUniform3fv;
+                       private IntPtr glUniform3i;
+                       private IntPtr glUniform3iv;
+                       private IntPtr glUniform4f;
+                       private IntPtr glUniform4fv;
+                       private IntPtr glUniform4i;
+                       private IntPtr glUniform4iv;
+                       private IntPtr glUniformMatrix2fv;
+                       private IntPtr glUniformMatrix3fv;
+                       private IntPtr glUniformMatrix4fv;
+                       private IntPtr glUseProgram;
+                       private IntPtr glValidateProgram;
+                       private IntPtr glVertexAttrib1f;
+                       private IntPtr glVertexAttrib1fv;
+                       private IntPtr glVertexAttrib2f;
+                       private IntPtr glVertexAttrib2fv;
+                       private IntPtr glVertexAttrib3f;
+                       private IntPtr glVertexAttrib3fv;
+                       private IntPtr glVertexAttrib4f;
+                       private IntPtr glVertexAttrib4fv;
+                       private IntPtr glVertexAttribPointer;
+                       private IntPtr glViewport;
+                       private IntPtr glEvasGLImageTargetTexture2DOES;
+                       private IntPtr glEvasGLImageTargetRenderbufferStorageOES;
+                       private IntPtr glGetProgramBinaryOES;
+                       private IntPtr glProgramBinaryOES;
+                       private IntPtr glMapBufferOES;
+                       private IntPtr glUnmapBufferOES;
+                       private IntPtr glGetBufferPointervOES;
+                       private IntPtr glTexImage3DOES;
+                       private IntPtr glTexSubImage3DOES;
+                       private IntPtr glCopyTexSubImage3DOES;
+                       private IntPtr glCompressedTexImage3DOES;
+                       private IntPtr glCompressedTexSubImage3DOES;
+                       private IntPtr glFramebufferTexture3DOES;
+                       private IntPtr glGetPerfMonitorGroupsAMD;
+                       private IntPtr glGetPerfMonitorCountersAMD;
+                       private IntPtr glGetPerfMonitorGroupStringAMD;
+                       private IntPtr glGetPerfMonitorCounterStringAMD;
+                       private IntPtr glGetPerfMonitorCounterInfoAMD;
+                       private IntPtr glGenPerfMonitorsAMD;
+                       private IntPtr glDeletePerfMonitorsAMD;
+                       private IntPtr glSelectPerfMonitorCountersAMD;
+                       private IntPtr glBeginPerfMonitorAMD;
+                       private IntPtr glEndPerfMonitorAMD;
+                       private IntPtr glGetPerfMonitorCounterDataAMD;
+                       private IntPtr glDiscardFramebufferEXT;
+                       private IntPtr glMultiDrawArraysEXT;
+                       private IntPtr glMultiDrawElementsEXT;
+                       private IntPtr glDeleteFencesNV;
+                       private IntPtr glGenFencesNV;
+                       private IntPtr glIsFenceNV;
+                       private IntPtr glTestFenceNV;
+                       private IntPtr glGetFenceivNV;
+                       private IntPtr glFinishFenceNV;
+                       private IntPtr glSetFenceNV;
+                       private IntPtr glGetDriverControlsQCOM;
+                       private IntPtr glGetDriverControlStringQCOM;
+                       private IntPtr glEnableDriverControlQCOM;
+                       private IntPtr glDisableDriverControlQCOM;
+                       private IntPtr glExtGetTexturesQCOM;
+                       private IntPtr glExtGetBuffersQCOM;
+                       private IntPtr glExtGetRenderbuffersQCOM;
+                       private IntPtr glExtGetFramebuffersQCOM;
+                       private IntPtr glExtGetTexLevelParameterivQCOM;
+                       private IntPtr glExtTexObjectStateOverrideiQCOM;
+                       private IntPtr glExtGetTexSubImageQCOM;
+                       private IntPtr glExtGetBufferPointervQCOM;
+                       private IntPtr glExtGetShadersQCOM;
+                       private IntPtr glExtGetProgramsQCOM;
+                       private IntPtr glExtIsProgramBinaryQCOM;
+                       private IntPtr glExtGetProgramBinarySourceQCOM;
+                       private IntPtr evasglCreateImage;
+                       private IntPtr evasglDestroyImage;
+                       private IntPtr evasglCreateImageForContext;
+                       private IntPtr glAlphaFunc;
+                       private IntPtr glClipPlanef;
+                       private IntPtr glColor4f;
+                       private IntPtr glFogf;
+                       private IntPtr glFogfv;
+                       private IntPtr glFrustumf;
+                       private IntPtr glGetClipPlanef;
+                       private IntPtr glGetLightfv;
+                       private IntPtr glGetMaterialfv;
+                       private IntPtr glGetTexEnvfv;
+                       private IntPtr glLightModelf;
+                       private IntPtr glLightModelfv;
+                       private IntPtr glLightf;
+                       private IntPtr glLightfv;
+                       private IntPtr glLoadMatrixf;
+                       private IntPtr glMaterialf;
+                       private IntPtr glMaterialfv;
+                       private IntPtr glMultMatrixf;
+                       private IntPtr glMultiTexCoord4f;
+                       private IntPtr glNormal3f;
+                       private IntPtr glOrthof;
+                       private IntPtr glPointParameterf;
+                       private IntPtr glPointParameterfv;
+                       private IntPtr glPointSize;
+                       private IntPtr glPointSizePointerOES;
+                       private IntPtr glRotatef;
+                       private IntPtr glScalef;
+                       private IntPtr glTexEnvf;
+                       private IntPtr glTexEnvfv;
+                       private IntPtr glTranslatef;
+                       private IntPtr glAlphaFuncx;
+                       private IntPtr glClearColorx;
+                       private IntPtr glClearDepthx;
+                       private IntPtr glClientActiveTexture;
+                       private IntPtr glClipPlanex;
+                       private IntPtr glColor4ub;
+                       private IntPtr glColor4x;
+                       private IntPtr glColorPointer;
+                       private IntPtr glDepthRangex;
+                       private IntPtr glDisableClientState;
+                       private IntPtr glEnableClientState;
+                       private IntPtr glFogx;
+                       private IntPtr glFogxv;
+                       private IntPtr glFrustumx;
+                       private IntPtr glGetClipPlanex;
+                       private IntPtr glGetFixedv;
+                       private IntPtr glGetLightxv;
+                       private IntPtr glGetMaterialxv;
+                       private IntPtr glGetPointerv;
+                       private IntPtr glGetTexEnviv;
+                       private IntPtr glGetTexEnvxv;
+                       private IntPtr glGetTexParameterxv;
+                       private IntPtr glLightModelx;
+                       private IntPtr glLightModelxv;
+                       private IntPtr glLightx;
+                       private IntPtr glLightxv;
+                       private IntPtr glLineWidthx;
+                       private IntPtr glLoadIdentity;
+                       private IntPtr glLoadMatrixx;
+                       private IntPtr glLogicOp;
+                       private IntPtr glMaterialx;
+                       private IntPtr glMaterialxv;
+                       private IntPtr glMatrixMode;
+                       private IntPtr glMultMatrixx;
+                       private IntPtr glMultiTexCoord4x;
+                       private IntPtr glNormal3x;
+                       private IntPtr glNormalPointer;
+                       private IntPtr glOrthox;
+                       private IntPtr glPointParameterx;
+                       private IntPtr glPointParameterxv;
+                       private IntPtr glPointSizex;
+                       private IntPtr glPolygonOffsetx;
+                       private IntPtr glPopMatrix;
+                       private IntPtr glPushMatrix;
+                       private IntPtr glRotatex;
+                       private IntPtr glSampleCoveragex;
+                       private IntPtr glScalex;
+                       private IntPtr glShadeModel;
+                       private IntPtr glTexCoordPointer;
+                       private IntPtr glTexEnvi;
+                       private IntPtr glTexEnvx;
+                       private IntPtr glTexEnviv;
+                       private IntPtr glTexEnvxv;
+                       private IntPtr glTexParameterx;
+                       private IntPtr glTexParameterxv;
+                       private IntPtr glTranslatex;
+                       private IntPtr glVertexPointer;
+                       private IntPtr glBlendEquationSeparateOES;
+                       private IntPtr glBlendFuncSeparateOES;
+                       private IntPtr glBlendEquationOES;
+                       private IntPtr glDrawTexsOES;
+                       private IntPtr glDrawTexiOES;
+                       private IntPtr glDrawTexxOES;
+                       private IntPtr glDrawTexsvOES;
+                       private IntPtr glDrawTexivOES;
+                       private IntPtr glDrawTexxvOES;
+                       private IntPtr glDrawTexfOES;
+                       private IntPtr glDrawTexfvOES;
+                       private IntPtr glAlphaFuncxOES;
+                       private IntPtr glClearColorxOES;
+                       private IntPtr glClearDepthxOES;
+                       private IntPtr glClipPlanexOES;
+                       private IntPtr glColor4xOES;
+                       private IntPtr glDepthRangexOES;
+                       private IntPtr glFogxOES;
+                       private IntPtr glFogxvOES;
+                       private IntPtr glFrustumxOES;
+                       private IntPtr glGetClipPlanexOES;
+                       private IntPtr glGetFixedvOES;
+                       private IntPtr glGetLightxvOES;
+                       private IntPtr glGetMaterialxvOES;
+                       private IntPtr glGetTexEnvxvOES;
+                       private IntPtr glGetTexParameterxvOES;
+                       private IntPtr glLightModelxOES;
+                       private IntPtr glLightModelxvOES;
+                       private IntPtr glLightxOES;
+                       private IntPtr glLightxvOES;
+                       private IntPtr glLineWidthxOES;
+                       private IntPtr glLoadMatrixxOES;
+                       private IntPtr glMaterialxOES;
+                       private IntPtr glMaterialxvOES;
+                       private IntPtr glMultMatrixxOES;
+                       private IntPtr glMultiTexCoord4xOES;
+                       private IntPtr glNormal3xOES;
+                       private IntPtr glOrthoxOES;
+                       private IntPtr glPointParameterxOES;
+                       private IntPtr glPointParameterxvOES;
+                       private IntPtr glPointSizexOES;
+                       private IntPtr glPolygonOffsetxOES;
+                       private IntPtr glRotatexOES;
+                       private IntPtr glSampleCoveragexOES;
+                       private IntPtr glScalexOES;
+                       private IntPtr glTexEnvxOES;
+                       private IntPtr glTexEnvxvOES;
+                       private IntPtr glTexParameterxOES;
+                       private IntPtr glTexParameterxvOES;
+                       private IntPtr glTranslatexOES;
+                       private IntPtr glIsRenderbufferOES;
+                       private IntPtr glBindRenderbufferOES;
+                       private IntPtr glDeleteRenderbuffersOES;
+                       private IntPtr glGenRenderbuffersOES;
+                       private IntPtr glRenderbufferStorageOES;
+                       private IntPtr glGetRenderbufferParameterivOES;
+                       private IntPtr glIsFramebufferOES;
+                       private IntPtr glBindFramebufferOES;
+                       private IntPtr glDeleteFramebuffersOES;
+                       private IntPtr glGenFramebuffersOES;
+                       private IntPtr glCheckFramebufferStatusOES;
+                       private IntPtr glFramebufferRenderbufferOES;
+                       private IntPtr glFramebufferTexture2DOES;
+                       private IntPtr glGetFramebufferAttachmentParameterivOES;
+                       private IntPtr glGenerateMipmapOES;
+                       private IntPtr glCurrentPaletteMatrixOES;
+                       private IntPtr glLoadPaletteFromModelViewMatrixOES;
+                       private IntPtr glMatrixIndexPointerOES;
+                       private IntPtr glWeightPointerOES;
+                       private IntPtr glQueryMatrixxOES;
+                       private IntPtr glDepthRangefOES;
+                       private IntPtr glFrustumfOES;
+                       private IntPtr glOrthofOES;
+                       private IntPtr glClipPlanefOES;
+                       private IntPtr glGetClipPlanefOES;
+                       private IntPtr glClearDepthfOES;
+                       private IntPtr glTexGenfOES;
+                       private IntPtr glTexGenfvOES;
+                       private IntPtr glTexGeniOES;
+                       private IntPtr glTexGenivOES;
+                       private IntPtr glTexGenxOES;
+                       private IntPtr glTexGenxvOES;
+                       private IntPtr glGetTexGenfvOES;
+                       private IntPtr glGetTexGenivOES;
+                       private IntPtr glGetTexGenxvOES;
+                       private IntPtr glBindVertexArrayOES;
+                       private IntPtr glDeleteVertexArraysOES;
+                       private IntPtr glGenVertexArraysOES;
+                       private IntPtr glIsVertexArrayOES;
+                       private IntPtr glCopyTextureLevelsAPPLE;
+                       private IntPtr glRenderbufferStorageMultisampleAPPLE;
+                       private IntPtr glResolveMultisampleFramebufferAPPLE;
+                       private IntPtr glFenceSyncAPPLE;
+                       private IntPtr glIsSyncAPPLE;
+                       private IntPtr glDeleteSyncAPPLE;
+                       private IntPtr glClientWaitSyncAPPLE;
+                       private IntPtr glWaitSyncAPPLE;
+                       private IntPtr glGetInteger64vAPPLE;
+                       private IntPtr glGetSyncivAPPLE;
+                       private IntPtr glMapBufferRangeEXT;
+                       private IntPtr glFlushMappedBufferRangeEXT;
+                       private IntPtr glRenderbufferStorageMultisampleEXT;
+                       private IntPtr glFramebufferTexture2DMultisampleEXT;
+                       private IntPtr glGetGraphicsResetStatusEXT;
+                       private IntPtr glReadnPixelsEXT;
+                       private IntPtr glGetnUniformfvEXT;
+                       private IntPtr glGetnUniformivEXT;
+                       private IntPtr glTexStorage1DEXT;
+                       private IntPtr glTexStorage2DEXT;
+                       private IntPtr glTexStorage3DEXT;
+                       private IntPtr glTextureStorage1DEXT;
+                       private IntPtr glTextureStorage2DEXT;
+                       private IntPtr glTextureStorage3DEXT;
+                       private IntPtr glClipPlanefIMG;
+                       private IntPtr glClipPlanexIMG;
+                       private IntPtr glRenderbufferStorageMultisampleIMG;
+                       private IntPtr glFramebufferTexture2DMultisampleIMG;
+                       private IntPtr glStartTilingQCOM;
+                       private IntPtr glEndTilingQCOM;
+                       private IntPtr evasglCreateSync;
+                       private IntPtr evasglDestroySync;
+                       private IntPtr evasglClientWaitSync;
+                       private IntPtr evasglSignalSync;
+                       private IntPtr evasglGetSyncAttrib;
+                       private IntPtr evasglWaitSync;
+                       private IntPtr evasglBindWaylandDisplay;
+                       private IntPtr evasglUnbindWaylandDisplay;
+                       private IntPtr evasglQueryWaylandBuffer;
+                       private IntPtr glBeginQuery;
+                       private IntPtr glBeginTransformFeedback;
+                       private IntPtr glBindBufferBase;
+                       private IntPtr glBindBufferRange;
+                       private IntPtr glBindSampler;
+                       private IntPtr glBindTransformFeedback;
+                       private IntPtr glBindVertexArray;
+                       private IntPtr glBlitFramebuffer;
+                       private IntPtr glClearBufferfi;
+                       private IntPtr glClearBufferfv;
+                       private IntPtr glClearBufferiv;
+                       private IntPtr glClearBufferuiv;
+                       private IntPtr glClientWaitSync;
+                       private IntPtr glCompressedTexImage3D;
+                       private IntPtr glCompressedTexSubImage3D;
+                       private IntPtr glCopyBufferSubData;
+                       private IntPtr glCopyTexSubImage3D;
+                       private IntPtr glDeleteQueries;
+                       private IntPtr glDeleteSamplers;
+                       private IntPtr glDeleteSync;
+                       private IntPtr glDeleteTransformFeedbacks;
+                       private IntPtr glDeleteVertexArrays;
+                       private IntPtr glDrawArraysInstanced;
+                       private IntPtr glDrawBuffers;
+                       private IntPtr glDrawElementsInstanced;
+                       private IntPtr glDrawRangeElements;
+                       private IntPtr glEndQuery;
+                       private IntPtr glEndTransformFeedback;
+                       private IntPtr glFenceSync;
+                       private IntPtr glFlushMappedBufferRange;
+                       private IntPtr glFramebufferTextureLayer;
+                       private IntPtr glGenQueries;
+                       private IntPtr glGenSamplers;
+                       private IntPtr glGenTransformFeedbacks;
+                       private IntPtr glGenVertexArrays;
+                       private IntPtr glGetActiveUniformBlockiv;
+                       private IntPtr glGetActiveUniformBlockName;
+                       private IntPtr glGetActiveUniformsiv;
+                       private IntPtr glGetBufferParameteri64v;
+                       private IntPtr glGetBufferPointerv;
+                       private IntPtr glGetFragDataLocation;
+                       private IntPtr glGetInteger64i_v;
+                       private IntPtr glGetInteger64v;
+                       private IntPtr glGetIntegeri_v;
+                       private IntPtr glGetInternalformativ;
+                       private IntPtr glGetProgramBinary;
+                       private IntPtr glGetQueryiv;
+                       private IntPtr glGetQueryObjectuiv;
+                       private IntPtr glGetSamplerParameterfv;
+                       private IntPtr glGetSamplerParameteriv;
+                       private IntPtr glGetStringi;
+                       private IntPtr glGetSynciv;
+                       private IntPtr glGetTransformFeedbackVarying;
+                       private IntPtr glGetUniformBlockIndex;
+                       private IntPtr glGetUniformIndices;
+                       private IntPtr glGetUniformuiv;
+                       private IntPtr glGetVertexAttribIiv;
+                       private IntPtr glGetVertexAttribIuiv;
+                       private IntPtr glInvalidateFramebuffer;
+                       private IntPtr glInvalidateSubFramebuffer;
+                       private IntPtr glIsQuery;
+                       private IntPtr glIsSampler;
+                       private IntPtr glIsSync;
+                       private IntPtr glIsTransformFeedback;
+                       private IntPtr glIsVertexArray;
+                       private IntPtr glMapBufferRange;
+                       private IntPtr glPauseTransformFeedback;
+                       private IntPtr glProgramBinary;
+                       private IntPtr glProgramParameteri;
+                       private IntPtr glReadBuffer;
+                       private IntPtr glRenderbufferStorageMultisample;
+                       private IntPtr glResumeTransformFeedback;
+                       private IntPtr glSamplerParameterf;
+                       private IntPtr glSamplerParameterfv;
+                       private IntPtr glSamplerParameteri;
+                       private IntPtr glSamplerParameteriv;
+                       private IntPtr glTexImage3D;
+                       private IntPtr glTexStorage2D;
+                       private IntPtr glTexStorage3D;
+                       private IntPtr glTexSubImage3D;
+                       private IntPtr glTransformFeedbackVaryings;
+                       private IntPtr glUniform1ui;
+                       private IntPtr glUniform1uiv;
+                       private IntPtr glUniform2ui;
+                       private IntPtr glUniform2uiv;
+                       private IntPtr glUniform3ui;
+                       private IntPtr glUniform3uiv;
+                       private IntPtr glUniform4ui;
+                       private IntPtr glUniform4uiv;
+                       private IntPtr glUniformBlockBinding;
+                       private IntPtr glUniformMatrix2x3fv;
+                       private IntPtr glUniformMatrix3x2fv;
+                       private IntPtr glUniformMatrix2x4fv;
+                       private IntPtr glUniformMatrix4x2fv;
+                       private IntPtr glUniformMatrix3x4fv;
+                       private IntPtr glUniformMatrix4x3fv;
+                       private IntPtr glUnmapBuffer;
+                       private IntPtr glVertexAttribDivisor;
+                       private IntPtr glVertexAttribI4i;
+                       private IntPtr glVertexAttribI4iv;
+                       private IntPtr glVertexAttribI4ui;
+                       private IntPtr glVertexAttribI4uiv;
+                       private IntPtr glVertexAttribIPointer;
+                       private IntPtr glWaitSync;
+                       private IntPtr glDispatchCompute;
+                       private IntPtr glDispatchComputeIndirect;
+                       private IntPtr glDrawArraysIndirect;
+                       private IntPtr glDrawElementsIndirect;
+                       private IntPtr glFramebufferParameteri;
+                       private IntPtr glGetFramebufferParameteriv;
+                       private IntPtr glGetProgramInterfaceiv;
+                       private IntPtr glGetProgramResourceIndex;
+                       private IntPtr glGetProgramResourceName;
+                       private IntPtr glGetProgramResourceiv;
+                       private IntPtr glGetProgramResourceLocation;
+                       private IntPtr glUseProgramStages;
+                       private IntPtr glActiveShaderProgram;
+                       private IntPtr glCreateShaderProgramv;
+                       private IntPtr glBindProgramPipeline;
+                       private IntPtr glDeleteProgramPipelines;
+                       private IntPtr glGenProgramPipelines;
+                       private IntPtr glIsProgramPipeline;
+                       private IntPtr glGetProgramPipelineiv;
+                       private IntPtr glProgramUniform1i;
+                       private IntPtr glProgramUniform2i;
+                       private IntPtr glProgramUniform3i;
+                       private IntPtr glProgramUniform4i;
+                       private IntPtr glProgramUniform1ui;
+                       private IntPtr glProgramUniform2ui;
+                       private IntPtr glProgramUniform3ui;
+                       private IntPtr glProgramUniform4ui;
+                       private IntPtr glProgramUniform1f;
+                       private IntPtr glProgramUniform2f;
+                       private IntPtr glProgramUniform3f;
+                       private IntPtr glProgramUniform4f;
+                       private IntPtr glProgramUniform1iv;
+                       private IntPtr glProgramUniform2iv;
+                       private IntPtr glProgramUniform3iv;
+                       private IntPtr glProgramUniform4iv;
+                       private IntPtr glProgramUniform1uiv;
+                       private IntPtr glProgramUniform2uiv;
+                       private IntPtr glProgramUniform3uiv;
+                       private IntPtr glProgramUniform4uiv;
+                       private IntPtr glProgramUniform1fv;
+                       private IntPtr glProgramUniform2fv;
+                       private IntPtr glProgramUniform3fv;
+                       private IntPtr glProgramUniform4fv;
+                       private IntPtr glProgramUniformMatrix2fv;
+                       private IntPtr glProgramUniformMatrix3fv;
+                       private IntPtr glProgramUniformMatrix4fv;
+                       private IntPtr glProgramUniformMatrix2x3fv;
+                       private IntPtr glProgramUniformMatrix3x2fv;
+                       private IntPtr glProgramUniformMatrix2x4fv;
+                       private IntPtr glProgramUniformMatrix4x2fv;
+                       private IntPtr glProgramUniformMatrix3x4fv;
+                       private IntPtr glProgramUniformMatrix4x3fv;
+                       private IntPtr glValidateProgramPipeline;
+                       private IntPtr glGetProgramPipelineInfoLog;
+                       private IntPtr glBindImageTexture;
+                       private IntPtr glGetBooleani_v;
+                       private IntPtr glMemoryBarrier;
+                       private IntPtr glMemoryBarrierByRegion;
+                       private IntPtr glTexStorage2DMultisample;
+                       private IntPtr glGetMultisamplefv;
+                       private IntPtr glSampleMaski;
+                       private IntPtr glGetTexLevelParameteriv;
+                       private IntPtr glGetTexLevelParameterfv;
+                       private IntPtr glBindVertexBuffer;
+                       private IntPtr glVertexAttribFormat;
+                       private IntPtr glVertexAttribIFormat;
+                       private IntPtr glVertexAttribBinding;
+                       private IntPtr glVertexBindingDivisor;
+                       private IntPtr glBlendBarrier;
+                       private IntPtr glCopyImageSubData;
+                       private IntPtr glDebugMessageControl;
+                       private IntPtr glDebugMessageInsert;
+                       private IntPtr glDebugMessageCallback;
+                       private IntPtr glGetDebugMessageLog;
+                       private IntPtr glPushDebugGroup;
+                       private IntPtr glPopDebugGroup;
+                       private IntPtr glObjectLabel;
+                       private IntPtr glGetObjectLabel;
+                       private IntPtr glObjectPtrLabel;
+                       private IntPtr glGetObjectPtrLabel;
+                       private IntPtr glEnablei;
+                       private IntPtr glDisablei;
+                       private IntPtr glBlendEquationi;
+                       private IntPtr glBlendEquationSeparatei;
+                       private IntPtr glBlendFunci;
+                       private IntPtr glBlendFuncSeparatei;
+                       private IntPtr glColorMaski;
+                       private IntPtr glIsEnabledi;
+                       private IntPtr glDrawElementsBaseVertex;
+                       private IntPtr glDrawRangeElementsBaseVertex;
+                       private IntPtr glDrawElementsInstancedBaseVertex;
+                       private IntPtr glFramebufferTexture;
+                       private IntPtr glPrimitiveBoundingBox;
+                       private IntPtr glGetGraphicsResetStatus;
+                       private IntPtr glReadnPixels;
+                       private IntPtr glGetnUniformfv;
+                       private IntPtr glGetnUniformiv;
+                       private IntPtr glGetnUniformuiv;
+                       private IntPtr glMinSampleShading;
+                       private IntPtr glPatchParameteri;
+                       private IntPtr glTexParameterIiv;
+                       private IntPtr glTexParameterIuiv;
+                       private IntPtr glGetTexParameterIiv;
+                       private IntPtr glGetTexParameterIuiv;
+                       private IntPtr glSamplerParameterIiv;
+                       private IntPtr glSamplerParameterIuiv;
+                       private IntPtr glGetSamplerParameterIiv;
+                       private IntPtr glGetSamplerParameterIuiv;
+                       private IntPtr glTexBuffer;
+                       private IntPtr glTexBufferRange;
+                       private IntPtr glTexStorage3DMultisample;
+#pragma warning restore 0169
+               }
+       }
+}
+
diff --git a/src/XSF/SkiaSharp/HandleDictionary.cs b/src/XSF/SkiaSharp/HandleDictionary.cs
new file mode 100644 (file)
index 0000000..274e410
--- /dev/null
@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+
+namespace SkiaSharp
+{
+       internal static class HandleDictionary
+       {
+               private static readonly Type IntPtrType = typeof (IntPtr);
+               private static readonly Type BoolType = typeof (bool);
+
+#if THROW_OBJECT_EXCEPTIONS
+               internal static readonly ConcurrentBag<Exception> exceptions = new ConcurrentBag<Exception> ();
+#endif
+
+               internal static readonly ConcurrentDictionary<Type, ConstructorInfo> constructors = new ConcurrentDictionary<Type, ConstructorInfo> ();
+               internal static readonly Dictionary<IntPtr, WeakReference> instances = new Dictionary<IntPtr, WeakReference> ();
+
+               internal static readonly ReaderWriterLockSlim instancesLock = new ReaderWriterLockSlim ();
+
+               /// <summary>
+               /// Retrieve the living instance if there is one, or null if not.
+               /// </summary>
+               /// <returns>The instance if it is alive, or null if there is none.</returns>
+               internal static bool GetInstance<TSkiaObject> (IntPtr handle, out TSkiaObject instance)
+                       where TSkiaObject : SKObject
+               {
+                       if (handle == IntPtr.Zero) {
+                               instance = null;
+                               return false;
+                       }
+
+                       instancesLock.EnterReadLock ();
+                       try {
+                               return GetInstanceNoLocks (handle, out instance);
+                       } finally {
+                               instancesLock.ExitReadLock ();
+                       }
+               }
+
+               /// <summary>
+               /// Retrieve or create an instance for the native handle.
+               /// </summary>
+               /// <returns>The instance, or null if the handle was null.</returns>
+               internal static TSkiaObject GetObject<TSkiaObject, TSkiaImplementation> (IntPtr handle, bool owns = true, bool unrefExisting = true, bool refNew = false)
+                       where TSkiaObject : SKObject
+                       where TSkiaImplementation : SKObject, TSkiaObject
+               {
+                       if (handle == IntPtr.Zero)
+                               return null;
+
+                       instancesLock.EnterUpgradeableReadLock ();
+                       try {
+                               if (GetInstanceNoLocks<TSkiaObject> (handle, out var instance)) {
+                                       // some object get automatically referenced on the native side,
+                                       // but managed code just has the same reference
+                                       if (unrefExisting && instance is ISKReferenceCounted refcnt) {
+#if THROW_OBJECT_EXCEPTIONS
+                                               if (refcnt.GetReferenceCount () == 1)
+                                                       throw new InvalidOperationException (
+                                                               $"About to unreference an object that has no references. " +
+                                                               $"H: {handle.ToString ("x")} Type: {instance.GetType ()}");
+#endif
+                                               refcnt.SafeUnRef ();
+                                       }
+
+                                       return instance;
+                               }
+
+                               var type = typeof (TSkiaImplementation);
+                               var constructor = constructors.GetOrAdd (type, t => GetConstructor (t));
+
+                               // we don't need to go into a writable here as the object will do it in the Handle property
+                               var obj = (TSkiaObject)constructor.Invoke (new object[] { handle, owns });
+                               if (refNew && obj is ISKReferenceCounted toRef)
+                                       toRef.SafeRef ();
+                               return obj;
+                       } finally {
+                               instancesLock.ExitUpgradeableReadLock ();
+                       }
+
+                       static ConstructorInfo GetConstructor (Type type)
+                       {
+                               var ctors = type.GetTypeInfo ().DeclaredConstructors;
+
+                               foreach (var ctor in ctors) {
+                                       var param = ctor.GetParameters ();
+                                       if (param.Length == 2 && param[0].ParameterType == IntPtrType && param[1].ParameterType == BoolType)
+                                               return ctor;
+                               }
+
+                               throw new MissingMethodException ($"No constructor found for {type.FullName}.ctor(System.IntPtr, System.Boolean)");
+                       }
+               }
+
+               /// <summary>
+               /// Retrieve the living instance if there is one, or null if not. This does not use locks.
+               /// </summary>
+               /// <returns>The instance if it is alive, or null if there is none.</returns>
+               private static bool GetInstanceNoLocks<TSkiaObject> (IntPtr handle, out TSkiaObject instance)
+                       where TSkiaObject : SKObject
+               {
+                       if (instances.TryGetValue (handle, out var weak) && weak.IsAlive) {
+                               if (weak.Target is TSkiaObject match) {
+                                       if (!match.IsDisposed) {
+                                               instance = match;
+                                               return true;
+                                       }
+#if THROW_OBJECT_EXCEPTIONS
+                               } else if (weak.Target is SKObject obj) {
+                                       if (!obj.IsDisposed && obj.OwnsHandle) {
+                                               throw new InvalidOperationException (
+                                                       $"A managed object exists for the handle, but is not the expected type. " +
+                                                       $"H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {typeof (TSkiaObject)})");
+                                       }
+                               } else if (weak.Target is object o) {
+                                       throw new InvalidOperationException (
+                                               $"An unknown object exists for the handle when trying to fetch an instance. " +
+                                               $"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {typeof (TSkiaObject)})");
+#endif
+                               }
+                       }
+
+                       instance = null;
+                       return false;
+               }
+
+               /// <summary>
+               /// Registers the specified instance with the dictionary.
+               /// </summary>
+               internal static void RegisterHandle (IntPtr handle, SKObject instance)
+               {
+                       if (handle == IntPtr.Zero || instance == null)
+                               return;
+
+                       SKObject objectToDispose = null;
+
+                       instancesLock.EnterWriteLock ();
+                       try {
+                               if (instances.TryGetValue (handle, out var oldValue) && oldValue.Target is SKObject obj && !obj.IsDisposed) {
+#if THROW_OBJECT_EXCEPTIONS
+                                       if (obj.OwnsHandle) {
+                                               // a mostly recoverable error
+                                               // if there is a managed object, then maybe something happened and the native object is dead
+                                               throw new InvalidOperationException (
+                                                       $"A managed object already exists for the specified native object. " +
+                                                       $"H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {instance.GetType ()})");
+                                       }
+#endif
+                                       // this means the ownership was handed off to a native object, so clean up the managed side
+                                       objectToDispose = obj;
+                               }
+
+                               instances[handle] = new WeakReference (instance);
+                       } finally {
+                               instancesLock.ExitWriteLock ();
+                       }
+
+                       // dispose the object we just replaced
+                       objectToDispose?.DisposeInternal ();
+               }
+
+               /// <summary>
+               /// Removes the registered instance from the dictionary.
+               /// </summary>
+               internal static void DeregisterHandle (IntPtr handle, SKObject instance)
+               {
+                       if (handle == IntPtr.Zero)
+                               return;
+
+                       instancesLock.EnterWriteLock ();
+                       try {
+                               var existed = instances.TryGetValue (handle, out var weak);
+                               if (existed && (!weak.IsAlive || weak.Target == instance)) {
+                                       instances.Remove (handle);
+                               } else {
+#if THROW_OBJECT_EXCEPTIONS
+                                       InvalidOperationException ex = null;
+                                       if (!existed) {
+                                               // the object may have been replaced
+
+                                               if (!instance.IsDisposed) {
+                                                       // recoverable error
+                                                       // there was no object there, but we are still alive
+                                                       ex = new InvalidOperationException (
+                                                               $"A managed object did not exist for the specified native object. " +
+                                                               $"H: {handle.ToString ("x")} Type: {instance.GetType ()}");
+                                               }
+                                       } else if (weak.Target is SKObject o && o != instance) {
+                                               // there was an object in the dictionary, but it was NOT this object
+
+                                               if (!instance.IsDisposed) {
+                                                       // recoverable error
+                                                       // there was a new living object there, but we are still alive
+                                                       ex = new InvalidOperationException (
+                                                               $"Trying to remove a different object with the same native handle. " +
+                                                               $"H: {handle.ToString ("x")} Type: ({o.GetType ()}, {instance.GetType ()})");
+                                               }
+                                       }
+                                       if (ex != null) {
+                                               if (instance.fromFinalizer)
+                                                       exceptions.Add (ex);
+                                               else
+                                                       throw ex;
+                                       }
+#endif
+                               }
+                       } finally {
+                               instancesLock.ExitWriteLock ();
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/HashCode.cs b/src/XSF/SkiaSharp/HashCode.cs
new file mode 100644 (file)
index 0000000..21a1bb3
--- /dev/null
@@ -0,0 +1,143 @@
+// Partial code copied from:
+// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/HashCode.cs
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SkiaSharp
+{
+       internal unsafe struct HashCode
+       {
+               private static readonly uint s_seed = GenerateGlobalSeed ();
+
+               private const uint Prime1 = 2654435761U;
+               private const uint Prime2 = 2246822519U;
+               private const uint Prime3 = 3266489917U;
+               private const uint Prime4 = 668265263U;
+               private const uint Prime5 = 374761393U;
+
+               private uint _v1, _v2, _v3, _v4;
+               private uint _queue1, _queue2, _queue3;
+               private uint _length;
+
+               private static unsafe uint GenerateGlobalSeed ()
+               {
+                       var rnd = new Random ();
+                       var result = rnd.Next ();
+                       return unchecked((uint)result);
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static void Initialize (out uint v1, out uint v2, out uint v3, out uint v4)
+               {
+                       v1 = s_seed + Prime1 + Prime2;
+                       v2 = s_seed + Prime2;
+                       v3 = s_seed;
+                       v4 = s_seed - Prime1;
+               }
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint Round (uint hash, uint input) =>
+                       RotateLeft (hash + input * Prime2, 13) * Prime1;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint QueueRound (uint hash, uint queuedValue) =>
+                       RotateLeft (hash + queuedValue * Prime3, 17) * Prime4;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint MixState (uint v1, uint v2, uint v3, uint v4) =>
+                       RotateLeft (v1, 1) + RotateLeft (v2, 7) + RotateLeft (v3, 12) + RotateLeft (v4, 18);
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint RotateLeft (uint value, int offset) =>
+                       (value << offset) | (value >> (32 - offset));
+
+               private static uint MixEmptyState () =>
+                       s_seed + Prime5;
+
+               [MethodImpl (MethodImplOptions.AggressiveInlining)]
+               private static uint MixFinal (uint hash)
+               {
+                       hash ^= hash >> 15;
+                       hash *= Prime2;
+                       hash ^= hash >> 13;
+                       hash *= Prime3;
+                       hash ^= hash >> 16;
+                       return hash;
+               }
+
+               public void Add (void* value) =>
+                       Add (value == null ? 0 : ((IntPtr)value).GetHashCode ());
+
+               public void Add<T> (T value) =>
+                       Add (value?.GetHashCode () ?? 0);
+
+               private void Add (int value)
+               {
+                       uint val = (uint)value;
+
+                       // Storing the value of _length locally shaves of quite a few bytes
+                       // in the resulting machine code.
+                       uint previousLength = _length++;
+                       uint position = previousLength % 4;
+
+                       // Switch can't be inlined.
+
+                       if (position == 0)
+                               _queue1 = val;
+                       else if (position == 1)
+                               _queue2 = val;
+                       else if (position == 2)
+                               _queue3 = val;
+                       else // position == 3
+                       {
+                               if (previousLength == 3)
+                                       Initialize (out _v1, out _v2, out _v3, out _v4);
+
+                               _v1 = Round (_v1, _queue1);
+                               _v2 = Round (_v2, _queue2);
+                               _v3 = Round (_v3, _queue3);
+                               _v4 = Round (_v4, val);
+                       }
+               }
+
+               public int ToHashCode ()
+               {
+                       // Storing the value of _length locally shaves of quite a few bytes
+                       // in the resulting machine code.
+                       uint length = _length;
+
+                       // position refers to the *next* queue position in this method, so
+                       // position == 1 means that _queue1 is populated; _queue2 would have
+                       // been populated on the next call to Add.
+                       uint position = length % 4;
+
+                       // If the length is less than 4, _v1 to _v4 don't contain anything
+                       // yet. xxHash32 treats this differently.
+
+                       uint hash = length < 4 ? MixEmptyState () : MixState (_v1, _v2, _v3, _v4);
+
+                       // _length is incremented once per Add(Int32) and is therefore 4
+                       // times too small (xxHash length is in bytes, not ints).
+
+                       hash += length * 4;
+
+                       // Mix what remains in the queue
+
+                       // Switch can't be inlined right now, so use as few branches as
+                       // possible by manually excluding impossible scenarios (position > 1
+                       // is always false if position is not > 0).
+                       if (position > 0) {
+                               hash = QueueRound (hash, _queue1);
+                               if (position > 1) {
+                                       hash = QueueRound (hash, _queue2);
+                                       if (position > 2)
+                                               hash = QueueRound (hash, _queue3);
+                               }
+                       }
+
+                       hash = MixFinal (hash);
+                       return (int)hash;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/MathTypes.cs b/src/XSF/SkiaSharp/MathTypes.cs
new file mode 100644 (file)
index 0000000..74f51a7
--- /dev/null
@@ -0,0 +1,698 @@
+using System;
+
+namespace SkiaSharp
+{
+       public partial struct SKPoint
+       {
+               public static readonly SKPoint Empty;
+
+               public SKPoint (float x, float y)
+               {
+                       this.x = x;
+                       this.y = y;
+               }
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public readonly float Length => (float)Math.Sqrt (x * x + y * y);
+
+               public readonly float LengthSquared => x * x + y * y;
+
+               public void Offset (SKPoint p)
+               {
+                       x += p.x;
+                       y += p.y;
+               }
+
+               public void Offset (float dx, float dy)
+               {
+                       x += dx;
+                       y += dy;
+               }
+
+               public readonly override string ToString () => $"{{X={x}, Y={y}}}";
+
+               public static SKPoint Normalize (SKPoint point)
+               {
+                       var ls = point.x * point.x + point.y * point.y;
+                       var invNorm = 1.0 / Math.Sqrt (ls);
+                       return new SKPoint ((float)(point.x * invNorm), (float)(point.y * invNorm));
+               }
+
+               public static float Distance (SKPoint point, SKPoint other)
+               {
+                       var dx = point.x - other.x;
+                       var dy = point.y - other.y;
+                       var ls = dx * dx + dy * dy;
+                       return (float)Math.Sqrt (ls);
+               }
+
+               public static float DistanceSquared (SKPoint point, SKPoint other)
+               {
+                       var dx = point.x - other.x;
+                       var dy = point.y - other.y;
+                       return dx * dx + dy * dy;
+               }
+
+               public static SKPoint Reflect (SKPoint point, SKPoint normal)
+               {
+                       var dot = point.x * point.x + point.y * point.y;
+                       return new SKPoint (
+                               point.x - 2.0f * dot * normal.x,
+                               point.y - 2.0f * dot * normal.y);
+               }
+
+               public static SKPoint Add (SKPoint pt, SKSizeI sz) => pt + sz;
+               public static SKPoint Add (SKPoint pt, SKSize sz) => pt + sz;
+               public static SKPoint Add (SKPoint pt, SKPointI sz) => pt + sz;
+               public static SKPoint Add (SKPoint pt, SKPoint sz) => pt + sz;
+
+               public static SKPoint Subtract (SKPoint pt, SKSizeI sz) => pt - sz;
+               public static SKPoint Subtract (SKPoint pt, SKSize sz) => pt - sz;
+               public static SKPoint Subtract (SKPoint pt, SKPointI sz) => pt - sz;
+               public static SKPoint Subtract (SKPoint pt, SKPoint sz) => pt - sz;
+
+               public static SKPoint operator + (SKPoint pt, SKSizeI sz) =>
+                       new SKPoint (pt.x + sz.Width, pt.y + sz.Height);
+               public static SKPoint operator + (SKPoint pt, SKSize sz) =>
+                       new SKPoint (pt.x + sz.Width, pt.y + sz.Height);
+               public static SKPoint operator + (SKPoint pt, SKPointI sz) =>
+                       new SKPoint (pt.x + sz.X, pt.y + sz.Y);
+               public static SKPoint operator + (SKPoint pt, SKPoint sz) =>
+                       new SKPoint (pt.x + sz.X, pt.y + sz.Y);
+
+               public static SKPoint operator - (SKPoint pt, SKSizeI sz) =>
+                       new SKPoint (pt.X - sz.Width, pt.Y - sz.Height);
+               public static SKPoint operator - (SKPoint pt, SKSize sz) =>
+                       new SKPoint (pt.X - sz.Width, pt.Y - sz.Height);
+               public static SKPoint operator - (SKPoint pt, SKPointI sz) =>
+                       new SKPoint (pt.X - sz.X, pt.Y - sz.Y);
+               public static SKPoint operator - (SKPoint pt, SKPoint sz) =>
+                       new SKPoint (pt.X - sz.X, pt.Y - sz.Y);
+       }
+
+       public partial struct SKPointI
+       {
+               public static readonly SKPointI Empty;
+
+               public SKPointI (SKSizeI sz)
+               {
+                       x = sz.Width;
+                       y = sz.Height;
+               }
+
+               public SKPointI (int x, int y)
+               {
+                       this.x = x;
+                       this.y = y;
+               }
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public readonly int Length => (int)Math.Sqrt (x * x + y * y);
+
+               public readonly int LengthSquared => x * x + y * y;
+
+               public void Offset (SKPointI p)
+               {
+                       x += p.X;
+                       y += p.Y;
+               }
+
+               public void Offset (int dx, int dy)
+               {
+                       x += dx;
+                       y += dy;
+               }
+
+               public readonly override string ToString () => $"{{X={x},Y={y}}}";
+
+               public static SKPointI Normalize (SKPointI point)
+               {
+                       var ls = point.x * point.x + point.y * point.y;
+                       var invNorm = 1.0 / Math.Sqrt (ls);
+                       return new SKPointI ((int)(point.x * invNorm), (int)(point.y * invNorm));
+               }
+
+               public static float Distance (SKPointI point, SKPointI other)
+               {
+                       var dx = point.x - other.x;
+                       var dy = point.y - other.y;
+                       var ls = dx * dx + dy * dy;
+                       return (float)Math.Sqrt (ls);
+               }
+
+               public static float DistanceSquared (SKPointI point, SKPointI other)
+               {
+                       var dx = point.x - other.x;
+                       var dy = point.y - other.y;
+                       return dx * dx + dy * dy;
+               }
+
+               public static SKPointI Reflect (SKPointI point, SKPointI normal)
+               {
+                       var dot = point.x * point.x + point.y * point.y;
+                       return new SKPointI (
+                               (int)(point.x - 2.0f * dot * normal.x),
+                               (int)(point.y - 2.0f * dot * normal.y));
+               }
+
+               public static SKPointI Ceiling (SKPoint value)
+               {
+                       int x, y;
+                       checked {
+                               x = (int)Math.Ceiling (value.X);
+                               y = (int)Math.Ceiling (value.Y);
+                       }
+
+                       return new SKPointI (x, y);
+               }
+
+               public static SKPointI Round (SKPoint value)
+               {
+                       int x, y;
+                       checked {
+                               x = (int)Math.Round (value.X);
+                               y = (int)Math.Round (value.Y);
+                       }
+
+                       return new SKPointI (x, y);
+               }
+
+               public static SKPointI Truncate (SKPoint value)
+               {
+                       int x, y;
+                       checked {
+                               x = (int)value.X;
+                               y = (int)value.Y;
+                       }
+
+                       return new SKPointI (x, y);
+               }
+
+               public static SKPointI Add (SKPointI pt, SKSizeI sz) => pt + sz;
+               public static SKPointI Add (SKPointI pt, SKPointI sz) => pt + sz;
+
+               public static SKPointI Subtract (SKPointI pt, SKSizeI sz) => pt - sz;
+               public static SKPointI Subtract (SKPointI pt, SKPointI sz) => pt - sz;
+
+               public static SKPointI operator + (SKPointI pt, SKSizeI sz) =>
+                       new SKPointI (pt.X + sz.Width, pt.Y + sz.Height);
+               public static SKPointI operator + (SKPointI pt, SKPointI sz) =>
+                       new SKPointI (pt.X + sz.X, pt.Y + sz.Y);
+
+               public static SKPointI operator - (SKPointI pt, SKSizeI sz) =>
+                       new SKPointI (pt.X - sz.Width, pt.Y - sz.Height);
+               public static SKPointI operator - (SKPointI pt, SKPointI sz) =>
+                       new SKPointI (pt.X - sz.X, pt.Y - sz.Y);
+
+               public static explicit operator SKSizeI (SKPointI p) =>
+                       new SKSizeI (p.X, p.Y);
+               public static implicit operator SKPoint (SKPointI p) =>
+                       new SKPoint (p.X, p.Y);
+       }
+
+       public partial struct SKPoint3
+       {
+               public static readonly SKPoint3 Empty;
+
+               public SKPoint3 (float x, float y, float z)
+               {
+                       this.x = x;
+                       this.y = y;
+                       this.z = z;
+               }
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public readonly override string ToString () => $"{{X={x}, Y={y}, Z={z}}}";
+
+               public static SKPoint3 Add (SKPoint3 pt, SKPoint3 sz) => pt + sz;
+
+               public static SKPoint3 Subtract (SKPoint3 pt, SKPoint3 sz) => pt - sz;
+
+               public static SKPoint3 operator + (SKPoint3 pt, SKPoint3 sz) =>
+                       new SKPoint3 (pt.X + sz.X, pt.Y + sz.Y, pt.Z + sz.Z);
+
+               public static SKPoint3 operator - (SKPoint3 pt, SKPoint3 sz) =>
+                       new SKPoint3 (pt.X - sz.X, pt.Y - sz.Y, pt.Z - sz.Z);
+       }
+
+       public partial struct SKSize
+       {
+               public static readonly SKSize Empty;
+
+               public SKSize (float width, float height)
+               {
+                       w = width;
+                       h = height;
+               }
+
+               public SKSize (SKPoint pt)
+               {
+                       w = pt.X;
+                       h = pt.Y;
+               }
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public readonly SKPoint ToPoint () =>
+                       new SKPoint (w, h);
+
+               public readonly SKSizeI ToSizeI ()
+               {
+                       int w, h;
+                       checked {
+                               w = (int)this.w;
+                               h = (int)this.h;
+                       }
+
+                       return new SKSizeI (w, h);
+               }
+
+               public readonly override string ToString () =>
+                       $"{{Width={w}, Height={h}}}";
+
+               public static SKSize Add (SKSize sz1, SKSize sz2) => sz1 + sz2;
+
+               public static SKSize Subtract (SKSize sz1, SKSize sz2) => sz1 - sz2;
+
+               public static SKSize operator + (SKSize sz1, SKSize sz2) =>
+                       new SKSize (sz1.Width + sz2.Width, sz1.Height + sz2.Height);
+
+               public static SKSize operator - (SKSize sz1, SKSize sz2) =>
+                       new SKSize (sz1.Width - sz2.Width, sz1.Height - sz2.Height);
+
+               public static explicit operator SKPoint (SKSize size) =>
+                       new SKPoint (size.Width, size.Height);
+               public static implicit operator SKSize (SKSizeI size) =>
+                       new SKSize (size.Width, size.Height);
+       }
+
+       public partial struct SKSizeI
+       {
+               public static readonly SKSizeI Empty;
+
+               public SKSizeI (int width, int height)
+               {
+                       w = width;
+                       h = height;
+               }
+
+               public SKSizeI (SKPointI pt)
+               {
+                       w = pt.X;
+                       h = pt.Y;
+               }
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public readonly SKPointI ToPointI () => new SKPointI (w, h);
+
+               public readonly override string ToString () =>
+                       $"{{Width={w}, Height={h}}}";
+
+               public static SKSizeI Add (SKSizeI sz1, SKSizeI sz2) => sz1 + sz2;
+
+               public static SKSizeI Subtract (SKSizeI sz1, SKSizeI sz2) => sz1 - sz2;
+
+               public static SKSizeI operator + (SKSizeI sz1, SKSizeI sz2) =>
+                       new SKSizeI (sz1.Width + sz2.Width, sz1.Height + sz2.Height);
+
+               public static SKSizeI operator - (SKSizeI sz1, SKSizeI sz2) =>
+                       new SKSizeI (sz1.Width - sz2.Width, sz1.Height - sz2.Height);
+
+               public static explicit operator SKPointI (SKSizeI size) =>
+                       new SKPointI (size.Width, size.Height);
+       }
+
+       public partial struct SKRect
+       {
+               public static readonly SKRect Empty;
+
+               public SKRect (float left, float top, float right, float bottom)
+               {
+                       this.left = left;
+                       this.right = right;
+                       this.top = top;
+                       this.bottom = bottom;
+               }
+
+               public readonly float MidX => left + (Width / 2f);
+
+               public readonly float MidY => top + (Height / 2f);
+
+               public readonly float Width => right - left;
+
+               public readonly float Height => bottom - top;
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public SKSize Size {
+                       readonly get => new SKSize (Width, Height);
+                       set {
+                               right = left + value.Width;
+                               bottom = top + value.Height;
+                       }
+               }
+
+               public SKPoint Location {
+                       readonly get => new SKPoint (left, top);
+                       set => this = SKRect.Create (value, Size);
+               }
+
+               public readonly SKRect Standardized {
+                       get {
+                               if (left > right) {
+                                       if (top > bottom) {
+                                               return new SKRect (right, bottom, left, top);
+                                       } else {
+                                               return new SKRect (right, top, left, bottom);
+                                       }
+                               } else {
+                                       if (top > bottom) {
+                                               return new SKRect (left, bottom, right, top);
+                                       } else {
+                                               return new SKRect (left, top, right, bottom);
+                                       }
+                               }
+                       }
+               }
+
+               public readonly SKRect AspectFit (SKSize size) => AspectResize (size, true);
+
+               public readonly SKRect AspectFill (SKSize size) => AspectResize (size, false);
+
+               private readonly SKRect AspectResize (SKSize size, bool fit)
+               {
+                       if (size.Width == 0 || size.Height == 0 || Width == 0 || Height == 0)
+                               return Create (MidX, MidY, 0, 0);
+
+                       var aspectWidth = size.Width;
+                       var aspectHeight = size.Height;
+                       var imgAspect = aspectWidth / aspectHeight;
+                       var fullRectAspect = Width / Height;
+
+                       var compare = fit ? (fullRectAspect > imgAspect) : (fullRectAspect < imgAspect);
+                       if (compare) {
+                               aspectHeight = Height;
+                               aspectWidth = aspectHeight * imgAspect;
+                       } else {
+                               aspectWidth = Width;
+                               aspectHeight = aspectWidth / imgAspect;
+                       }
+                       var aspectLeft = MidX - (aspectWidth / 2f);
+                       var aspectTop = MidY - (aspectHeight / 2f);
+
+                       return Create (aspectLeft, aspectTop, aspectWidth, aspectHeight);
+               }
+
+               public static SKRect Inflate (SKRect rect, float x, float y)
+               {
+                       var r = new SKRect (rect.left, rect.top, rect.right, rect.bottom);
+                       r.Inflate (x, y);
+                       return r;
+               }
+
+               public void Inflate (SKSize size) =>
+                       Inflate (size.Width, size.Height);
+
+               public void Inflate (float x, float y)
+               {
+                       left -= x;
+                       top -= y;
+                       right += x;
+                       bottom += y;
+               }
+
+               public static SKRect Intersect (SKRect a, SKRect b)
+               {
+                       if (!a.IntersectsWithInclusive (b)) {
+                               return Empty;
+                       }
+                       return new SKRect (
+                               Math.Max (a.left, b.left),
+                               Math.Max (a.top, b.top),
+                               Math.Min (a.right, b.right),
+                               Math.Min (a.bottom, b.bottom));
+               }
+
+               public void Intersect (SKRect rect) =>
+                       this = Intersect (this, rect);
+
+               public static SKRect Union (SKRect a, SKRect b) =>
+                       new SKRect (
+                               Math.Min (a.left, b.left),
+                               Math.Min (a.top, b.top),
+                               Math.Max (a.right, b.right),
+                               Math.Max (a.bottom, b.bottom));
+
+               public void Union (SKRect rect) =>
+                       this = Union (this, rect);
+
+               public static implicit operator SKRect (SKRectI r) =>
+                       new SKRect (r.Left, r.Top, r.Right, r.Bottom);
+
+               public readonly bool Contains (float x, float y) =>
+                       (x >= left) && (x < right) && (y >= top) && (y < bottom);
+
+               public readonly bool Contains (SKPoint pt) =>
+                       Contains (pt.X, pt.Y);
+
+               public readonly bool Contains (SKRect rect) =>
+                       (left <= rect.left) && (right >= rect.right) &&
+                       (top <= rect.top) && (bottom >= rect.bottom);
+
+               public readonly bool IntersectsWith (SKRect rect) =>
+                       (left < rect.right) && (right > rect.left) && (top < rect.bottom) && (bottom > rect.top);
+
+               public readonly bool IntersectsWithInclusive (SKRect rect) =>
+                       (left <= rect.right) && (right >= rect.left) && (top <= rect.bottom) && (bottom >= rect.top);
+
+               public void Offset (float x, float y)
+               {
+                       left += x;
+                       top += y;
+                       right += x;
+                       bottom += y;
+               }
+
+               public void Offset (SKPoint pos) => Offset (pos.X, pos.Y);
+
+               public readonly override string ToString () =>
+                       $"{{Left={Left},Top={Top},Width={Width},Height={Height}}}";
+
+               public static SKRect Create (SKPoint location, SKSize size) =>
+                       Create (location.X, location.Y, size.Width, size.Height);
+
+               public static SKRect Create (SKSize size) =>
+                       Create (SKPoint.Empty, size);
+
+               public static SKRect Create (float width, float height) =>
+                       new SKRect (SKPoint.Empty.X, SKPoint.Empty.Y, width, height);
+
+               public static SKRect Create (float x, float y, float width, float height) =>
+                       new SKRect (x, y, x + width, y + height);
+       }
+
+       public partial struct SKRectI
+       {
+               public static readonly SKRectI Empty;
+
+               public SKRectI (int left, int top, int right, int bottom)
+               {
+                       this.left = left;
+                       this.right = right;
+                       this.top = top;
+                       this.bottom = bottom;
+               }
+
+               public readonly int MidX => left + (Width / 2);
+
+               public readonly int MidY => top + (Height / 2);
+
+               public readonly int Width => right - left;
+
+               public readonly int Height => bottom - top;
+
+               public readonly bool IsEmpty => this == Empty;
+
+               public SKSizeI Size {
+                       readonly get => new SKSizeI (Width, Height);
+                       set {
+                               right = left + value.Width;
+                               bottom = top + value.Height;
+                       }
+               }
+
+               public SKPointI Location {
+                       readonly get => new SKPointI (left, top);
+                       set => this = SKRectI.Create (value, Size);
+               }
+
+               public readonly SKRectI Standardized {
+                       get {
+                               if (left > right) {
+                                       if (top > bottom) {
+                                               return new SKRectI (right, bottom, left, top);
+                                       } else {
+                                               return new SKRectI (right, top, left, bottom);
+                                       }
+                               } else {
+                                       if (top > bottom) {
+                                               return new SKRectI (left, bottom, right, top);
+                                       } else {
+                                               return new SKRectI (left, top, right, bottom);
+                                       }
+                               }
+                       }
+               }
+
+               public readonly SKRectI AspectFit (SKSizeI size) =>
+                       Truncate (((SKRect)this).AspectFit (size));
+
+               public readonly SKRectI AspectFill (SKSizeI size) =>
+                       Truncate (((SKRect)this).AspectFill (size));
+
+               public static SKRectI Ceiling (SKRect value) =>
+                       Ceiling (value, false);
+
+               public static SKRectI Ceiling (SKRect value, bool outwards)
+               {
+                       int x, y, r, b;
+                       checked {
+                               x = (int)(outwards && value.Width > 0 ? Math.Floor (value.Left) : Math.Ceiling (value.Left));
+                               y = (int)(outwards && value.Height > 0 ? Math.Floor (value.Top) : Math.Ceiling (value.Top));
+                               r = (int)(outwards && value.Width < 0 ? Math.Floor (value.Right) : Math.Ceiling (value.Right));
+                               b = (int)(outwards && value.Height < 0 ? Math.Floor (value.Bottom) : Math.Ceiling (value.Bottom));
+                       }
+
+                       return new SKRectI (x, y, r, b);
+               }
+
+               public static SKRectI Inflate (SKRectI rect, int x, int y)
+               {
+                       var r = new SKRectI (rect.left, rect.top, rect.right, rect.bottom);
+                       r.Inflate (x, y);
+                       return r;
+               }
+
+               public void Inflate (SKSizeI size) =>
+                       Inflate (size.Width, size.Height);
+
+               public void Inflate (int width, int height)
+               {
+                       left -= width;
+                       top -= height;
+                       right += width;
+                       bottom += height;
+               }
+
+               public static SKRectI Intersect (SKRectI a, SKRectI b)
+               {
+                       if (!a.IntersectsWithInclusive (b))
+                               return Empty;
+
+                       return new SKRectI (
+                               Math.Max (a.left, b.left),
+                               Math.Max (a.top, b.top),
+                               Math.Min (a.right, b.right),
+                               Math.Min (a.bottom, b.bottom));
+               }
+
+               public void Intersect (SKRectI rect) =>
+                       this = Intersect (this, rect);
+
+               public static SKRectI Round (SKRect value)
+               {
+                       int x, y, r, b;
+                       checked {
+                               x = (int)Math.Round (value.Left);
+                               y = (int)Math.Round (value.Top);
+                               r = (int)Math.Round (value.Right);
+                               b = (int)Math.Round (value.Bottom);
+                       }
+
+                       return new SKRectI (x, y, r, b);
+               }
+
+               public static SKRectI Floor (SKRect value) => Floor (value, false);
+
+               public static SKRectI Floor (SKRect value, bool inwards)
+               {
+                       int x, y, r, b;
+                       checked {
+                               x = (int)(inwards && value.Width > 0 ? Math.Ceiling (value.Left) : Math.Floor (value.Left));
+                               y = (int)(inwards && value.Height > 0 ? Math.Ceiling (value.Top) : Math.Floor (value.Top));
+                               r = (int)(inwards && value.Width < 0 ? Math.Ceiling (value.Right) : Math.Floor (value.Right));
+                               b = (int)(inwards && value.Height < 0 ? Math.Ceiling (value.Bottom) : Math.Floor (value.Bottom));
+                       }
+
+                       return new SKRectI (x, y, r, b);
+               }
+
+               public static SKRectI Truncate (SKRect value)
+               {
+                       int x, y, r, b;
+                       checked {
+                               x = (int)value.Left;
+                               y = (int)value.Top;
+                               r = (int)value.Right;
+                               b = (int)value.Bottom;
+                       }
+
+                       return new SKRectI (x, y, r, b);
+               }
+
+               public static SKRectI Union (SKRectI a, SKRectI b) =>
+                       new SKRectI (
+                               Math.Min (a.Left, b.Left),
+                               Math.Min (a.Top, b.Top),
+                               Math.Max (a.Right, b.Right),
+                               Math.Max (a.Bottom, b.Bottom));
+
+               public void Union (SKRectI rect) =>
+                       this = Union (this, rect);
+
+               public readonly bool Contains (int x, int y) =>
+                       (x >= left) && (x < right) && (y >= top) && (y < bottom);
+
+               public readonly bool Contains (SKPointI pt) =>
+                       Contains (pt.X, pt.Y);
+
+               public readonly bool Contains (SKRectI rect) =>
+                       (left <= rect.left) && (right >= rect.right) &&
+                       (top <= rect.top) && (bottom >= rect.bottom);
+
+               public readonly bool IntersectsWith (SKRectI rect) =>
+                       (left < rect.right) && (right > rect.left) && (top < rect.bottom) && (bottom > rect.top);
+
+               public readonly bool IntersectsWithInclusive (SKRectI rect) =>
+                       (left <= rect.right) && (right >= rect.left) && (top <= rect.bottom) && (bottom >= rect.top);
+
+               public void Offset (int x, int y)
+               {
+                       left += x;
+                       top += y;
+                       right += x;
+                       bottom += y;
+               }
+
+               public void Offset (SKPointI pos) => Offset (pos.X, pos.Y);
+
+               public readonly override string ToString () =>
+                       $"{{Left={Left},Top={Top},Width={Width},Height={Height}}}";
+
+               public static SKRectI Create (SKSizeI size) =>
+                       Create (SKPointI.Empty.X, SKPointI.Empty.Y, size.Width, size.Height);
+
+               public static SKRectI Create (SKPointI location, SKSizeI size) =>
+                       Create (location.X, location.Y, size.Width, size.Height);
+
+               public static SKRectI Create (int width, int height) =>
+                       new SKRectI (SKPointI.Empty.X, SKPointI.Empty.X, width, height);
+
+               public static SKRectI Create (int x, int y, int width, int height) =>
+                       new SKRectI (x, y, x + width, y + height);
+       }
+}
diff --git a/src/XSF/SkiaSharp/PlatformConfiguration.cs b/src/XSF/SkiaSharp/PlatformConfiguration.cs
new file mode 100644 (file)
index 0000000..78b0b14
--- /dev/null
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       internal static class PlatformConfiguration
+       {
+               public static bool IsUnix { get; }
+               public static bool IsWindows { get; }
+
+               static PlatformConfiguration ()
+               {
+                       IsUnix = Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix;
+                       IsWindows = !IsUnix;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/PreserveAttribute.cs b/src/XSF/SkiaSharp/PreserveAttribute.cs
new file mode 100644 (file)
index 0000000..ac0ad49
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+
+namespace SkiaSharp
+{
+       internal sealed class PreserveAttribute : Attribute
+       {
+               public bool AllMembers { get; set; }
+
+               public bool Conditional { get; set; }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SK3dView.cs b/src/XSF/SkiaSharp/SK3dView.cs
new file mode 100644 (file)
index 0000000..ee51c0d
--- /dev/null
@@ -0,0 +1,105 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SK3dView : SKObject
+       {
+               [Preserve]
+               internal SK3dView (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               public SK3dView ()
+                       : this (SkiaApi.sk_3dview_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SK3dView instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_3dview_destroy (Handle);
+
+               // Matrix
+
+               public SKMatrix Matrix {
+                       get {
+                               var matrix = SKMatrix.MakeIdentity ();
+                               GetMatrix (ref matrix);
+                               return matrix;
+                       }
+               }
+
+               public void GetMatrix (ref SKMatrix matrix)
+               {
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_3dview_get_matrix (Handle, m);
+                       }
+               }
+
+               // Save
+
+               public void Save () =>
+                       SkiaApi.sk_3dview_save (Handle);
+
+               // Restore
+
+               public void Restore () =>
+                       SkiaApi.sk_3dview_restore (Handle);
+
+               // Translate
+
+               public void Translate (float x, float y, float z) =>
+                       SkiaApi.sk_3dview_translate (Handle, x, y, z);
+
+               public void TranslateX (float x) =>
+                       Translate (x, 0, 0);
+
+               public void TranslateY (float y) =>
+                       Translate (0, y, 0);
+
+               public void TranslateZ (float z) =>
+                       Translate (0, 0, z);
+
+               // Rotate*Degrees
+
+               public void RotateXDegrees (float degrees) =>
+                       SkiaApi.sk_3dview_rotate_x_degrees (Handle, degrees);
+
+               public void RotateYDegrees (float degrees) =>
+                       SkiaApi.sk_3dview_rotate_y_degrees (Handle, degrees);
+
+               public void RotateZDegrees (float degrees) =>
+                       SkiaApi.sk_3dview_rotate_z_degrees (Handle, degrees);
+
+               // Rotate*Radians
+
+               public void RotateXRadians (float radians) =>
+                       SkiaApi.sk_3dview_rotate_x_radians (Handle, radians);
+
+               public void RotateYRadians (float radians) =>
+                       SkiaApi.sk_3dview_rotate_y_radians (Handle, radians);
+
+               public void RotateZRadians (float radians) =>
+                       SkiaApi.sk_3dview_rotate_z_radians (Handle, radians);
+
+               // DotWithNormal
+
+               public float DotWithNormal (float dx, float dy, float dz) =>
+                       SkiaApi.sk_3dview_dot_with_normal (Handle, dx, dy, dz);
+
+               // Apply
+
+               public void ApplyToCanvas (SKCanvas canvas)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException (nameof (canvas));
+
+                       SkiaApi.sk_3dview_apply_to_canvas (Handle, canvas.Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKAbstractManagedStream.cs b/src/XSF/SkiaSharp/SKAbstractManagedStream.cs
new file mode 100644 (file)
index 0000000..717e3c1
--- /dev/null
@@ -0,0 +1,180 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace SkiaSharp
+{
+       public unsafe abstract class SKAbstractManagedStream : SKStreamAsset
+       {
+               private static readonly SKManagedStreamDelegates delegates;
+
+               private int fromNative;
+
+               static SKAbstractManagedStream ()
+               {
+                       delegates = new SKManagedStreamDelegates {
+                               fRead = ReadInternal,
+                               fPeek = PeekInternal,
+                               fIsAtEnd = IsAtEndInternal,
+                               fHasPosition = HasPositionInternal,
+                               fHasLength = HasLengthInternal,
+                               fRewind = RewindInternal,
+                               fGetPosition = GetPositionInternal,
+                               fSeek = SeekInternal,
+                               fMove = MoveInternal,
+                               fGetLength = GetLengthInternal,
+                               fDuplicate = DuplicateInternal,
+                               fFork = ForkInternal,
+                               fDestroy = DestroyInternal,
+                       };
+                       SkiaApi.sk_managedstream_set_procs (delegates);
+               }
+
+               protected SKAbstractManagedStream ()
+                       : this (true)
+               {
+               }
+
+               protected SKAbstractManagedStream (bool owns)
+                       : base (IntPtr.Zero, owns)
+               {
+                       var ctx = DelegateProxies.CreateUserData (this, true);
+                       Handle = SkiaApi.sk_managedstream_new ((void*)ctx);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative ()
+               {
+                       if (Interlocked.CompareExchange (ref fromNative, 0, 0) == 0)
+                               SkiaApi.sk_managedstream_destroy (Handle);
+               }
+
+               protected abstract IntPtr OnRead (IntPtr buffer, IntPtr size);
+
+               protected abstract IntPtr OnPeek (IntPtr buffer, IntPtr size);
+
+               protected abstract bool OnIsAtEnd ();
+
+               protected abstract bool OnHasPosition ();
+
+               protected abstract bool OnHasLength ();
+
+               protected abstract bool OnRewind ();
+
+               protected abstract IntPtr OnGetPosition ();
+
+               protected abstract IntPtr OnGetLength ();
+
+               protected abstract bool OnSeek (IntPtr position);
+
+               protected abstract bool OnMove (int offset);
+
+               protected abstract IntPtr OnCreateNew ();
+
+               protected virtual IntPtr OnFork ()
+               {
+                       var stream = OnCreateNew ();
+                       SkiaApi.sk_stream_seek (stream, SkiaApi.sk_stream_get_position (Handle));
+                       return stream;
+               }
+
+               protected virtual IntPtr OnDuplicate () => OnCreateNew ();
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamReadProxyDelegate))]
+               private static IntPtr ReadInternal (IntPtr s, void* context, void* buffer, IntPtr size)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnRead ((IntPtr)buffer, size);
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamPeekProxyDelegate))]
+               private static IntPtr PeekInternal (IntPtr s, void* context, void* buffer, IntPtr size)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnPeek ((IntPtr)buffer, size);
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamIsAtEndProxyDelegate))]
+               private static bool IsAtEndInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnIsAtEnd ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamHasPositionProxyDelegate))]
+               private static bool HasPositionInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnHasPosition ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamHasLengthProxyDelegate))]
+               private static bool HasLengthInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnHasLength ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamRewindProxyDelegate))]
+               private static bool RewindInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnRewind ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamGetPositionProxyDelegate))]
+               private static IntPtr GetPositionInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnGetPosition ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamSeekProxyDelegate))]
+               private static bool SeekInternal (IntPtr s, void* context, IntPtr position)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnSeek (position);
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamMoveProxyDelegate))]
+               private static bool MoveInternal (IntPtr s, void* context, int offset)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnMove (offset);
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamGetLengthProxyDelegate))]
+               private static IntPtr GetLengthInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnGetLength ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamDuplicateProxyDelegate))]
+               private static IntPtr DuplicateInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnDuplicate ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamForkProxyDelegate))]
+               private static IntPtr ForkInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out _);
+                       return stream.OnFork ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedStreamDestroyProxyDelegate))]
+               private static void DestroyInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedStream> ((IntPtr)context, out var gch);
+                       if (stream != null) {
+                               Interlocked.Exchange (ref stream.fromNative, 1);
+                               stream.Dispose ();
+                       }
+                       gch.Free ();
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKAbstractManagedWStream.cs b/src/XSF/SkiaSharp/SKAbstractManagedWStream.cs
new file mode 100644 (file)
index 0000000..ef564f5
--- /dev/null
@@ -0,0 +1,84 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace SkiaSharp
+{
+       public unsafe abstract class SKAbstractManagedWStream : SKWStream
+       {
+               private static readonly SKManagedWStreamDelegates delegates;
+
+               private int fromNative;
+
+               static SKAbstractManagedWStream ()
+               {
+                       delegates = new SKManagedWStreamDelegates {
+                               fWrite = WriteInternal,
+                               fFlush = FlushInternal,
+                               fBytesWritten = BytesWrittenInternal,
+                               fDestroy = DestroyInternal,
+                       };
+
+                       SkiaApi.sk_managedwstream_set_procs (delegates);
+               }
+
+               protected SKAbstractManagedWStream ()
+                       : this (true)
+               {
+               }
+
+               protected SKAbstractManagedWStream (bool owns)
+                       : base (IntPtr.Zero, owns)
+               {
+                       var ctx = DelegateProxies.CreateUserData (this, true);
+                       Handle = SkiaApi.sk_managedwstream_new ((void*)ctx);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative ()
+               {
+                       if (Interlocked.CompareExchange (ref fromNative, 0, 0) == 0)
+                               SkiaApi.sk_managedwstream_destroy (Handle);
+               }
+
+               protected abstract bool OnWrite (IntPtr buffer, IntPtr size);
+
+               protected abstract void OnFlush ();
+
+               protected abstract IntPtr OnBytesWritten ();
+
+               [MonoPInvokeCallback (typeof (SKManagedWStreamWriteProxyDelegate))]
+               private static bool WriteInternal (IntPtr s, void* context, void* buffer, IntPtr size)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedWStream> ((IntPtr)context, out _);
+                       return stream.OnWrite ((IntPtr)buffer, size);
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedWStreamFlushProxyDelegate))]
+               private static void FlushInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedWStream> ((IntPtr)context, out _);
+                       stream.OnFlush ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedWStreamBytesWrittenProxyDelegate))]
+               private static IntPtr BytesWrittenInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedWStream> ((IntPtr)context, out _);
+                       return stream.OnBytesWritten ();
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedWStreamDestroyProxyDelegate))]
+               private static void DestroyInternal (IntPtr s, void* context)
+               {
+                       var stream = DelegateProxies.GetUserData<SKAbstractManagedWStream> ((IntPtr)context, out var gch);
+                       if (stream != null) {
+                               Interlocked.Exchange (ref stream.fromNative, 1);
+                               stream.Dispose ();
+                       }
+                       gch.Free ();
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKAutoCoInitialize.cs b/src/XSF/SkiaSharp/SKAutoCoInitialize.cs
new file mode 100644 (file)
index 0000000..1a2be90
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public class SKAutoCoInitialize : IDisposable
+       {
+               private long hResult;
+
+               public SKAutoCoInitialize()
+               {
+                       if (PlatformConfiguration.IsWindows)
+                               hResult = CoInitializeEx(IntPtr.Zero, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+                       else
+                               hResult = S_OK;
+               }
+
+               public bool Initialized => hResult >= 0 || hResult == RPC_E_CHANGED_MODE;
+
+               public void Uninitialize()
+               {
+                       if (hResult >= 0)
+                       {
+                               if (PlatformConfiguration.IsWindows)
+                                       CoUninitialize();
+
+                               hResult = -1;
+                       }
+               }
+
+               public void Dispose() => Uninitialize();
+
+               private const long S_OK = 0x00000000L;
+               private const long RPC_E_CHANGED_MODE = 0x80010106L;
+
+               private const uint COINIT_MULTITHREADED = 0x0;
+               private const uint COINIT_APARTMENTTHREADED = 0x2;
+               private const uint COINIT_DISABLE_OLE1DDE = 0x4;
+               private const uint COINIT_SPEED_OVER_MEMORY = 0x8;
+
+               [DllImport("ole32.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
+               private static extern long CoInitializeEx([In, Optional] IntPtr pvReserved, [In] uint dwCoInit);
+
+               [DllImport("ole32.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
+               private static extern void CoUninitialize();
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKBitmap.cs b/src/XSF/SkiaSharp/SKBitmap.cs
new file mode 100644 (file)
index 0000000..4832df5
--- /dev/null
@@ -0,0 +1,840 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete]
+       public enum SKBitmapResizeMethod
+       {
+               Box,
+               Triangle,
+               Lanczos3,
+               Hamming,
+               Mitchell
+       }
+
+       public static partial class SkiaExtensions
+       {
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public static SKFilterQuality ToFilterQuality (this SKBitmapResizeMethod method)
+               {
+                       switch (method) {
+                               case SKBitmapResizeMethod.Box:
+                               case SKBitmapResizeMethod.Triangle:
+                                       return SKFilterQuality.Low;
+                               case SKBitmapResizeMethod.Lanczos3:
+                                       return SKFilterQuality.Medium;
+                               case SKBitmapResizeMethod.Hamming:
+                               case SKBitmapResizeMethod.Mitchell:
+                                       return SKFilterQuality.High;
+                               default:
+                                       return SKFilterQuality.Medium;
+                       }
+               }
+       }
+
+       // TODO: keep in mind SKBitmap may be going away (according to Google)
+       // TODO: `ComputeIsOpaque` may be useful
+       // TODO: `GenerationID` may be useful
+       // TODO: `GetAddr` and `GetPixel` are confusing
+
+       public unsafe class SKBitmap : SKObject
+       {
+               private const string UnsupportedColorTypeMessage = "Setting the ColorTable is only supported for bitmaps with ColorTypes of Index8.";
+               private const string UnableToAllocatePixelsMessage = "Unable to allocate pixels for the bitmap.";
+
+               [Preserve]
+               internal SKBitmap (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKBitmap ()
+                       : this (SkiaApi.sk_bitmap_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKBitmap instance.");
+                       }
+               }
+
+               public SKBitmap (int width, int height, bool isOpaque = false)
+                       : this (width, height, SKImageInfo.PlatformColorType, isOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul)
+               {
+               }
+
+               public SKBitmap (int width, int height, SKColorType colorType, SKAlphaType alphaType)
+                       : this (new SKImageInfo (width, height, colorType, alphaType))
+               {
+               }
+
+               public SKBitmap (SKImageInfo info)
+                       : this (info, info.RowBytes)
+               {
+               }
+
+               public SKBitmap (SKImageInfo info, int rowBytes)
+                       : this ()
+               {
+                       if (!TryAllocPixels (info, rowBytes)) {
+                               throw new Exception (UnableToAllocatePixelsMessage);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use SKBitmap(SKImageInfo, SKBitmapAllocFlags) instead.")]
+               public SKBitmap (SKImageInfo info, SKColorTable ctable, SKBitmapAllocFlags flags)
+                       : this (info, SKBitmapAllocFlags.None)
+               {
+               }
+
+               public SKBitmap (SKImageInfo info, SKBitmapAllocFlags flags)
+                       : this ()
+               {
+                       if (!TryAllocPixels (info, flags)) {
+                               throw new Exception (UnableToAllocatePixelsMessage);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use SKBitmap(SKImageInfo) instead.")]
+               public SKBitmap (SKImageInfo info, SKColorTable ctable)
+                       : this (info, SKBitmapAllocFlags.None)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_bitmap_destructor (Handle);
+
+               // TryAllocPixels
+
+               public bool TryAllocPixels (SKImageInfo info)
+               {
+                       return TryAllocPixels (info, info.RowBytes);
+               }
+
+               public bool TryAllocPixels (SKImageInfo info, int rowBytes)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return SkiaApi.sk_bitmap_try_alloc_pixels (Handle, &cinfo, (IntPtr)rowBytes);
+               }
+
+               public bool TryAllocPixels (SKImageInfo info, SKBitmapAllocFlags flags)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return SkiaApi.sk_bitmap_try_alloc_pixels_with_flags (Handle, &cinfo, (uint)flags);
+               }
+
+               // Reset
+
+               public void Reset ()
+               {
+                       SkiaApi.sk_bitmap_reset (Handle);
+               }
+
+               // SetImmutable
+
+               public void SetImmutable ()
+               {
+                       SkiaApi.sk_bitmap_set_immutable (Handle);
+               }
+
+               // Erase
+
+               public void Erase (SKColor color)
+               {
+                       SkiaApi.sk_bitmap_erase (Handle, (uint)color);
+               }
+
+               public void Erase (SKColor color, SKRectI rect)
+               {
+                       SkiaApi.sk_bitmap_erase_rect (Handle, (uint)color, &rect);
+               }
+
+               // GetAddr*
+
+               public byte GetAddr8 (int x, int y) => SkiaApi.sk_bitmap_get_addr_8 (Handle, x, y);
+
+               public UInt16 GetAddr16 (int x, int y) => SkiaApi.sk_bitmap_get_addr_16 (Handle, x, y);
+
+               public UInt32 GetAddr32 (int x, int y) => SkiaApi.sk_bitmap_get_addr_32 (Handle, x, y);
+
+               public IntPtr GetAddr (int x, int y) => (IntPtr)SkiaApi.sk_bitmap_get_addr (Handle, x, y);
+
+               // Pixels (color)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixel(int, int) instead.")]
+               public SKPMColor GetIndex8Color (int x, int y)
+               {
+                       return (SKPMColor)GetPixel (x, y);
+               }
+
+               public SKColor GetPixel (int x, int y)
+               {
+                       return SkiaApi.sk_bitmap_get_pixel_color (Handle, x, y);
+               }
+
+               public void SetPixel (int x, int y, SKColor color)
+               {
+                       SkiaApi.sk_bitmap_set_pixel_color (Handle, x, y, (uint)color);
+               }
+
+               // Copy
+
+               public bool CanCopyTo (SKColorType colorType)
+               {
+                       // TODO: optimize as this does more work that we really want
+
+                       if (colorType == SKColorType.Unknown)
+                               return false;
+
+                       using var bmp = new SKBitmap ();
+
+                       var info = Info
+                               .WithColorType (colorType)
+                               .WithSize (1, 1);
+                       return bmp.TryAllocPixels (info);
+               }
+
+               public SKBitmap Copy ()
+               {
+                       return Copy (ColorType);
+               }
+
+               public SKBitmap Copy (SKColorType colorType)
+               {
+                       var destination = new SKBitmap ();
+                       if (!CopyTo (destination, colorType)) {
+                               destination.Dispose ();
+                               destination = null;
+                       }
+                       return destination;
+               }
+
+               public bool CopyTo (SKBitmap destination)
+               {
+                       if (destination == null) {
+                               throw new ArgumentNullException (nameof (destination));
+                       }
+                       return CopyTo (destination, ColorType);
+               }
+
+               public bool CopyTo (SKBitmap destination, SKColorType colorType)
+               {
+                       if (destination == null)
+                               throw new ArgumentNullException (nameof (destination));
+
+                       if (colorType == SKColorType.Unknown)
+                               return false;
+
+                       using var srcPixmap = PeekPixels ();
+                       if (srcPixmap == null)
+                               return false;
+
+                       using var temp = new SKBitmap ();
+
+                       var dstInfo = srcPixmap.Info.WithColorType (colorType);
+                       if (!temp.TryAllocPixels (dstInfo))
+                               return false;
+
+                       using var canvas = new SKCanvas (temp);
+
+                       using var paint = new SKPaint {
+                               Shader = ToShader (),
+                               BlendMode = SKBlendMode.Src
+                       };
+
+                       canvas.DrawPaint (paint);
+
+                       destination.Swap (temp);
+                       return true;
+               }
+
+               // ExtractSubset
+
+               public bool ExtractSubset (SKBitmap destination, SKRectI subset)
+               {
+                       if (destination == null) {
+                               throw new ArgumentNullException (nameof (destination));
+                       }
+                       return SkiaApi.sk_bitmap_extract_subset (Handle, destination.Handle, &subset);
+               }
+
+               // ExtractAlpha
+
+               public bool ExtractAlpha (SKBitmap destination)
+               {
+                       return ExtractAlpha (destination, null, out var offset);
+               }
+
+               public bool ExtractAlpha (SKBitmap destination, out SKPointI offset)
+               {
+                       return ExtractAlpha (destination, null, out offset);
+               }
+
+               public bool ExtractAlpha (SKBitmap destination, SKPaint paint)
+               {
+                       return ExtractAlpha (destination, paint, out var offset);
+               }
+
+               public bool ExtractAlpha (SKBitmap destination, SKPaint paint, out SKPointI offset)
+               {
+                       if (destination == null) {
+                               throw new ArgumentNullException (nameof (destination));
+                       }
+                       fixed (SKPointI* o = &offset) {
+                               return SkiaApi.sk_bitmap_extract_alpha (Handle, destination.Handle, paint == null ? IntPtr.Zero : paint.Handle, o);
+                       }
+               }
+
+               // properties
+
+               public bool ReadyToDraw => SkiaApi.sk_bitmap_ready_to_draw (Handle);
+
+               public SKImageInfo Info {
+                       get {
+                               SKImageInfoNative cinfo;
+                               SkiaApi.sk_bitmap_get_info (Handle, &cinfo);
+                               return SKImageInfoNative.ToManaged (ref cinfo);
+                       }
+               }
+
+               public int Width {
+                       get { return Info.Width; }
+               }
+
+               public int Height {
+                       get { return Info.Height; }
+               }
+
+               public SKColorType ColorType {
+                       get { return Info.ColorType; }
+               }
+
+               public SKAlphaType AlphaType {
+                       get { return Info.AlphaType; }
+               }
+
+               public SKColorSpace ColorSpace {
+                       get { return Info.ColorSpace; }
+               }
+
+               public int BytesPerPixel {
+                       get { return Info.BytesPerPixel; }
+               }
+
+               public int RowBytes {
+                       get { return (int)SkiaApi.sk_bitmap_get_row_bytes (Handle); }
+               }
+
+               public int ByteCount {
+                       get { return (int)SkiaApi.sk_bitmap_get_byte_count (Handle); }
+               }
+
+               // *Pixels*
+
+               public IntPtr GetPixels () =>
+                       GetPixels (out _);
+
+               public ReadOnlySpan<byte> GetPixelSpan ()
+               {
+                       return new ReadOnlySpan<byte> ((void*)GetPixels (out var length), (int)length);
+               }
+
+               public IntPtr GetPixels (out IntPtr length)
+               {
+                       fixed (IntPtr* l = &length) {
+                               return (IntPtr)SkiaApi.sk_bitmap_get_pixels (Handle, l);
+                       }
+               }
+
+               public void SetPixels (IntPtr pixels)
+               {
+                       SkiaApi.sk_bitmap_set_pixels (Handle, (void*)pixels);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use SetPixels(IntPtr) instead.")]
+               public void SetPixels (IntPtr pixels, SKColorTable ct)
+               {
+                       SetPixels (pixels);
+               }
+
+               // SetColorTable
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported.")]
+               public void SetColorTable (SKColorTable ct)
+               {
+                       // no-op due to unsupperted action
+               }
+
+               // more properties
+
+               public byte[] Bytes {
+                       get {
+                               var array = GetPixelSpan ().ToArray ();
+                               GC.KeepAlive (this);
+                               return array;
+                       }
+               }
+
+               public SKColor[] Pixels {
+                       get {
+                               var info = Info;
+                               var pixels = new SKColor[info.Width * info.Height];
+                               fixed (SKColor* p = pixels) {
+                                       SkiaApi.sk_bitmap_get_pixel_colors (Handle, (uint*)p);
+                               }
+                               return pixels;
+                       }
+                       set {
+                               fixed (SKColor* v = value) {
+                                       SkiaApi.sk_bitmap_set_pixel_colors (Handle, (uint*)v);
+                               }
+                       }
+               }
+
+               public bool IsEmpty {
+                       get { return Info.IsEmpty; }
+               }
+
+               public bool IsNull {
+                       get { return SkiaApi.sk_bitmap_is_null (Handle); }
+               }
+
+               public bool DrawsNothing {
+                       get { return IsEmpty || IsNull; }
+               }
+
+               public bool IsImmutable {
+                       get { return SkiaApi.sk_bitmap_is_immutable (Handle); }
+               }
+
+               public bool IsVolatile {
+                       get { return SkiaApi.sk_bitmap_is_volatile (Handle); }
+                       set { SkiaApi.sk_bitmap_set_volatile (Handle, value); }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported.")]
+               public SKColorTable ColorTable => null;
+
+               // DecodeBounds
+
+               public static SKImageInfo DecodeBounds (Stream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               return codec?.Info ?? SKImageInfo.Empty;
+                       }
+               }
+
+               public static SKImageInfo DecodeBounds (SKStream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               return codec?.Info ?? SKImageInfo.Empty;
+                       }
+               }
+
+               public static SKImageInfo DecodeBounds (SKData data)
+               {
+                       if (data == null) {
+                               throw new ArgumentNullException (nameof (data));
+                       }
+                       using (var codec = SKCodec.Create (data)) {
+                               return codec?.Info ?? SKImageInfo.Empty;
+                       }
+               }
+
+               public static SKImageInfo DecodeBounds (string filename)
+               {
+                       if (filename == null) {
+                               throw new ArgumentNullException (nameof (filename));
+                       }
+                       using (var codec = SKCodec.Create (filename)) {
+                               return codec?.Info ?? SKImageInfo.Empty;
+                       }
+               }
+
+               public static SKImageInfo DecodeBounds (byte[] buffer) =>
+                       DecodeBounds (buffer.AsSpan ());
+
+               public static SKImageInfo DecodeBounds (ReadOnlySpan<byte> buffer)
+               {
+                       fixed (byte* b = buffer) {
+                               using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
+                               using var codec = SKCodec.Create (skdata);
+                               return codec?.Info ?? SKImageInfo.Empty;
+                       }
+               }
+
+               // Decode
+
+               public static SKBitmap Decode (SKCodec codec)
+               {
+                       if (codec == null) {
+                               throw new ArgumentNullException (nameof (codec));
+                       }
+                       var info = codec.Info;
+                       if (info.AlphaType == SKAlphaType.Unpremul) {
+                               info.AlphaType = SKAlphaType.Premul;
+                       }
+                       // for backwards compatibility, remove the colorspace
+                       info.ColorSpace = null;
+                       return Decode (codec, info);
+               }
+
+               public static SKBitmap Decode (SKCodec codec, SKImageInfo bitmapInfo)
+               {
+                       if (codec == null) {
+                               throw new ArgumentNullException (nameof (codec));
+                       }
+
+                       var bitmap = new SKBitmap (bitmapInfo);
+                       var result = codec.GetPixels (bitmapInfo, bitmap.GetPixels (out var length));
+                       if (result != SKCodecResult.Success && result != SKCodecResult.IncompleteInput) {
+                               bitmap.Dispose ();
+                               bitmap = null;
+                       }
+                       return bitmap;
+               }
+
+               public static SKBitmap Decode (Stream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec);
+                       }
+               }
+
+               public static SKBitmap Decode (Stream stream, SKImageInfo bitmapInfo)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec, bitmapInfo);
+                       }
+               }
+
+               public static SKBitmap Decode (SKStream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec);
+                       }
+               }
+
+               public static SKBitmap Decode (SKStream stream, SKImageInfo bitmapInfo)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+                       using (var codec = SKCodec.Create (stream)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec, bitmapInfo);
+                       }
+               }
+
+               public static SKBitmap Decode (SKData data)
+               {
+                       if (data == null) {
+                               throw new ArgumentNullException (nameof (data));
+                       }
+                       using (var codec = SKCodec.Create (data)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec);
+                       }
+               }
+
+               public static SKBitmap Decode (SKData data, SKImageInfo bitmapInfo)
+               {
+                       if (data == null) {
+                               throw new ArgumentNullException (nameof (data));
+                       }
+                       using (var codec = SKCodec.Create (data)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec, bitmapInfo);
+                       }
+               }
+
+               public static SKBitmap Decode (string filename)
+               {
+                       if (filename == null) {
+                               throw new ArgumentNullException (nameof (filename));
+                       }
+                       using (var codec = SKCodec.Create (filename)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec);
+                       }
+               }
+
+               public static SKBitmap Decode (string filename, SKImageInfo bitmapInfo)
+               {
+                       if (filename == null) {
+                               throw new ArgumentNullException (nameof (filename));
+                       }
+                       using (var codec = SKCodec.Create (filename)) {
+                               if (codec == null) {
+                                       return null;
+                               }
+                               return Decode (codec, bitmapInfo);
+                       }
+               }
+
+               public static SKBitmap Decode (byte[] buffer) =>
+                       Decode (buffer.AsSpan ());
+
+               public static SKBitmap Decode (byte[] buffer, SKImageInfo bitmapInfo) =>
+                       Decode (buffer.AsSpan (), bitmapInfo);
+
+               public static SKBitmap Decode (ReadOnlySpan<byte> buffer)
+               {
+                       fixed (byte* b = buffer) {
+                               using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
+                               using var codec = SKCodec.Create (skdata);
+                               return Decode (codec);
+                       }
+               }
+
+               public static SKBitmap Decode (ReadOnlySpan<byte> buffer, SKImageInfo bitmapInfo)
+               {
+                       fixed (byte* b = buffer) {
+                               using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
+                               using var codec = SKCodec.Create (skdata);
+                               return Decode (codec, bitmapInfo);
+                       }
+               }
+
+               // InstallPixels
+
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels)
+               {
+                       return InstallPixels (info, pixels, info.RowBytes, null, null);
+               }
+
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes)
+               {
+                       return InstallPixels (info, pixels, rowBytes, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use InstallPixels(SKImageInfo, IntPtr, int) instead.")]
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKColorTable ctable)
+               {
+                       return InstallPixels (info, pixels, rowBytes, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use InstallPixels(SKImageInfo, IntPtr, int, SKBitmapReleaseDelegate, object) instead.")]
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKColorTable ctable, SKBitmapReleaseDelegate releaseProc, object context)
+               {
+                       return InstallPixels (info, pixels, rowBytes, releaseProc, context);
+               }
+
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKBitmapReleaseDelegate releaseProc)
+               {
+                       return InstallPixels (info, pixels, rowBytes, releaseProc, null);
+               }
+
+               public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKBitmapReleaseDelegate releaseProc, object context)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       var del = releaseProc != null && context != null
+                               ? new SKBitmapReleaseDelegate ((addr, _) => releaseProc (addr, context))
+                               : releaseProc;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.SKBitmapReleaseDelegateProxy, out _, out var ctx);
+                       return SkiaApi.sk_bitmap_install_pixels (Handle, &cinfo, (void*)pixels, (IntPtr)rowBytes, proxy, (void*)ctx);
+               }
+
+               public bool InstallPixels (SKPixmap pixmap)
+               {
+                       return SkiaApi.sk_bitmap_install_pixels_with_pixmap (Handle, pixmap.Handle);
+               }
+
+               // InstallMaskPixels
+
+               public bool InstallMaskPixels (SKMask mask)
+               {
+                       return SkiaApi.sk_bitmap_install_mask_pixels (Handle, &mask);
+               }
+
+               // NotifyPixelsChanged
+
+               public void NotifyPixelsChanged ()
+               {
+                       SkiaApi.sk_bitmap_notify_pixels_changed (Handle);
+               }
+
+               // PeekPixels
+
+               public SKPixmap PeekPixels ()
+               {
+                       SKPixmap pixmap = new SKPixmap ();
+                       var result = PeekPixels (pixmap);
+                       if (result) {
+                               return pixmap;
+                       } else {
+                               pixmap.Dispose ();
+                               return null;
+                       }
+               }
+
+               public bool PeekPixels (SKPixmap pixmap)
+               {
+                       if (pixmap == null) {
+                               throw new ArgumentNullException (nameof (pixmap));
+                       }
+                       var result = SkiaApi.sk_bitmap_peek_pixels (Handle, pixmap.Handle);
+                       if (result)
+                               pixmap.pixelSource = this;
+                       return result;
+               }
+
+               // Resize
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Resize(SKImageInfo, SKFilterQuality) instead.")]
+               public SKBitmap Resize (SKImageInfo info, SKBitmapResizeMethod method) =>
+                       Resize (info, method.ToFilterQuality ());
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use ScalePixels(SKBitmap, SKFilterQuality) instead.")]
+               public bool Resize (SKBitmap dst, SKBitmapResizeMethod method) =>
+                       ScalePixels (dst, method.ToFilterQuality ());
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use ScalePixels(SKBitmap, SKFilterQuality) instead.")]
+               public static bool Resize (SKBitmap dst, SKBitmap src, SKBitmapResizeMethod method) =>
+                       src.ScalePixels (dst, method.ToFilterQuality ());
+
+               public SKBitmap Resize (SKImageInfo info, SKFilterQuality quality)
+               {
+                       var dst = new SKBitmap (info);
+                       if (ScalePixels (dst, quality)) {
+                               return dst;
+                       } else {
+                               dst.Dispose ();
+                               return null;
+                       }
+               }
+
+               public SKBitmap Resize (SKSizeI size, SKFilterQuality quality) =>
+                       Resize (Info.WithSize (size), quality);
+
+               // ScalePixels
+
+               public bool ScalePixels (SKBitmap destination, SKFilterQuality quality)
+               {
+                       if (destination == null) {
+                               throw new ArgumentNullException (nameof (destination));
+                       }
+
+                       using (var dstPix = destination.PeekPixels ()) {
+                               return ScalePixels (dstPix, quality);
+                       }
+               }
+
+               public bool ScalePixels (SKPixmap destination, SKFilterQuality quality)
+               {
+                       if (destination == null) {
+                               throw new ArgumentNullException (nameof (destination));
+                       }
+
+                       using (var srcPix = PeekPixels ()) {
+                               return srcPix.ScalePixels (destination, quality);
+                       }
+               }
+
+               // From/ToImage
+
+               public static SKBitmap FromImage (SKImage image)
+               {
+                       if (image == null) {
+                               throw new ArgumentNullException (nameof (image));
+                       }
+
+                       var info = new SKImageInfo (image.Width, image.Height, SKImageInfo.PlatformColorType, image.AlphaType);
+                       var bmp = new SKBitmap (info);
+                       if (!image.ReadPixels (info, bmp.GetPixels (), info.RowBytes, 0, 0)) {
+                               bmp.Dispose ();
+                               bmp = null;
+                       }
+                       return bmp;
+               }
+
+               // Encode
+
+               public SKData Encode (SKEncodedImageFormat format, int quality)
+               {
+                       using var pixmap = PeekPixels ();
+                       return pixmap?.Encode (format, quality);
+               }
+
+               public bool Encode (Stream dst, SKEncodedImageFormat format, int quality)
+               {
+                       using var wrapped = new SKManagedWStream (dst);
+                       return Encode (wrapped, format, quality);
+               }
+
+               public bool Encode (SKWStream dst, SKEncodedImageFormat format, int quality)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var pixmap = PeekPixels ();
+                       return pixmap?.Encode (dst, format, quality) ?? false;
+               }
+
+               // Swap
+
+               private void Swap (SKBitmap other)
+               {
+                       SkiaApi.sk_bitmap_swap (Handle, other.Handle);
+               }
+
+               // ToShader
+
+               public SKShader ToShader () =>
+                       ToShader (SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy) =>
+                       SKShader.CreateBitmap (this, tmx, tmy);
+
+               public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix) =>
+                       SKShader.CreateBitmap (this, tmx, tmy, localMatrix);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKCanvas.cs b/src/XSF/SkiaSharp/SKCanvas.cs
new file mode 100644 (file)
index 0000000..d6dcf08
--- /dev/null
@@ -0,0 +1,1036 @@
+using System;
+
+namespace SkiaSharp
+{
+       // TODO: carefully consider the `PeekPixels`, `ReadPixels`
+
+       public unsafe class SKCanvas : SKObject
+       {
+               private const int PatchCornerCount = 4;
+               private const int PatchCubicsCount = 12;
+
+               [Preserve]
+               internal SKCanvas (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKCanvas (SKBitmap bitmap)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       Handle = SkiaApi.sk_canvas_new_from_bitmap (bitmap.Handle);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_canvas_destroy (Handle);
+
+               public void Discard () =>
+                       SkiaApi.sk_canvas_discard (Handle);
+
+               // QuickReject
+
+               public bool QuickReject (SKRect rect)
+               {
+                       return SkiaApi.sk_canvas_quick_reject (Handle, &rect);
+               }
+
+               public bool QuickReject (SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       return path.IsEmpty || QuickReject (path.Bounds);
+               }
+
+               // Save*
+
+               public int Save ()
+               {
+                       if (Handle == IntPtr.Zero)
+                               throw new ObjectDisposedException ("SKCanvas");
+                       return SkiaApi.sk_canvas_save (Handle);
+               }
+
+               public int SaveLayer (SKRect limit, SKPaint paint)
+               {
+                       return SkiaApi.sk_canvas_save_layer (Handle, &limit, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public int SaveLayer (SKPaint paint)
+               {
+                       return SkiaApi.sk_canvas_save_layer (Handle, null, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public int SaveLayer () =>
+                       SaveLayer (null);
+
+               // DrawColor
+
+               public void DrawColor (SKColor color, SKBlendMode mode = SKBlendMode.Src)
+               {
+                       SkiaApi.sk_canvas_draw_color (Handle, (uint)color, mode);
+               }
+
+               // DrawLine
+
+               public void DrawLine (SKPoint p0, SKPoint p1, SKPaint paint)
+               {
+                       DrawLine (p0.X, p0.Y, p1.X, p1.Y, paint);
+               }
+
+               public void DrawLine (float x0, float y0, float x1, float y1, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_line (Handle, x0, y0, x1, y1, paint.Handle);
+               }
+
+               // Clear
+
+               public void Clear ()
+               {
+                       DrawColor (SKColors.Empty, SKBlendMode.Src);
+               }
+
+               public void Clear (SKColor color)
+               {
+                       DrawColor (color, SKBlendMode.Src);
+               }
+
+               // Restore*
+
+               public void Restore ()
+               {
+                       SkiaApi.sk_canvas_restore (Handle);
+               }
+
+               public void RestoreToCount (int count)
+               {
+                       SkiaApi.sk_canvas_restore_to_count (Handle, count);
+               }
+
+               // Translate
+
+               public void Translate (float dx, float dy)
+               {
+                       SkiaApi.sk_canvas_translate (Handle, dx, dy);
+               }
+
+               public void Translate (SKPoint point)
+               {
+                       SkiaApi.sk_canvas_translate (Handle, point.X, point.Y);
+               }
+
+               // Scale
+
+               public void Scale (float s)
+               {
+                       SkiaApi.sk_canvas_scale (Handle, s, s);
+               }
+
+               public void Scale (float sx, float sy)
+               {
+                       SkiaApi.sk_canvas_scale (Handle, sx, sy);
+               }
+
+               public void Scale (SKPoint size)
+               {
+                       SkiaApi.sk_canvas_scale (Handle, size.X, size.Y);
+               }
+
+               public void Scale (float sx, float sy, float px, float py)
+               {
+                       Translate (px, py);
+                       Scale (sx, sy);
+                       Translate (-px, -py);
+               }
+
+               // Rotate*
+
+               public void RotateDegrees (float degrees)
+               {
+                       SkiaApi.sk_canvas_rotate_degrees (Handle, degrees);
+               }
+
+               public void RotateRadians (float radians)
+               {
+                       SkiaApi.sk_canvas_rotate_radians (Handle, radians);
+               }
+
+               public void RotateDegrees (float degrees, float px, float py)
+               {
+                       Translate (px, py);
+                       RotateDegrees (degrees);
+                       Translate (-px, -py);
+               }
+
+               public void RotateRadians (float radians, float px, float py)
+               {
+                       Translate (px, py);
+                       RotateRadians (radians);
+                       Translate (-px, -py);
+               }
+
+               // Skew
+
+               public void Skew (float sx, float sy)
+               {
+                       SkiaApi.sk_canvas_skew (Handle, sx, sy);
+               }
+
+               public void Skew (SKPoint skew)
+               {
+                       SkiaApi.sk_canvas_skew (Handle, skew.X, skew.Y);
+               }
+
+               // Concat
+
+               public void Concat (ref SKMatrix m)
+               {
+                       fixed (SKMatrix* ptr = &m) {
+                               SkiaApi.sk_canvas_concat (Handle, ptr);
+                       }
+               }
+
+               // Clip*
+
+               public void ClipRect (SKRect rect, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
+               {
+                       SkiaApi.sk_canvas_clip_rect_with_operation (Handle, &rect, operation, antialias);
+               }
+
+               public void ClipRoundRect (SKRoundRect rect, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
+               {
+                       if (rect == null)
+                               throw new ArgumentNullException (nameof (rect));
+
+                       SkiaApi.sk_canvas_clip_rrect_with_operation (Handle, rect.Handle, operation, antialias);
+               }
+
+               public void ClipPath (SKPath path, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       SkiaApi.sk_canvas_clip_path_with_operation (Handle, path.Handle, operation, antialias);
+               }
+
+               public void ClipRegion (SKRegion region, SKClipOperation operation = SKClipOperation.Intersect)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+
+                       SkiaApi.sk_canvas_clip_region (Handle, region.Handle, operation);
+               }
+
+               public SKRect LocalClipBounds {
+                       get {
+                               GetLocalClipBounds (out var bounds);
+                               return bounds;
+                       }
+               }
+
+               public SKRectI DeviceClipBounds {
+                       get {
+                               GetDeviceClipBounds (out var bounds);
+                               return bounds;
+                       }
+               }
+
+               public bool IsClipEmpty => SkiaApi.sk_canvas_is_clip_empty (Handle);
+
+               public bool IsClipRect => SkiaApi.sk_canvas_is_clip_rect (Handle);
+
+               public bool GetLocalClipBounds (out SKRect bounds)
+               {
+                       fixed (SKRect* b = &bounds) {
+                               return SkiaApi.sk_canvas_get_local_clip_bounds (Handle, b);
+                       }
+               }
+
+               public bool GetDeviceClipBounds (out SKRectI bounds)
+               {
+                       fixed (SKRectI* b = &bounds) {
+                               return SkiaApi.sk_canvas_get_device_clip_bounds (Handle, b);
+                       }
+               }
+
+               // DrawPaint
+
+               public void DrawPaint (SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_paint (Handle, paint.Handle);
+               }
+
+               // DrawRegion
+
+               public void DrawRegion (SKRegion region, SKPaint paint)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_region (Handle, region.Handle, paint.Handle);
+               }
+
+               // DrawRect
+
+               public void DrawRect (float x, float y, float w, float h, SKPaint paint)
+               {
+                       DrawRect (SKRect.Create (x, y, w, h), paint);
+               }
+
+               public void DrawRect (SKRect rect, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_rect (Handle, &rect, paint.Handle);
+               }
+
+               // DrawRoundRect
+
+               public void DrawRoundRect (SKRoundRect rect, SKPaint paint)
+               {
+                       if (rect == null)
+                               throw new ArgumentNullException (nameof (rect));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_rrect (Handle, rect.Handle, paint.Handle);
+               }
+
+               public void DrawRoundRect (float x, float y, float w, float h, float rx, float ry, SKPaint paint)
+               {
+                       DrawRoundRect (SKRect.Create (x, y, w, h), rx, ry, paint);
+               }
+
+               public void DrawRoundRect (SKRect rect, float rx, float ry, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_round_rect (Handle, &rect, rx, ry, paint.Handle);
+               }
+
+               public void DrawRoundRect (SKRect rect, SKSize r, SKPaint paint)
+               {
+                       DrawRoundRect (rect, r.Width, r.Height, paint);
+               }
+
+               // DrawOval
+
+               public void DrawOval (float cx, float cy, float rx, float ry, SKPaint paint)
+               {
+                       DrawOval (new SKRect (cx - rx, cy - ry, cx + rx, cy + ry), paint);
+               }
+
+               public void DrawOval (SKPoint c, SKSize r, SKPaint paint)
+               {
+                       DrawOval (c.X, c.Y, r.Width, r.Height, paint);
+               }
+
+               public void DrawOval (SKRect rect, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_oval (Handle, &rect, paint.Handle);
+               }
+
+               // DrawCircle
+
+               public void DrawCircle (float cx, float cy, float radius, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_circle (Handle, cx, cy, radius, paint.Handle);
+               }
+
+               public void DrawCircle (SKPoint c, float radius, SKPaint paint)
+               {
+                       DrawCircle (c.X, c.Y, radius, paint);
+               }
+
+               // DrawPath
+
+               public void DrawPath (SKPath path, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       SkiaApi.sk_canvas_draw_path (Handle, path.Handle, paint.Handle);
+               }
+
+               // DrawPoints
+
+               public void DrawPoints (SKPointMode mode, SKPoint[] points, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+                       fixed (SKPoint* p = points) {
+                               SkiaApi.sk_canvas_draw_points (Handle, mode, (IntPtr)points.Length, p, paint.Handle);
+                       }
+               }
+
+               // DrawPoint
+
+               public void DrawPoint (SKPoint p, SKPaint paint)
+               {
+                       DrawPoint (p.X, p.Y, paint);
+               }
+
+               public void DrawPoint (float x, float y, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_point (Handle, x, y, paint.Handle);
+               }
+
+               public void DrawPoint (SKPoint p, SKColor color)
+               {
+                       DrawPoint (p.X, p.Y, color);
+               }
+
+               public void DrawPoint (float x, float y, SKColor color)
+               {
+                       using (var paint = new SKPaint { Color = color }) {
+                               DrawPoint (x, y, paint);
+                       }
+               }
+
+               // DrawImage
+
+               public void DrawImage (SKImage image, SKPoint p, SKPaint paint = null)
+               {
+                       DrawImage (image, p.X, p.Y, paint);
+               }
+
+               public void DrawImage (SKImage image, float x, float y, SKPaint paint = null)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException (nameof (image));
+                       SkiaApi.sk_canvas_draw_image (Handle, image.Handle, x, y, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public void DrawImage (SKImage image, SKRect dest, SKPaint paint = null)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException (nameof (image));
+                       SkiaApi.sk_canvas_draw_image_rect (Handle, image.Handle, null, &dest, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public void DrawImage (SKImage image, SKRect source, SKRect dest, SKPaint paint = null)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException (nameof (image));
+                       SkiaApi.sk_canvas_draw_image_rect (Handle, image.Handle, &source, &dest, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               // DrawPicture
+
+               public void DrawPicture (SKPicture picture, float x, float y, SKPaint paint = null)
+               {
+                       var matrix = SKMatrix.MakeTranslation (x, y);
+                       DrawPicture (picture, ref matrix, paint);
+               }
+
+               public void DrawPicture (SKPicture picture, SKPoint p, SKPaint paint = null)
+               {
+                       DrawPicture (picture, p.X, p.Y, paint);
+               }
+
+               public void DrawPicture (SKPicture picture, ref SKMatrix matrix, SKPaint paint = null)
+               {
+                       if (picture == null)
+                               throw new ArgumentNullException (nameof (picture));
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_canvas_draw_picture (Handle, picture.Handle, m, paint == null ? IntPtr.Zero : paint.Handle);
+                       }
+               }
+
+               public void DrawPicture (SKPicture picture, SKPaint paint = null)
+               {
+                       if (picture == null)
+                               throw new ArgumentNullException (nameof (picture));
+                       SkiaApi.sk_canvas_draw_picture (Handle, picture.Handle, null, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               // DrawDrawable
+
+               public void DrawDrawable (SKDrawable drawable, ref SKMatrix matrix)
+               {
+                       if (drawable == null)
+                               throw new ArgumentNullException (nameof (drawable));
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_canvas_draw_drawable (Handle, drawable.Handle, m);
+                       }
+               }
+
+               public void DrawDrawable (SKDrawable drawable, float x, float y)
+               {
+                       if (drawable == null)
+                               throw new ArgumentNullException (nameof (drawable));
+                       var matrix = SKMatrix.MakeTranslation (x, y);
+                       DrawDrawable (drawable, ref matrix);
+               }
+
+               public void DrawDrawable (SKDrawable drawable, SKPoint p)
+               {
+                       if (drawable == null)
+                               throw new ArgumentNullException (nameof (drawable));
+                       var matrix = SKMatrix.MakeTranslation (p.X, p.Y);
+                       DrawDrawable (drawable, ref matrix);
+               }
+
+               // DrawBitmap
+
+               public void DrawBitmap (SKBitmap bitmap, SKPoint p, SKPaint paint = null)
+               {
+                       DrawBitmap (bitmap, p.X, p.Y, paint);
+               }
+
+               public void DrawBitmap (SKBitmap bitmap, float x, float y, SKPaint paint = null)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       SkiaApi.sk_canvas_draw_bitmap (Handle, bitmap.Handle, x, y, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public void DrawBitmap (SKBitmap bitmap, SKRect dest, SKPaint paint = null)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       SkiaApi.sk_canvas_draw_bitmap_rect (Handle, bitmap.Handle, null, &dest, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public void DrawBitmap (SKBitmap bitmap, SKRect source, SKRect dest, SKPaint paint = null)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       SkiaApi.sk_canvas_draw_bitmap_rect (Handle, bitmap.Handle, &source, &dest, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               // DrawSurface
+
+               public void DrawSurface (SKSurface surface, SKPoint p, SKPaint paint = null)
+               {
+                       DrawSurface (surface, p.X, p.Y, paint);
+               }
+
+               public void DrawSurface (SKSurface surface, float x, float y, SKPaint paint = null)
+               {
+                       if (surface == null)
+                               throw new ArgumentNullException (nameof (surface));
+
+                       surface.Draw (this, x, y, paint);
+               }
+
+               // DrawText (SKTextBlob)
+
+               public void DrawText (SKTextBlob text, float x, float y, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       SkiaApi.sk_canvas_draw_text_blob (Handle, text.Handle, x, y, paint.Handle);
+               }
+
+               // DrawText
+
+               public void DrawText (string text, SKPoint p, SKPaint paint)
+               {
+                       DrawText (text, p.X, p.Y, paint);
+               }
+
+               public void DrawText (string text, float x, float y, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       var bytes = StringUtilities.GetEncodedText (text, paint.TextEncoding);
+                       DrawText (bytes, x, y, paint);
+               }
+
+               public void DrawText (byte[] text, SKPoint p, SKPaint paint)
+               {
+                       DrawText (text, p.X, p.Y, paint);
+               }
+
+               public void DrawText (byte[] text, float x, float y, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       fixed (byte* t = text) {
+                               SkiaApi.sk_canvas_draw_text (Handle, t, (IntPtr)text.Length, x, y, paint.Handle);
+                       }
+               }
+
+               public void DrawText (IntPtr buffer, int length, SKPoint p, SKPaint paint)
+               {
+                       DrawText (buffer, length, p.X, p.Y, paint);
+               }
+
+               public void DrawText (IntPtr buffer, int length, float x, float y, SKPaint paint)
+               {
+                       if (buffer == IntPtr.Zero && length != 0)
+                               throw new ArgumentNullException (nameof (buffer));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       SkiaApi.sk_canvas_draw_text (Handle, (void*)buffer, (IntPtr)length, x, y, paint.Handle);
+               }
+
+               // DrawPositionedText
+
+               public void DrawPositionedText (string text, SKPoint[] points, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+
+                       var bytes = StringUtilities.GetEncodedText (text, paint.TextEncoding);
+                       DrawPositionedText (bytes, points, paint);
+               }
+
+               public void DrawPositionedText (byte[] text, SKPoint[] points, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+
+                       fixed (byte* t = text)
+                       fixed (SKPoint* p = points) {
+                               SkiaApi.sk_canvas_draw_pos_text (Handle, t, (IntPtr)text.Length, p, paint.Handle);
+                       }
+               }
+
+               public void DrawPositionedText (IntPtr buffer, int length, SKPoint[] points, SKPaint paint)
+               {
+                       if (buffer == IntPtr.Zero && length != 0)
+                               throw new ArgumentNullException (nameof (buffer));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+
+                       fixed (SKPoint* p = points) {
+                               SkiaApi.sk_canvas_draw_pos_text (Handle, (void*)buffer, (IntPtr)length, p, paint.Handle);
+                       }
+               }
+
+               // DrawTextOnPath
+
+               public void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKPaint paint)
+               {
+                       DrawTextOnPath (text, path, offset.X, offset.Y, paint);
+               }
+
+               public void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       var bytes = StringUtilities.GetEncodedText (text, paint.TextEncoding);
+                       DrawTextOnPath (bytes, path, hOffset, vOffset, paint);
+               }
+
+               public void DrawTextOnPath (byte[] text, SKPath path, SKPoint offset, SKPaint paint)
+               {
+                       DrawTextOnPath (text, path, offset.X, offset.Y, paint);
+               }
+
+               public void DrawTextOnPath (byte[] text, SKPath path, float hOffset, float vOffset, SKPaint paint)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       fixed (byte* t = text) {
+                               SkiaApi.sk_canvas_draw_text_on_path (Handle, t, (IntPtr)text.Length, path.Handle, hOffset, vOffset, paint.Handle);
+                       }
+               }
+
+               public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, SKPoint offset, SKPaint paint)
+               {
+                       DrawTextOnPath (buffer, length, path, offset.X, offset.Y, paint);
+               }
+
+               public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint)
+               {
+                       if (buffer == IntPtr.Zero && length != 0)
+                               throw new ArgumentNullException (nameof (buffer));
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       SkiaApi.sk_canvas_draw_text_on_path (Handle, (void*)buffer, (IntPtr)length, path.Handle, hOffset, vOffset, paint.Handle);
+               }
+
+               // Flush
+
+               public void Flush ()
+               {
+                       SkiaApi.sk_canvas_flush (Handle);
+               }
+
+               // Draw*Annotation
+
+               public void DrawAnnotation (SKRect rect, string key, SKData value)
+               {
+                       var bytes = StringUtilities.GetEncodedText (key, SKTextEncoding.Utf8);
+                       fixed (byte* b = bytes) {
+                               SkiaApi.sk_canvas_draw_annotation (base.Handle, &rect, b, value == null ? IntPtr.Zero : value.Handle);
+                       }
+               }
+
+               public void DrawUrlAnnotation (SKRect rect, SKData value)
+               {
+                       SkiaApi.sk_canvas_draw_url_annotation (Handle, &rect, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKData DrawUrlAnnotation (SKRect rect, string value)
+               {
+                       var data = SKData.FromCString (value);
+                       DrawUrlAnnotation (rect, data);
+                       return data;
+               }
+
+               public void DrawNamedDestinationAnnotation (SKPoint point, SKData value)
+               {
+                       SkiaApi.sk_canvas_draw_named_destination_annotation (Handle, &point, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKData DrawNamedDestinationAnnotation (SKPoint point, string value)
+               {
+                       var data = SKData.FromCString (value);
+                       DrawNamedDestinationAnnotation (point, data);
+                       return data;
+               }
+
+               public void DrawLinkDestinationAnnotation (SKRect rect, SKData value)
+               {
+                       SkiaApi.sk_canvas_draw_link_destination_annotation (Handle, &rect, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKData DrawLinkDestinationAnnotation (SKRect rect, string value)
+               {
+                       var data = SKData.FromCString (value);
+                       DrawLinkDestinationAnnotation (rect, data);
+                       return data;
+               }
+
+               // Draw*NinePatch
+
+               public void DrawBitmapNinePatch (SKBitmap bitmap, SKRectI center, SKRect dst, SKPaint paint = null)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       // the "center" rect must fit inside the bitmap "rect"
+                       if (!SKRect.Create (bitmap.Info.Size).Contains (center))
+                               throw new ArgumentException ("Center rectangle must be contained inside the bitmap bounds.", nameof (center));
+
+                       SkiaApi.sk_canvas_draw_bitmap_nine (Handle, bitmap.Handle, &center, &dst, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public void DrawImageNinePatch (SKImage image, SKRectI center, SKRect dst, SKPaint paint = null)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException (nameof (image));
+                       // the "center" rect must fit inside the image "rect"
+                       if (!SKRect.Create (image.Width, image.Height).Contains (center))
+                               throw new ArgumentException ("Center rectangle must be contained inside the image bounds.", nameof (center));
+
+                       SkiaApi.sk_canvas_draw_image_nine (Handle, image.Handle, &center, &dst, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               // Draw*Lattice
+
+               public void DrawBitmapLattice (SKBitmap bitmap, int[] xDivs, int[] yDivs, SKRect dst, SKPaint paint = null)
+               {
+                       var lattice = new SKLattice {
+                               XDivs = xDivs,
+                               YDivs = yDivs
+                       };
+                       DrawBitmapLattice (bitmap, lattice, dst, paint);
+               }
+
+               public void DrawImageLattice (SKImage image, int[] xDivs, int[] yDivs, SKRect dst, SKPaint paint = null)
+               {
+                       var lattice = new SKLattice {
+                               XDivs = xDivs,
+                               YDivs = yDivs
+                       };
+                       DrawImageLattice (image, lattice, dst, paint);
+               }
+
+               public void DrawBitmapLattice (SKBitmap bitmap, SKLattice lattice, SKRect dst, SKPaint paint = null)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+                       if (lattice.XDivs == null)
+                               throw new ArgumentNullException (nameof (lattice.XDivs));
+                       if (lattice.YDivs == null)
+                               throw new ArgumentNullException (nameof (lattice.YDivs));
+
+                       fixed (int* x = lattice.XDivs)
+                       fixed (int* y = lattice.YDivs)
+                       fixed (SKLatticeRectType* r = lattice.RectTypes)
+                       fixed (SKColor* c = lattice.Colors) {
+                               var nativeLattice = new SKLatticeInternal {
+                                       fBounds = null,
+                                       fRectTypes = r,
+                                       fXCount = lattice.XDivs.Length,
+                                       fXDivs = x,
+                                       fYCount = lattice.YDivs.Length,
+                                       fYDivs = y,
+                                       fColors = (uint*)c,
+                               };
+                               if (lattice.Bounds != null) {
+                                       var bounds = lattice.Bounds.Value;
+                                       nativeLattice.fBounds = &bounds;
+                               }
+                               SkiaApi.sk_canvas_draw_bitmap_lattice (Handle, bitmap.Handle, &nativeLattice, &dst, paint == null ? IntPtr.Zero : paint.Handle);
+                       }
+               }
+
+               public void DrawImageLattice (SKImage image, SKLattice lattice, SKRect dst, SKPaint paint = null)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException (nameof (image));
+                       if (lattice.XDivs == null)
+                               throw new ArgumentNullException (nameof (lattice.XDivs));
+                       if (lattice.YDivs == null)
+                               throw new ArgumentNullException (nameof (lattice.YDivs));
+
+                       fixed (int* x = lattice.XDivs)
+                       fixed (int* y = lattice.YDivs)
+                       fixed (SKLatticeRectType* r = lattice.RectTypes)
+                       fixed (SKColor* c = lattice.Colors) {
+                               var nativeLattice = new SKLatticeInternal {
+                                       fBounds = null,
+                                       fRectTypes = r,
+                                       fXCount = lattice.XDivs.Length,
+                                       fXDivs = x,
+                                       fYCount = lattice.YDivs.Length,
+                                       fYDivs = y,
+                                       fColors = (uint*)c,
+                               };
+                               if (lattice.Bounds != null) {
+                                       var bounds = lattice.Bounds.Value;
+                                       nativeLattice.fBounds = &bounds;
+                               }
+                               SkiaApi.sk_canvas_draw_image_lattice (Handle, image.Handle, &nativeLattice, &dst, paint == null ? IntPtr.Zero : paint.Handle);
+                       }
+               }
+
+               // *Matrix
+
+               public void ResetMatrix ()
+               {
+                       SkiaApi.sk_canvas_reset_matrix (Handle);
+               }
+
+               public void SetMatrix (SKMatrix matrix)
+               {
+                       SkiaApi.sk_canvas_set_matrix (Handle, &matrix);
+               }
+
+               public SKMatrix TotalMatrix {
+                       get {
+                               SKMatrix matrix;
+                               SkiaApi.sk_canvas_get_total_matrix (Handle, &matrix);
+                               return matrix;
+                       }
+               }
+
+               // SaveCount
+
+               public int SaveCount => SkiaApi.sk_canvas_get_save_count (Handle);
+
+               // DrawVertices
+
+               public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKColor[] colors, SKPaint paint)
+               {
+                       var vert = SKVertices.CreateCopy (vmode, vertices, colors);
+                       DrawVertices (vert, SKBlendMode.Modulate, paint);
+               }
+
+               public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKPaint paint)
+               {
+                       var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors);
+                       DrawVertices (vert, SKBlendMode.Modulate, paint);
+               }
+
+               public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, UInt16[] indices, SKPaint paint)
+               {
+                       var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices);
+                       DrawVertices (vert, SKBlendMode.Modulate, paint);
+               }
+
+               public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKBlendMode mode, UInt16[] indices, SKPaint paint)
+               {
+                       var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices);
+                       DrawVertices (vert, mode, paint);
+               }
+
+               public void DrawVertices (SKVertices vertices, SKBlendMode mode, SKPaint paint)
+               {
+                       if (vertices == null)
+                               throw new ArgumentNullException (nameof (vertices));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_vertices (Handle, vertices.Handle, mode, paint.Handle);
+               }
+
+               // DrawArc
+
+               public void DrawArc (SKRect oval, float startAngle, float sweepAngle, bool useCenter, SKPaint paint)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+                       SkiaApi.sk_canvas_draw_arc (Handle, &oval, startAngle, sweepAngle, useCenter, paint.Handle);
+               }
+
+               // DrawRoundRectDifference
+
+               public void DrawRoundRectDifference (SKRoundRect outer, SKRoundRect inner, SKPaint paint)
+               {
+                       if (outer == null)
+                               throw new ArgumentNullException (nameof (outer));
+                       if (inner == null)
+                               throw new ArgumentNullException (nameof (inner));
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       SkiaApi.sk_canvas_draw_drrect (Handle, outer.Handle, inner.Handle, paint.Handle);
+               }
+
+               // DrawAtlas
+
+               public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKPaint paint) =>
+                       DrawAtlas (atlas, sprites, transforms, null, SKBlendMode.Dst, null, paint);
+
+               public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKPaint paint) =>
+                       DrawAtlas (atlas, sprites, transforms, colors, mode, null, paint);
+
+               public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKRect cullRect, SKPaint paint) =>
+                       DrawAtlas (atlas, sprites, transforms, colors, mode, &cullRect, paint);
+
+               private void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKRect* cullRect, SKPaint paint)
+               {
+                       if (atlas == null)
+                               throw new ArgumentNullException (nameof (atlas));
+                       if (sprites == null)
+                               throw new ArgumentNullException (nameof (sprites));
+                       if (transforms == null)
+                               throw new ArgumentNullException (nameof (transforms));
+
+                       if (transforms.Length != sprites.Length)
+                               throw new ArgumentException ("The number of transforms must match the number of sprites.", nameof (transforms));
+                       if (colors != null && colors.Length != sprites.Length)
+                               throw new ArgumentException ("The number of colors must match the number of sprites.", nameof (colors));
+
+                       fixed (SKRect* s = sprites)
+                       fixed (SKRotationScaleMatrix* t = transforms)
+                       fixed (SKColor* c = colors) {
+                               SkiaApi.sk_canvas_draw_atlas (Handle, atlas.Handle, t, s, (uint*)c, transforms.Length, mode, cullRect, paint.Handle);
+                       }
+               }
+
+               // DrawPatch
+
+               public void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKPaint paint) =>
+                       DrawPatch (cubics, colors, texCoords, SKBlendMode.Modulate, paint);
+
+               public void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKBlendMode mode, SKPaint paint)
+               {
+                       if (cubics == null)
+                               throw new ArgumentNullException (nameof (cubics));
+                       if (cubics.Length != PatchCubicsCount)
+                               throw new ArgumentException ($"Cubics must have a length of {PatchCubicsCount}.", nameof (cubics));
+
+                       if (colors != null && colors.Length != PatchCornerCount)
+                               throw new ArgumentException ($"Colors must have a length of {PatchCornerCount}.", nameof (colors));
+
+                       if (texCoords != null && texCoords.Length != PatchCornerCount)
+                               throw new ArgumentException ($"Texture coordinates must have a length of {PatchCornerCount}.", nameof (texCoords));
+
+                       if (paint == null)
+                               throw new ArgumentNullException (nameof (paint));
+
+                       fixed (SKPoint* cubes = cubics)
+                       fixed (SKColor* cols = colors)
+                       fixed (SKPoint* coords = texCoords) {
+                               SkiaApi.sk_canvas_draw_patch (Handle, cubes, (uint*)cols, coords, mode, paint.Handle);
+                       }
+               }
+       }
+
+       public class SKAutoCanvasRestore : IDisposable
+       {
+               private SKCanvas canvas;
+               private readonly int saveCount;
+
+               public SKAutoCanvasRestore (SKCanvas canvas)
+                       : this (canvas, true)
+               {
+               }
+
+               public SKAutoCanvasRestore (SKCanvas canvas, bool doSave)
+               {
+                       this.canvas = canvas;
+                       this.saveCount = 0;
+
+                       if (canvas != null) {
+                               saveCount = canvas.SaveCount;
+                               if (doSave) {
+                                       canvas.Save ();
+                               }
+                       }
+               }
+
+               public void Dispose ()
+               {
+                       Restore ();
+               }
+
+               /// <summary>
+               /// Perform the restore now, instead of waiting for the Dispose.
+               /// Will only do this once.
+               /// </summary>
+               public void Restore ()
+               {
+                       if (canvas != null) {
+                               canvas.RestoreToCount (saveCount);
+                               canvas = null;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKCodec.cs b/src/XSF/SkiaSharp/SKCodec.cs
new file mode 100644 (file)
index 0000000..13610ea
--- /dev/null
@@ -0,0 +1,347 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       // TODO: `Create(...)` should have overloads that accept a SKPngChunkReader
+       // TODO: missing the `QueryYuv8` and `GetYuv8Planes` members
+
+       public unsafe class SKCodec : SKObject
+       {
+               [Preserve]
+               internal SKCodec (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_codec_destroy (Handle);
+
+               public static int MinBufferedBytesNeeded =>
+                       (int)SkiaApi.sk_codec_min_buffered_bytes_needed ();
+
+               public SKImageInfo Info {
+                       get {
+                               SKImageInfoNative cinfo;
+                               SkiaApi.sk_codec_get_info (Handle, &cinfo);
+                               return SKImageInfoNative.ToManaged (ref cinfo);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use EncodedOrigin instead.")]
+               public SKCodecOrigin Origin =>
+                       (SKCodecOrigin)EncodedOrigin;
+
+               public SKEncodedOrigin EncodedOrigin =>
+                       SkiaApi.sk_codec_get_origin (Handle);
+
+               public SKEncodedImageFormat EncodedFormat =>
+                       SkiaApi.sk_codec_get_encoded_format (Handle);
+
+               public SKSizeI GetScaledDimensions (float desiredScale)
+               {
+                       SKSizeI dimensions;
+                       SkiaApi.sk_codec_get_scaled_dimensions (Handle, desiredScale, &dimensions);
+                       return dimensions;
+               }
+
+               public bool GetValidSubset (ref SKRectI desiredSubset)
+               {
+                       fixed (SKRectI* ds = &desiredSubset) {
+                               return SkiaApi.sk_codec_get_valid_subset (Handle, ds);
+                       }
+               }
+
+               public byte[] Pixels {
+                       get {
+                               var result = GetPixels (out var pixels);
+                               if (result != SKCodecResult.Success && result != SKCodecResult.IncompleteInput) {
+                                       throw new Exception (result.ToString ());
+                               }
+                               return pixels;
+                       }
+               }
+
+               // frames
+
+               public int RepetitionCount =>
+                       SkiaApi.sk_codec_get_repetition_count (Handle);
+
+               public int FrameCount =>
+                       SkiaApi.sk_codec_get_frame_count (Handle);
+
+               public SKCodecFrameInfo[] FrameInfo {
+                       get {
+                               var length = SkiaApi.sk_codec_get_frame_count (Handle);
+                               var info = new SKCodecFrameInfo[length];
+                               fixed (SKCodecFrameInfo* i = info) {
+                                       SkiaApi.sk_codec_get_frame_info (Handle, i);
+                               }
+                               return info;
+                       }
+               }
+
+               public bool GetFrameInfo (int index, out SKCodecFrameInfo frameInfo)
+               {
+                       fixed (SKCodecFrameInfo* f = &frameInfo) {
+                               return SkiaApi.sk_codec_get_frame_info_for_index (Handle, index, f);
+                       }
+               }
+
+               // pixels
+
+               public SKCodecResult GetPixels (out byte[] pixels) =>
+                       GetPixels (Info, out pixels);
+
+               public SKCodecResult GetPixels (SKImageInfo info, out byte[] pixels)
+               {
+                       pixels = new byte[info.BytesSize];
+                       return GetPixels (info, pixels);
+               }
+
+               public SKCodecResult GetPixels (SKImageInfo info, byte[] pixels)
+               {
+                       if (pixels == null)
+                               throw new ArgumentNullException (nameof (pixels));
+
+                       fixed (byte* p = pixels) {
+                               return GetPixels (info, (IntPtr)p, info.RowBytes, SKCodecOptions.Default);
+                       }
+               }
+
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels) =>
+                       GetPixels (info, pixels, info.RowBytes, SKCodecOptions.Default);
+
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, SKCodecOptions options) =>
+                       GetPixels (info, pixels, info.RowBytes, options);
+
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options)
+               {
+                       if (pixels == IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (pixels));
+
+                       var nInfo = SKImageInfoNative.FromManaged (ref info);
+                       var nOptions = new SKCodecOptionsInternal {
+                               fZeroInitialized = options.ZeroInitialized,
+                               fSubset = null,
+                               fFrameIndex = options.FrameIndex,
+                               fPriorFrame = options.PriorFrame,
+                               fPremulBehavior = options.PremulBehavior,
+                       };
+                       var subset = default (SKRectI);
+                       if (options.HasSubset) {
+                               subset = options.Subset.Value;
+                               nOptions.fSubset = &subset;
+                       }
+                       return SkiaApi.sk_codec_get_pixels (Handle, &nInfo, (void*)pixels, (IntPtr)rowBytes, &nOptions);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr, int, SKCodecOptions) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options, IntPtr colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, rowBytes, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr, SKCodecOptions) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, SKCodecOptions options, IntPtr colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, info.RowBytes, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, IntPtr colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, info.RowBytes, SKCodecOptions.Default);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr, int, SKCodecOptions) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options, SKColorTable colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, rowBytes, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr, SKCodecOptions) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, SKCodecOptions options, SKColorTable colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, info.RowBytes, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixels(SKImageInfo, IntPtr) instead.")]
+               public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels, SKColorTable colorTable, ref int colorTableCount) =>
+                       GetPixels (info, pixels, info.RowBytes, SKCodecOptions.Default);
+
+               // incremental (start)
+
+               public SKCodecResult StartIncrementalDecode (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options)
+               {
+                       if (pixels == IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (pixels));
+
+                       var nInfo = SKImageInfoNative.FromManaged (ref info);
+                       var nOptions = new SKCodecOptionsInternal {
+                               fZeroInitialized = options.ZeroInitialized,
+                               fSubset = null,
+                               fFrameIndex = options.FrameIndex,
+                               fPriorFrame = options.PriorFrame,
+                               fPremulBehavior = options.PremulBehavior,
+                       };
+                       var subset = default (SKRectI);
+                       if (options.HasSubset) {
+                               subset = options.Subset.Value;
+                               nOptions.fSubset = &subset;
+                       }
+
+                       return SkiaApi.sk_codec_start_incremental_decode (Handle, &nInfo, (void*)pixels, (IntPtr)rowBytes, &nOptions);
+               }
+
+               public SKCodecResult StartIncrementalDecode (SKImageInfo info, IntPtr pixels, int rowBytes)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return SkiaApi.sk_codec_start_incremental_decode (Handle, &cinfo, (void*)pixels, (IntPtr)rowBytes, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use StartIncrementalDecode(SKImageInfo, IntPtr, int, SKCodecOptions) instead.")]
+               public SKCodecResult StartIncrementalDecode (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options, IntPtr colorTable, ref int colorTableCount) =>
+                       StartIncrementalDecode (info, pixels, rowBytes, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use StartIncrementalDecode(SKImageInfo, IntPtr, int, SKCodecOptions) instead.")]
+               public SKCodecResult StartIncrementalDecode (SKImageInfo info, IntPtr pixels, int rowBytes, SKCodecOptions options, SKColorTable colorTable, ref int colorTableCount) =>
+                       StartIncrementalDecode (info, pixels, rowBytes, options);
+
+               // incremental (step)
+
+               public SKCodecResult IncrementalDecode (out int rowsDecoded)
+               {
+                       fixed (int* r = &rowsDecoded) {
+                               return SkiaApi.sk_codec_incremental_decode (Handle, r);
+                       }
+               }
+
+               public SKCodecResult IncrementalDecode () =>
+                       SkiaApi.sk_codec_incremental_decode (Handle, null);
+
+               // scanline (start)
+
+               public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options)
+               {
+                       var nInfo = SKImageInfoNative.FromManaged (ref info);
+                       var nOptions = new SKCodecOptionsInternal {
+                               fZeroInitialized = options.ZeroInitialized,
+                               fSubset = null,
+                               fFrameIndex = options.FrameIndex,
+                               fPriorFrame = options.PriorFrame,
+                               fPremulBehavior = options.PremulBehavior,
+                       };
+                       var subset = default (SKRectI);
+                       if (options.HasSubset) {
+                               subset = options.Subset.Value;
+                               nOptions.fSubset = &subset;
+                       }
+
+                       return SkiaApi.sk_codec_start_scanline_decode (Handle, &nInfo, &nOptions);
+               }
+
+               public SKCodecResult StartScanlineDecode (SKImageInfo info)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return SkiaApi.sk_codec_start_scanline_decode (Handle, &cinfo, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use StartScanlineDecode(SKImageInfo, SKCodecOptions) instead.")]
+               public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options, IntPtr colorTable, ref int colorTableCount) =>
+                       StartScanlineDecode (info, options);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use StartScanlineDecode(SKImageInfo, SKCodecOptions) instead.")]
+               public SKCodecResult StartScanlineDecode (SKImageInfo info, SKCodecOptions options, SKColorTable colorTable, ref int colorTableCount) =>
+                       StartScanlineDecode (info, options);
+
+               // scanline (step)
+
+               public int GetScanlines (IntPtr dst, int countLines, int rowBytes)
+               {
+                       if (dst == IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_codec_get_scanlines (Handle, (void*)dst, countLines, (IntPtr)rowBytes);
+               }
+
+               public bool SkipScanlines (int countLines) =>
+                       SkiaApi.sk_codec_skip_scanlines (Handle, countLines);
+
+               public SKCodecScanlineOrder ScanlineOrder =>
+                       SkiaApi.sk_codec_get_scanline_order (Handle);
+
+               public int NextScanline => SkiaApi.sk_codec_next_scanline (Handle);
+
+               public int GetOutputScanline (int inputScanline) =>
+                       SkiaApi.sk_codec_output_scanline (Handle, inputScanline);
+
+               // create (streams)
+
+               public static SKCodec Create (string filename) =>
+                       Create (filename, out var result);
+
+               public static SKCodec Create (string filename, out SKCodecResult result)
+               {
+                       var stream = SKFileStream.OpenStream (filename);
+                       if (stream == null) {
+                               result = SKCodecResult.InternalError;
+                               return null;
+                       }
+
+                       return Create (stream, out result);
+               }
+
+               public static SKCodec Create (Stream stream) =>
+                       Create (stream, out var result);
+
+               public static SKCodec Create (Stream stream, out SKCodecResult result) =>
+                       Create (WrapManagedStream (stream), out result);
+
+               public static SKCodec Create (SKStream stream) =>
+                       Create (stream, out var result);
+
+               public static SKCodec Create (SKStream stream, out SKCodecResult result)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       fixed (SKCodecResult* r = &result) {
+                               var codec = GetObject<SKCodec> (SkiaApi.sk_codec_new_from_stream (stream.Handle, r));
+                               stream.RevokeOwnership (codec);
+                               return codec;
+                       }
+               }
+
+               // create (data)
+
+               public static SKCodec Create (SKData data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       return GetObject<SKCodec> (SkiaApi.sk_codec_new_from_data (data.Handle));
+               }
+
+               // utils
+
+               internal static SKStream WrapManagedStream (Stream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       // we will need a seekable stream, so buffer it if need be
+                       if (stream.CanSeek) {
+                               return new SKManagedStream (stream, true);
+                       } else {
+                               return new SKFrontBufferedManagedStream (stream, MinBufferedBytesNeeded, true);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColor.cs b/src/XSF/SkiaSharp/SKColor.cs
new file mode 100644 (file)
index 0000000..eda060d
--- /dev/null
@@ -0,0 +1,192 @@
+using System;
+using System.Globalization;
+
+namespace SkiaSharp
+{
+       public readonly struct SKColor : IEquatable<SKColor>
+       {
+               public static readonly SKColor Empty;
+
+               private readonly uint color;
+
+               public SKColor (uint value)
+               {
+                       color = value;
+               }
+
+               public SKColor (byte red, byte green, byte blue, byte alpha)
+               {
+                       color = (uint)((alpha << 24) | (red << 16) | (green << 8) | blue);
+               }
+
+               public SKColor (byte red, byte green, byte blue)
+               {
+                       color = (0xff000000u | (uint)(red << 16) | (uint)(green << 8) | blue);
+               }
+
+               public readonly SKColor WithRed (byte red) =>
+                       new SKColor (red, Green, Blue, Alpha);
+
+               public readonly SKColor WithGreen (byte green) =>
+                       new SKColor (Red, green, Blue, Alpha);
+
+               public readonly SKColor WithBlue (byte blue) =>
+                       new SKColor (Red, Green, blue, Alpha);
+
+               public readonly SKColor WithAlpha (byte alpha) =>
+                       new SKColor (Red, Green, Blue, alpha);
+
+               public readonly byte Alpha => (byte)((color >> 24) & 0xff);
+               public readonly byte Red => (byte)((color >> 16) & 0xff);
+               public readonly byte Green => (byte)((color >> 8) & 0xff);
+               public readonly byte Blue => (byte)((color) & 0xff);
+
+               public readonly float Hue {
+                       get {
+                               ToHsv (out var h, out _, out _);
+                               return h;
+                       }
+               }
+
+               public static SKColor FromHsl (float h, float s, float l, byte a = 255)
+               {
+                       var colorf = SKColorF.FromHsl (h, s, l);
+
+                       // RGB results from 0 to 255
+                       var r = colorf.Red * 255f;
+                       var g = colorf.Green * 255f;
+                       var b = colorf.Blue * 255f;
+
+                       return new SKColor ((byte)r, (byte)g, (byte)b, a);
+               }
+
+               public static SKColor FromHsv (float h, float s, float v, byte a = 255)
+               {
+                       var colorf = SKColorF.FromHsv (h, s, v);
+
+                       // RGB results from 0 to 255
+                       var r = colorf.Red * 255f;
+                       var g = colorf.Green * 255f;
+                       var b = colorf.Blue * 255f;
+
+                       return new SKColor ((byte)r, (byte)g, (byte)b, a);
+               }
+
+               public readonly void ToHsl (out float h, out float s, out float l)
+               {
+                       // RGB from 0 to 255
+                       var r = Red / 255f;
+                       var g = Green / 255f;
+                       var b = Blue / 255f;
+
+                       var colorf = new SKColorF (r, g, b);
+                       colorf.ToHsl (out h, out s, out l);
+               }
+
+               public readonly void ToHsv (out float h, out float s, out float v)
+               {
+                       // RGB from 0 to 255
+                       var r = Red / 255f;
+                       var g = Green / 255f;
+                       var b = Blue / 255f;
+
+                       var colorf = new SKColorF (r, g, b);
+                       colorf.ToHsv (out h, out s, out v);
+               }
+
+               public readonly override string ToString () =>
+                       $"#{Alpha:x2}{Red:x2}{Green:x2}{Blue:x2}";
+
+               public readonly bool Equals (SKColor obj) =>
+                       obj.color == color;
+
+               public readonly override bool Equals (object other) =>
+                       other is SKColor f && Equals (f);
+
+               public static bool operator == (SKColor left, SKColor right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKColor left, SKColor right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode () =>
+                       color.GetHashCode ();
+
+               public static implicit operator SKColor (uint color) =>
+                       new SKColor (color);
+
+               public static explicit operator uint (SKColor color) =>
+                       color.color;
+
+               public static SKColor Parse (string hexString)
+               {
+                       if (!TryParse (hexString, out var color))
+                               throw new ArgumentException ("Invalid hexadecimal color string.", nameof (hexString));
+                       return color;
+               }
+
+               public static bool TryParse (string hexString, out SKColor color)
+               {
+                       if (string.IsNullOrWhiteSpace (hexString)) {
+                               // error
+                               color = SKColor.Empty;
+                               return false;
+                       }
+
+                       // clean up string
+                       hexString = hexString.Trim ().ToUpperInvariant ();
+                       if (hexString[0] == '#')
+                               hexString = hexString.Substring (1);
+
+                       var len = hexString.Length;
+                       if (len == 3 || len == 4) {
+                               byte a;
+                               // parse [A]
+                               if (len == 4) {
+                                       if (!byte.TryParse (string.Concat (hexString[len - 4], hexString[len - 4]), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out a)) {
+                                               // error
+                                               color = SKColor.Empty;
+                                               return false;
+                                       }
+                               } else {
+                                       a = 255;
+                               }
+
+                               // parse RGB
+                               if (!byte.TryParse (string.Concat (hexString[len - 3], hexString[len - 3]), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var r) ||
+                                       !byte.TryParse (string.Concat (hexString[len - 2], hexString[len - 2]), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var g) ||
+                                       !byte.TryParse (string.Concat (hexString[len - 1], hexString[len - 1]), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var b)) {
+                                       // error
+                                       color = SKColor.Empty;
+                                       return false;
+                               }
+
+                               // success
+                               color = new SKColor (r, g, b, a);
+                               return true;
+                       }
+
+                       if (len == 6 || len == 8) {
+                               // parse [AA]RRGGBB
+                               if (!uint.TryParse (hexString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var number)) {
+                                       // error
+                                       color = SKColor.Empty;
+                                       return false;
+                               }
+
+                               // success
+                               color = (SKColor)number;
+
+                               // alpha was not provided, so use 255
+                               if (len == 6) {
+                                       color = color.WithAlpha (255);
+                               }
+                               return true;
+                       }
+
+                       // error
+                       color = SKColor.Empty;
+                       return false;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColorF.cs b/src/XSF/SkiaSharp/SKColorF.cs
new file mode 100644 (file)
index 0000000..4c536fe
--- /dev/null
@@ -0,0 +1,261 @@
+using System;
+
+namespace SkiaSharp
+{
+       public readonly unsafe partial struct SKColorF
+       {
+               private const float EPSILON = 0.001f;
+
+               public static readonly SKColorF Empty;
+
+               public SKColorF (float red, float green, float blue)
+               {
+                       fR = red;
+                       fG = green;
+                       fB = blue;
+                       fA = 1f;
+               }
+
+               public SKColorF (float red, float green, float blue, float alpha)
+               {
+                       fR = red;
+                       fG = green;
+                       fB = blue;
+                       fA = alpha;
+               }
+
+               public readonly SKColorF WithRed (float red) =>
+                       new SKColorF (red, fG, fB, fA);
+
+               public readonly SKColorF WithGreen (float green) =>
+                       new SKColorF (fR, green, fB, fA);
+
+               public readonly SKColorF WithBlue (float blue) =>
+                       new SKColorF (fR, fG, blue, fA);
+
+               public readonly SKColorF WithAlpha (float alpha) =>
+                       new SKColorF (fR, fG, fB, alpha);
+
+               public readonly float Hue {
+                       get {
+                               ToHsv (out var h, out _, out _);
+                               return h;
+                       }
+               }
+
+               public readonly SKColorF Clamp ()
+               {
+                       return new SKColorF (Clamp (fR), Clamp (fG), Clamp (fB), Clamp (fA));
+
+                       static float Clamp (float v)
+                       {
+                               if (v > 1f)
+                                       return 1f;
+                               if (v < 0f)
+                                       return 0f;
+                               return v;
+                       }
+               }
+
+               public static SKColorF FromHsl (float h, float s, float l, float a = 1f)
+               {
+                       // convert from percentages
+                       h = h / 360f;
+                       s = s / 100f;
+                       l = l / 100f;
+
+                       // RGB results from 0 to 1
+                       var r = l;
+                       var g = l;
+                       var b = l;
+
+                       // HSL from 0 to 1
+                       if (Math.Abs (s) > EPSILON) {
+                               float v2;
+                               if (l < 0.5f)
+                                       v2 = l * (1f + s);
+                               else
+                                       v2 = (l + s) - (s * l);
+
+                               var v1 = 2f * l - v2;
+
+                               r = HueToRgb (v1, v2, h + (1f / 3f));
+                               g = HueToRgb (v1, v2, h);
+                               b = HueToRgb (v1, v2, h - (1f / 3f));
+                       }
+
+                       return new SKColorF (r, g, b, a);
+               }
+
+               private static float HueToRgb (float v1, float v2, float vH)
+               {
+                       if (vH < 0f)
+                               vH += 1f;
+                       if (vH > 1f)
+                               vH -= 1f;
+
+                       if ((6f * vH) < 1f)
+                               return (v1 + (v2 - v1) * 6f * vH);
+                       if ((2f * vH) < 1f)
+                               return (v2);
+                       if ((3f * vH) < 2f)
+                               return (v1 + (v2 - v1) * ((2f / 3f) - vH) * 6f);
+                       return (v1);
+               }
+
+               public static SKColorF FromHsv (float h, float s, float v, float a = 1f)
+               {
+                       // convert from percentages
+                       h = h / 360f;
+                       s = s / 100f;
+                       v = v / 100f;
+
+                       // RGB results from 0 to 1
+                       var r = v;
+                       var g = v;
+                       var b = v;
+
+                       // HSL from 0 to 1
+                       if (Math.Abs (s) > EPSILON) {
+                               h = h * 6f;
+                               if (Math.Abs (h - 6f) < EPSILON)
+                                       h = 0f; // H must be < 1
+
+                               var hInt = (int)h;
+                               var v1 = v * (1f - s);
+                               var v2 = v * (1f - s * (h - hInt));
+                               var v3 = v * (1f - s * (1f - (h - hInt)));
+
+                               if (hInt == 0) {
+                                       r = v;
+                                       g = v3;
+                                       b = v1;
+                               } else if (hInt == 1) {
+                                       r = v2;
+                                       g = v;
+                                       b = v1;
+                               } else if (hInt == 2) {
+                                       r = v1;
+                                       g = v;
+                                       b = v3;
+                               } else if (hInt == 3) {
+                                       r = v1;
+                                       g = v2;
+                                       b = v;
+                               } else if (hInt == 4) {
+                                       r = v3;
+                                       g = v1;
+                                       b = v;
+                               } else {
+                                       r = v;
+                                       g = v1;
+                                       b = v2;
+                               }
+                       }
+
+                       return new SKColorF (r, g, b, a);
+               }
+
+               public readonly void ToHsl (out float h, out float s, out float l)
+               {
+                       // RGB from 0 to 1
+                       var r = fR;
+                       var g = fG;
+                       var b = fB;
+
+                       var min = Math.Min (Math.Min (r, g), b); // min value of RGB
+                       var max = Math.Max (Math.Max (r, g), b); // max value of RGB
+                       var delta = max - min; // delta RGB value
+
+                       // default to a gray, no chroma...
+                       h = 0f;
+                       s = 0f;
+                       l = (max + min) / 2f;
+
+                       // chromatic data...
+                       if (Math.Abs (delta) > EPSILON) {
+                               if (l < 0.5f)
+                                       s = delta / (max + min);
+                               else
+                                       s = delta / (2f - max - min);
+
+                               var deltaR = (((max - r) / 6f) + (delta / 2f)) / delta;
+                               var deltaG = (((max - g) / 6f) + (delta / 2f)) / delta;
+                               var deltaB = (((max - b) / 6f) + (delta / 2f)) / delta;
+
+                               if (Math.Abs (r - max) < EPSILON) // r == max
+                                       h = deltaB - deltaG;
+                               else if (Math.Abs (g - max) < EPSILON) // g == max
+                                       h = (1f / 3f) + deltaR - deltaB;
+                               else // b == max
+                                       h = (2f / 3f) + deltaG - deltaR;
+
+                               if (h < 0f)
+                                       h += 1f;
+                               if (h > 1f)
+                                       h -= 1f;
+                       }
+
+                       // convert to percentages
+                       h = h * 360f;
+                       s = s * 100f;
+                       l = l * 100f;
+               }
+
+               public readonly void ToHsv (out float h, out float s, out float v)
+               {
+                       // RGB from 0 to 1
+                       var r = fR;
+                       var g = fG;
+                       var b = fB;
+
+                       var min = Math.Min (Math.Min (r, g), b); // min value of RGB
+                       var max = Math.Max (Math.Max (r, g), b); // max value of RGB
+                       var delta = max - min; // delta RGB value 
+
+                       // default to a gray, no chroma...
+                       h = 0;
+                       s = 0;
+                       v = max;
+
+                       // chromatic data...
+                       if (Math.Abs (delta) > EPSILON) {
+                               s = delta / max;
+
+                               var deltaR = (((max - r) / 6f) + (delta / 2f)) / delta;
+                               var deltaG = (((max - g) / 6f) + (delta / 2f)) / delta;
+                               var deltaB = (((max - b) / 6f) + (delta / 2f)) / delta;
+
+                               if (Math.Abs (r - max) < EPSILON) // r == max
+                                       h = deltaB - deltaG;
+                               else if (Math.Abs (g - max) < EPSILON) // g == max
+                                       h = (1f / 3f) + deltaR - deltaB;
+                               else // b == max
+                                       h = (2f / 3f) + deltaG - deltaR;
+
+                               if (h < 0f)
+                                       h += 1f;
+                               if (h > 1f)
+                                       h -= 1f;
+                       }
+
+                       // convert to percentages
+                       h = h * 360f;
+                       s = s * 100f;
+                       v = v * 100f;
+               }
+
+               public readonly override string ToString () =>
+                       ((SKColor)this).ToString ();
+
+               public static implicit operator SKColorF (SKColor color)
+               {
+                       SKColorF colorF;
+                       SkiaApi.sk_color4f_from_color ((uint)color, &colorF);
+                       return colorF;
+               }
+
+               public static explicit operator SKColor (SKColorF color) =>
+                       SkiaApi.sk_color4f_to_color (&color);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColorFilter.cs b/src/XSF/SkiaSharp/SKColorFilter.cs
new file mode 100644 (file)
index 0000000..02da771
--- /dev/null
@@ -0,0 +1,96 @@
+using System;
+
+namespace SkiaSharp
+{
+       // TODO: `FilterColor` may be useful
+
+       public unsafe class SKColorFilter : SKObject, ISKReferenceCounted
+       {
+               public const int ColorMatrixSize = 20;
+               public const int TableMaxLength = 256;
+
+               [Preserve]
+               internal SKColorFilter(IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static SKColorFilter CreateBlendMode(SKColor c, SKBlendMode mode)
+               {
+                       return GetObject<SKColorFilter>(SkiaApi.sk_colorfilter_new_mode((uint)c, mode));
+               }
+
+               public static SKColorFilter CreateLighting(SKColor mul, SKColor add)
+               {
+                       return GetObject<SKColorFilter>(SkiaApi.sk_colorfilter_new_lighting((uint)mul, (uint)add));
+               }
+
+               public static SKColorFilter CreateCompose(SKColorFilter outer, SKColorFilter inner)
+               {
+                       if (outer == null)
+                               throw new ArgumentNullException(nameof(outer));
+                       if (inner == null)
+                               throw new ArgumentNullException(nameof(inner));
+                       return GetObject<SKColorFilter>(SkiaApi.sk_colorfilter_new_compose(outer.Handle, inner.Handle));
+               }
+
+               public static SKColorFilter CreateColorMatrix(float[] matrix)
+               {
+                       if (matrix == null)
+                               throw new ArgumentNullException(nameof(matrix));
+                       if (matrix.Length != 20)
+                               throw new ArgumentException("Matrix must have a length of 20.", nameof(matrix));
+                       fixed (float* m = matrix) {
+                               return GetObject<SKColorFilter> (SkiaApi.sk_colorfilter_new_color_matrix (m));
+                       }
+               }
+
+               public static SKColorFilter CreateLumaColor()
+               {
+                       return GetObject<SKColorFilter>(SkiaApi.sk_colorfilter_new_luma_color());
+               }
+
+               public static SKColorFilter CreateTable(byte[] table)
+               {
+                       if (table == null)
+                               throw new ArgumentNullException(nameof(table));
+                       if (table.Length != TableMaxLength)
+                               throw new ArgumentException($"Table must have a length of {TableMaxLength}.", nameof(table));
+                       fixed (byte* t = table) {
+                               return GetObject<SKColorFilter> (SkiaApi.sk_colorfilter_new_table (t));
+                       }
+               }
+
+               public static SKColorFilter CreateTable(byte[] tableA, byte[] tableR, byte[] tableG, byte[] tableB)
+               {
+                       if (tableA != null && tableA.Length != TableMaxLength)
+                               throw new ArgumentException($"Table A must have a length of {TableMaxLength}.", nameof(tableA));
+                       if (tableR != null && tableR.Length != TableMaxLength)
+                               throw new ArgumentException($"Table R must have a length of {TableMaxLength}.", nameof(tableR));
+                       if (tableG != null && tableG.Length != TableMaxLength)
+                               throw new ArgumentException($"Table G must have a length of {TableMaxLength}.", nameof(tableG));
+                       if (tableB != null && tableB.Length != TableMaxLength)
+                               throw new ArgumentException($"Table B must have a length of {TableMaxLength}.", nameof(tableB));
+
+                       fixed (byte* a = tableA)
+                       fixed (byte* r = tableR)
+                       fixed (byte* g = tableG)
+                       fixed (byte* b = tableB) {
+                               return GetObject<SKColorFilter> (SkiaApi.sk_colorfilter_new_table_argb (a, r, g, b));
+                       }
+               }
+
+               public static SKColorFilter CreateHighContrast(SKHighContrastConfig config)
+               {
+                       return GetObject<SKColorFilter>(SkiaApi.sk_colorfilter_new_high_contrast(&config));
+               }
+
+               public static SKColorFilter CreateHighContrast(bool grayscale, SKHighContrastConfigInvertStyle invertStyle, float contrast)
+               {
+                       return CreateHighContrast(new SKHighContrastConfig(grayscale, invertStyle, contrast));
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColorSpace.cs b/src/XSF/SkiaSharp/SKColorSpace.cs
new file mode 100644 (file)
index 0000000..f61407e
--- /dev/null
@@ -0,0 +1,196 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class SKColorSpace : SKObject, ISKReferenceCounted
+       {
+               private static readonly SKColorSpace srgb;
+               private static readonly SKColorSpace srgbLinear;
+
+               static SKColorSpace ()
+               {
+                       srgb = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb ());
+                       srgbLinear = new SKColorSpaceStatic (SkiaApi.sk_colorspace_new_srgb_linear ());
+               }
+
+               internal static void EnsureStaticInstanceAreInitialized ()
+               {
+                       // IMPORTANT: do not remove to ensure that the static instances
+                       //            are initialized before any access is made to them
+               }
+
+               [Preserve]
+               internal SKColorSpace (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // properties
+
+               public bool GammaIsCloseToSrgb =>
+                       SkiaApi.sk_colorspace_gamma_close_to_srgb (Handle);
+
+               public bool GammaIsLinear =>
+                       SkiaApi.sk_colorspace_gamma_is_linear (Handle);
+
+               public bool IsSrgb =>
+                       SkiaApi.sk_colorspace_is_srgb (Handle);
+
+               public SKColorSpaceType Type =>
+                       SkiaApi.sk_colorspace_gamma_get_type (Handle);
+
+               public SKNamedGamma NamedGamma =>
+                       SkiaApi.sk_colorspace_gamma_get_gamma_named (Handle);
+
+               public bool IsNumericalTransferFunction =>
+                       GetNumericalTransferFunction (out _);
+
+               public static bool Equal (SKColorSpace left, SKColorSpace right)
+               {
+                       if (left == null)
+                               throw new ArgumentNullException (nameof (left));
+                       if (right == null)
+                               throw new ArgumentNullException (nameof (right));
+
+                       return SkiaApi.sk_colorspace_equals (left.Handle, right.Handle);
+               }
+
+               // CreateSrgb
+
+               public static SKColorSpace CreateSrgb () => srgb;
+
+               // CreateSrgbLinear
+
+               public static SKColorSpace CreateSrgbLinear () => srgbLinear;
+
+               // CreateIcc
+
+               public static SKColorSpace CreateIcc (IntPtr input, long length)
+               {
+                       if (input == IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (input));
+
+                       return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_icc ((void*)input, (IntPtr)length));
+               }
+
+               public static SKColorSpace CreateIcc (byte[] input, long length)
+               {
+                       if (input == null)
+                               throw new ArgumentNullException (nameof (input));
+
+                       fixed (byte* i = input) {
+                               return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_icc (i, (IntPtr)length));
+                       }
+               }
+
+               public static SKColorSpace CreateIcc (byte[] input)
+               {
+                       if (input == null)
+                               throw new ArgumentNullException (nameof (input));
+
+                       fixed (byte* i = input) {
+                               return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_icc (i, (IntPtr)input.Length));
+                       }
+               }
+
+               // CreateRgb
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRgb(SKColorSpaceRenderTargetGamma, SKMatrix44) instead.")]
+               public static SKColorSpace CreateRgb (SKColorSpaceRenderTargetGamma gamma, SKMatrix44 toXyzD50, SKColorSpaceFlags flags) =>
+                       CreateRgb (gamma, toXyzD50);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRgb(SKColorSpaceRenderTargetGamma, SKColorSpaceGamut) instead.")]
+               public static SKColorSpace CreateRgb (SKColorSpaceRenderTargetGamma gamma, SKColorSpaceGamut gamut, SKColorSpaceFlags flags) =>
+                       CreateRgb (gamma, gamut);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRgb(SKColorSpaceTransferFn, SKMatrix44) instead.")]
+               public static SKColorSpace CreateRgb (SKColorSpaceTransferFn coeffs, SKMatrix44 toXyzD50, SKColorSpaceFlags flags) =>
+                       CreateRgb (coeffs, toXyzD50);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRgb(SKColorSpaceTransferFn, SKColorSpaceGamut) instead.")]
+               public static SKColorSpace CreateRgb (SKColorSpaceTransferFn coeffs, SKColorSpaceGamut gamut, SKColorSpaceFlags flags) =>
+                       CreateRgb (coeffs, gamut);
+
+               public static SKColorSpace CreateRgb (SKColorSpaceRenderTargetGamma gamma, SKMatrix44 toXyzD50)
+               {
+                       if (toXyzD50 == null)
+                               throw new ArgumentNullException (nameof (toXyzD50));
+
+                       return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_gamma (gamma, toXyzD50.Handle));
+               }
+
+               public static SKColorSpace CreateRgb (SKColorSpaceRenderTargetGamma gamma, SKColorSpaceGamut gamut) =>
+                       GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_gamma_and_gamut (gamma, gamut));
+
+               public static SKColorSpace CreateRgb (SKColorSpaceTransferFn coeffs, SKMatrix44 toXyzD50)
+               {
+                       if (toXyzD50 == null)
+                               throw new ArgumentNullException (nameof (toXyzD50));
+
+                       return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_coeffs (&coeffs, toXyzD50.Handle));
+               }
+
+               public static SKColorSpace CreateRgb (SKColorSpaceTransferFn coeffs, SKColorSpaceGamut gamut) =>
+                       GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_coeffs_and_gamut (&coeffs, gamut));
+
+               public static SKColorSpace CreateRgb (SKNamedGamma gamma, SKMatrix44 toXyzD50)
+               {
+                       if (toXyzD50 == null)
+                               throw new ArgumentNullException (nameof (toXyzD50));
+
+                       return GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_gamma_named (gamma, toXyzD50.Handle));
+               }
+
+               public static SKColorSpace CreateRgb (SKNamedGamma gamma, SKColorSpaceGamut gamut) =>
+                       GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_gamma_named_and_gamut (gamma, gamut));
+
+               // GetNumericalTransferFunction
+
+               public bool GetNumericalTransferFunction (out SKColorSpaceTransferFn fn)
+               {
+                       fixed (SKColorSpaceTransferFn* f = &fn) {
+                               return SkiaApi.sk_colorspace_is_numerical_transfer_fn (Handle, f);
+                       }
+               }
+
+               // *XyzD50
+
+               public SKMatrix44 ToXyzD50 () =>
+                       OwnedBy (GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_to_xyzd50 (Handle), false), this);
+
+               public bool ToXyzD50 (SKMatrix44 toXyzD50)
+               {
+                       if (toXyzD50 == null)
+                               throw new ArgumentNullException (nameof (toXyzD50));
+
+                       return SkiaApi.sk_colorspace_to_xyzd50 (Handle, toXyzD50.Handle);
+               }
+
+               public SKMatrix44 FromXyzD50 () =>
+                       OwnedBy (GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_from_xyzd50 (Handle), false), this);
+
+               //
+
+               private sealed class SKColorSpaceStatic : SKColorSpace
+               {
+                       internal SKColorSpaceStatic (IntPtr x)
+                               : base (x, false)
+                       {
+                               IgnorePublicDispose = true;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColorSpaceStructs.cs b/src/XSF/SkiaSharp/SKColorSpaceStructs.cs
new file mode 100644 (file)
index 0000000..1ea242f
--- /dev/null
@@ -0,0 +1,112 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe partial struct SKColorSpacePrimaries
+       {
+               public static readonly SKColorSpacePrimaries Empty;
+
+               public SKColorSpacePrimaries (float[] values)
+               {
+                       if (values == null)
+                               throw new ArgumentNullException (nameof (values));
+                       if (values.Length != 8)
+                               throw new ArgumentException ("The values must have exactly 8 items, one for each of [RX, RY, GX, GY, BX, BY, WX, WY].", nameof (values));
+
+                       fRX = values[0];
+                       fRY = values[1];
+                       fGX = values[2];
+                       fGY = values[3];
+                       fBX = values[4];
+                       fBY = values[5];
+                       fWX = values[6];
+                       fWY = values[7];
+               }
+
+               public SKColorSpacePrimaries (float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy)
+               {
+                       fRX = rx;
+                       fRY = ry;
+                       fGX = gx;
+                       fGY = gy;
+                       fBX = bx;
+                       fBY = by;
+                       fWX = wx;
+                       fWY = wy;
+               }
+
+               public readonly float[] Values =>
+                       new[] { fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY };
+
+               public readonly SKMatrix44 ToXyzD50 ()
+               {
+                       var xyzD50 = new SKMatrix44 ();
+                       if (!ToXyzD50 (xyzD50)) {
+                               xyzD50.Dispose ();
+                               xyzD50 = null;
+                       }
+                       return xyzD50;
+               }
+
+               public readonly bool ToXyzD50 (SKMatrix44 toXyzD50)
+               {
+                       if (toXyzD50 == null)
+                               throw new ArgumentNullException (nameof (toXyzD50));
+
+                       fixed (SKColorSpacePrimaries* t = &this) {
+                               return SkiaApi.sk_colorspaceprimaries_to_xyzd50 (t, toXyzD50.Handle);
+                       }
+               }
+       }
+
+       public unsafe partial struct SKColorSpaceTransferFn
+       {
+               public static readonly SKColorSpaceTransferFn Empty;
+
+               public SKColorSpaceTransferFn (float[] values)
+               {
+                       if (values == null)
+                               throw new ArgumentNullException (nameof (values));
+                       if (values.Length != 7)
+                               throw new ArgumentException ("The values must have exactly 7 items, one for each of [G, A, B, C, D, E, F].", nameof (values));
+
+                       fG = values[0];
+                       fA = values[1];
+                       fB = values[2];
+                       fC = values[3];
+                       fD = values[4];
+                       fE = values[5];
+                       fF = values[6];
+               }
+
+               public SKColorSpaceTransferFn (float g, float a, float b, float c, float d, float e, float f)
+               {
+                       fG = g;
+                       fA = a;
+                       fB = b;
+                       fC = c;
+                       fD = d;
+                       fE = e;
+                       fF = f;
+               }
+
+               public readonly float[] Values =>
+                       new[] { fG, fA, fB, fC, fD, fE, fF };
+
+               public readonly SKColorSpaceTransferFn Invert ()
+               {
+                       SKColorSpaceTransferFn inverted;
+                       fixed (SKColorSpaceTransferFn* t = &this) {
+                               SkiaApi.sk_colorspace_transfer_fn_invert (t, &inverted);
+                       }
+                       return inverted;
+               }
+
+               public readonly float Transform (float x)
+               {
+                       fixed (SKColorSpaceTransferFn* t = &this) {
+                               return SkiaApi.sk_colorspace_transfer_fn_transform (t, x);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKColorTable.cs b/src/XSF/SkiaSharp/SKColorTable.cs
new file mode 100644 (file)
index 0000000..852c8b2
--- /dev/null
@@ -0,0 +1,101 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete ("The Index8 color type and color table is no longer supported.")]
+       public unsafe class SKColorTable : SKObject, ISKReferenceCounted
+       {
+               public const int MaxLength = 256;
+
+               [Preserve]
+               internal SKColorTable (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               public SKColorTable ()
+                       : this (new SKPMColor[MaxLength])
+               {
+               }
+
+               public SKColorTable (int count)
+                       : this (new SKPMColor[count])
+               {
+               }
+
+               public SKColorTable (SKColor[] colors)
+                       : this (colors, colors.Length)
+               {
+               }
+
+               public SKColorTable (SKColor[] colors, int count)
+                       : this (SKPMColor.PreMultiply (colors), count)
+               {
+               }
+
+               public SKColorTable (SKPMColor[] colors)
+                       : this (colors, colors.Length)
+               {
+               }
+
+               public SKColorTable (SKPMColor[] colors, int count)
+                       : this (CreateNew (colors, count), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKColorTable instance.");
+                       }
+               }
+
+               private static IntPtr CreateNew (SKPMColor[] colors, int count)
+               {
+                       fixed (SKPMColor* c = colors) {
+                               return SkiaApi.sk_colortable_new ((uint*)c, count);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public int Count => SkiaApi.sk_colortable_count (Handle);
+
+               public SKPMColor[] Colors {
+                       get {
+                               var count = Count;
+                               var pointer = ReadColors ();
+
+                               if (count == 0 || pointer == IntPtr.Zero) {
+                                       return new SKPMColor[0];
+                               }
+
+                               return PtrToStructureArray<SKPMColor> (pointer, count);
+                       }
+               }
+
+               public SKColor[] UnPreMultipledColors => SKPMColor.UnPreMultiply (Colors);
+
+               public SKPMColor this[int index] {
+                       get {
+                               var count = Count;
+                               var pointer = ReadColors ();
+
+                               if (index < 0 || index >= count || pointer == IntPtr.Zero) {
+                                       throw new ArgumentOutOfRangeException (nameof (index));
+                               }
+
+                               return PtrToStructure<SKPMColor> (pointer, index);
+                       }
+               }
+
+               public SKColor GetUnPreMultipliedColor (int index) => SKPMColor.UnPreMultiply (this[index]);
+
+               public IntPtr ReadColors ()
+               {
+                       uint* colors;
+                       SkiaApi.sk_colortable_read_colors (Handle, &colors);
+                       return (IntPtr)colors;
+               }
+       }
+}
+
diff --git a/src/XSF/SkiaSharp/SKColors.cs b/src/XSF/SkiaSharp/SKColors.cs
new file mode 100644 (file)
index 0000000..2c44960
--- /dev/null
@@ -0,0 +1,151 @@
+using System;
+
+namespace SkiaSharp
+{
+       public struct SKColors
+       {
+               public static SKColor Empty => new SKColor (0x00000000);
+               public static SKColor AliceBlue = new SKColor (0xFFF0F8FF);
+               public static SKColor AntiqueWhite = new SKColor (0xFFFAEBD7);
+               public static SKColor Aqua = new SKColor (0xFF00FFFF);
+               public static SKColor Aquamarine = new SKColor (0xFF7FFFD4);
+               public static SKColor Azure = new SKColor (0xFFF0FFFF);
+               public static SKColor Beige = new SKColor (0xFFF5F5DC);
+               public static SKColor Bisque = new SKColor (0xFFFFE4C4);
+               public static SKColor Black = new SKColor (0xFF000000);
+               public static SKColor BlanchedAlmond = new SKColor (0xFFFFEBCD);
+               public static SKColor Blue = new SKColor (0xFF0000FF);
+               public static SKColor BlueViolet = new SKColor (0xFF8A2BE2);
+               public static SKColor Brown = new SKColor (0xFFA52A2A);
+               public static SKColor BurlyWood = new SKColor (0xFFDEB887);
+               public static SKColor CadetBlue = new SKColor (0xFF5F9EA0);
+               public static SKColor Chartreuse = new SKColor (0xFF7FFF00);
+               public static SKColor Chocolate = new SKColor (0xFFD2691E);
+               public static SKColor Coral = new SKColor (0xFFFF7F50);
+               public static SKColor CornflowerBlue = new SKColor (0xFF6495ED);
+               public static SKColor Cornsilk = new SKColor (0xFFFFF8DC);
+               public static SKColor Crimson = new SKColor (0xFFDC143C);
+               public static SKColor Cyan = new SKColor (0xFF00FFFF);
+               public static SKColor DarkBlue = new SKColor (0xFF00008B);
+               public static SKColor DarkCyan = new SKColor (0xFF008B8B);
+               public static SKColor DarkGoldenrod = new SKColor (0xFFB8860B);
+               public static SKColor DarkGray = new SKColor (0xFFA9A9A9);
+               public static SKColor DarkGreen = new SKColor (0xFF006400);
+               public static SKColor DarkKhaki = new SKColor (0xFFBDB76B);
+               public static SKColor DarkMagenta = new SKColor (0xFF8B008B);
+               public static SKColor DarkOliveGreen = new SKColor (0xFF556B2F);
+               public static SKColor DarkOrange = new SKColor (0xFFFF8C00);
+               public static SKColor DarkOrchid = new SKColor (0xFF9932CC);
+               public static SKColor DarkRed = new SKColor (0xFF8B0000);
+               public static SKColor DarkSalmon = new SKColor (0xFFE9967A);
+               public static SKColor DarkSeaGreen = new SKColor (0xFF8FBC8B);
+               public static SKColor DarkSlateBlue = new SKColor (0xFF483D8B);
+               public static SKColor DarkSlateGray = new SKColor (0xFF2F4F4F);
+               public static SKColor DarkTurquoise = new SKColor (0xFF00CED1);
+               public static SKColor DarkViolet = new SKColor (0xFF9400D3);
+               public static SKColor DeepPink = new SKColor (0xFFFF1493);
+               public static SKColor DeepSkyBlue = new SKColor (0xFF00BFFF);
+               public static SKColor DimGray = new SKColor (0xFF696969);
+               public static SKColor DodgerBlue = new SKColor (0xFF1E90FF);
+               public static SKColor Firebrick = new SKColor (0xFFB22222);
+               public static SKColor FloralWhite = new SKColor (0xFFFFFAF0);
+               public static SKColor ForestGreen = new SKColor (0xFF228B22);
+               public static SKColor Fuchsia = new SKColor (0xFFFF00FF);
+               public static SKColor Gainsboro = new SKColor (0xFFDCDCDC);
+               public static SKColor GhostWhite = new SKColor (0xFFF8F8FF);
+               public static SKColor Gold = new SKColor (0xFFFFD700);
+               public static SKColor Goldenrod = new SKColor (0xFFDAA520);
+               public static SKColor Gray = new SKColor (0xFF808080);
+               public static SKColor Green = new SKColor (0xFF008000);
+               public static SKColor GreenYellow = new SKColor (0xFFADFF2F);
+               public static SKColor Honeydew = new SKColor (0xFFF0FFF0);
+               public static SKColor HotPink = new SKColor (0xFFFF69B4);
+               public static SKColor IndianRed = new SKColor (0xFFCD5C5C);
+               public static SKColor Indigo = new SKColor (0xFF4B0082);
+               public static SKColor Ivory = new SKColor (0xFFFFFFF0);
+               public static SKColor Khaki = new SKColor (0xFFF0E68C);
+               public static SKColor Lavender = new SKColor (0xFFE6E6FA);
+               public static SKColor LavenderBlush = new SKColor (0xFFFFF0F5);
+               public static SKColor LawnGreen = new SKColor (0xFF7CFC00);
+               public static SKColor LemonChiffon = new SKColor (0xFFFFFACD);
+               public static SKColor LightBlue = new SKColor (0xFFADD8E6);
+               public static SKColor LightCoral = new SKColor (0xFFF08080);
+               public static SKColor LightCyan = new SKColor (0xFFE0FFFF);
+               public static SKColor LightGoldenrodYellow = new SKColor (0xFFFAFAD2);
+               public static SKColor LightGray = new SKColor (0xFFD3D3D3);
+               public static SKColor LightGreen = new SKColor (0xFF90EE90);
+               public static SKColor LightPink = new SKColor (0xFFFFB6C1);
+               public static SKColor LightSalmon = new SKColor (0xFFFFA07A);
+               public static SKColor LightSeaGreen = new SKColor (0xFF20B2AA);
+               public static SKColor LightSkyBlue = new SKColor (0xFF87CEFA);
+               public static SKColor LightSlateGray = new SKColor (0xFF778899);
+               public static SKColor LightSteelBlue = new SKColor (0xFFB0C4DE);
+               public static SKColor LightYellow = new SKColor (0xFFFFFFE0);
+               public static SKColor Lime = new SKColor (0xFF00FF00);
+               public static SKColor LimeGreen = new SKColor (0xFF32CD32);
+               public static SKColor Linen = new SKColor (0xFFFAF0E6);
+               public static SKColor Magenta = new SKColor (0xFFFF00FF);
+               public static SKColor Maroon = new SKColor (0xFF800000);
+               public static SKColor MediumAquamarine = new SKColor (0xFF66CDAA);
+               public static SKColor MediumBlue = new SKColor (0xFF0000CD);
+               public static SKColor MediumOrchid = new SKColor (0xFFBA55D3);
+               public static SKColor MediumPurple = new SKColor (0xFF9370DB);
+               public static SKColor MediumSeaGreen = new SKColor (0xFF3CB371);
+               public static SKColor MediumSlateBlue = new SKColor (0xFF7B68EE);
+               public static SKColor MediumSpringGreen = new SKColor (0xFF00FA9A);
+               public static SKColor MediumTurquoise = new SKColor (0xFF48D1CC);
+               public static SKColor MediumVioletRed = new SKColor (0xFFC71585);
+               public static SKColor MidnightBlue = new SKColor (0xFF191970);
+               public static SKColor MintCream = new SKColor (0xFFF5FFFA);
+               public static SKColor MistyRose = new SKColor (0xFFFFE4E1);
+               public static SKColor Moccasin = new SKColor (0xFFFFE4B5);
+               public static SKColor NavajoWhite = new SKColor (0xFFFFDEAD);
+               public static SKColor Navy = new SKColor (0xFF000080);
+               public static SKColor OldLace = new SKColor (0xFFFDF5E6);
+               public static SKColor Olive = new SKColor (0xFF808000);
+               public static SKColor OliveDrab = new SKColor (0xFF6B8E23);
+               public static SKColor Orange = new SKColor (0xFFFFA500);
+               public static SKColor OrangeRed = new SKColor (0xFFFF4500);
+               public static SKColor Orchid = new SKColor (0xFFDA70D6);
+               public static SKColor PaleGoldenrod = new SKColor (0xFFEEE8AA);
+               public static SKColor PaleGreen = new SKColor (0xFF98FB98);
+               public static SKColor PaleTurquoise = new SKColor (0xFFAFEEEE);
+               public static SKColor PaleVioletRed = new SKColor (0xFFDB7093);
+               public static SKColor PapayaWhip = new SKColor (0xFFFFEFD5);
+               public static SKColor PeachPuff = new SKColor (0xFFFFDAB9);
+               public static SKColor Peru = new SKColor (0xFFCD853F);
+               public static SKColor Pink = new SKColor (0xFFFFC0CB);
+               public static SKColor Plum = new SKColor (0xFFDDA0DD);
+               public static SKColor PowderBlue = new SKColor (0xFFB0E0E6);
+               public static SKColor Purple = new SKColor (0xFF800080);
+               public static SKColor Red = new SKColor (0xFFFF0000);
+               public static SKColor RosyBrown = new SKColor (0xFFBC8F8F);
+               public static SKColor RoyalBlue = new SKColor (0xFF4169E1);
+               public static SKColor SaddleBrown = new SKColor (0xFF8B4513);
+               public static SKColor Salmon = new SKColor (0xFFFA8072);
+               public static SKColor SandyBrown = new SKColor (0xFFF4A460);
+               public static SKColor SeaGreen = new SKColor (0xFF2E8B57);
+               public static SKColor SeaShell = new SKColor (0xFFFFF5EE);
+               public static SKColor Sienna = new SKColor (0xFFA0522D);
+               public static SKColor Silver = new SKColor (0xFFC0C0C0);
+               public static SKColor SkyBlue = new SKColor (0xFF87CEEB);
+               public static SKColor SlateBlue = new SKColor (0xFF6A5ACD);
+               public static SKColor SlateGray = new SKColor (0xFF708090);
+               public static SKColor Snow = new SKColor (0xFFFFFAFA);
+               public static SKColor SpringGreen = new SKColor (0xFF00FF7F);
+               public static SKColor SteelBlue = new SKColor (0xFF4682B4);
+               public static SKColor Tan = new SKColor (0xFFD2B48C);
+               public static SKColor Teal = new SKColor (0xFF008080);
+               public static SKColor Thistle = new SKColor (0xFFD8BFD8);
+               public static SKColor Tomato = new SKColor (0xFFFF6347);
+               public static SKColor Turquoise = new SKColor (0xFF40E0D0);
+               public static SKColor Violet = new SKColor (0xFFEE82EE);
+               public static SKColor Wheat = new SKColor (0xFFF5DEB3);
+               public static SKColor White = new SKColor (0xFFFFFFFF);
+               public static SKColor WhiteSmoke = new SKColor (0xFFF5F5F5);
+               public static SKColor Yellow = new SKColor (0xFFFFFF00);
+               public static SKColor YellowGreen = new SKColor (0xFF9ACD32);
+               public static SKColor Transparent = new SKColor (0x00FFFFFF);
+               
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKData.cs b/src/XSF/SkiaSharp/SKData.cs
new file mode 100644 (file)
index 0000000..395b39e
--- /dev/null
@@ -0,0 +1,296 @@
+using System;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Text;
+using System.ComponentModel;
+using System.Buffers;
+
+namespace SkiaSharp
+{
+       public unsafe class SKData : SKObject, ISKNonVirtualReferenceCounted
+       {
+               // We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
+               // The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
+               // improvement in Copy performance.
+               internal const int CopyBufferSize = 81920;
+
+               private static readonly Lazy<SKData> empty;
+
+               static SKData()
+               {
+                       empty = new Lazy<SKData> (() => new SKDataStatic (SkiaApi.sk_data_new_empty ()));
+               }
+
+               internal static void EnsureStaticInstanceAreInitialized ()
+               {
+                       // IMPORTANT: do not remove to ensure that the static instances
+                       //            are initialized before any access is made to them
+               }
+
+               [Preserve]
+               internal SKData (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               void ISKNonVirtualReferenceCounted.ReferenceNative () => SkiaApi.sk_data_ref (Handle);
+
+               void ISKNonVirtualReferenceCounted.UnreferenceNative () => SkiaApi.sk_data_unref (Handle);
+
+               public static SKData Empty => empty.Value;
+
+               // CreateCopy
+
+               public static SKData CreateCopy (IntPtr bytes, ulong length)
+               {
+                       if (SizeOf <IntPtr> () == 4 && length > UInt32.MaxValue)
+                               throw new ArgumentOutOfRangeException (nameof (length), "The length exceeds the size of pointers.");
+                       return GetObject<SKData> (SkiaApi.sk_data_new_with_copy ((void*)bytes, (IntPtr) length));
+               }
+
+               public static SKData CreateCopy (byte[] bytes) =>
+                       CreateCopy (bytes, (ulong)bytes.Length);
+
+               public static SKData CreateCopy (byte[] bytes, ulong length)
+               {
+                       fixed (byte* b = bytes) {
+                               return GetObject<SKData> (SkiaApi.sk_data_new_with_copy (b, (IntPtr)length));
+                       }
+               }
+
+               public static SKData CreateCopy (ReadOnlySpan<byte> bytes)
+               {
+                       fixed (byte* b = bytes) {
+                               return CreateCopy ((IntPtr)b, (ulong)bytes.Length);
+                       }
+               }
+
+               // Create
+
+               public static SKData Create (int size)
+               {
+                       return GetObject<SKData> (SkiaApi.sk_data_new_uninitialized ((IntPtr) size));
+               }
+
+               public static SKData Create (ulong size)
+               {
+                       if (SizeOf <IntPtr> () == 4 && size > UInt32.MaxValue)
+                               throw new ArgumentOutOfRangeException (nameof (size), "The size exceeds the size of pointers.");
+                               
+                       return GetObject<SKData> (SkiaApi.sk_data_new_uninitialized ((IntPtr) size));
+               }
+
+               public static SKData Create (string filename)
+               {
+                       if (string.IsNullOrEmpty (filename))
+                               throw new ArgumentException ("The filename cannot be empty.", nameof (filename));
+
+                       var utf8path = StringUtilities.GetEncodedText (filename, SKTextEncoding.Utf8);
+                       fixed (byte* u = utf8path) {
+                               return GetObject<SKData> (SkiaApi.sk_data_new_from_file (u));
+                       }
+               }
+
+               public static SKData Create (Stream stream)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+                       return Create (stream, stream.Length);
+               }
+
+               public static SKData Create (Stream stream, int length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       using (var managed = new SKManagedStream (stream))
+                               return Create (managed, length);
+               }
+
+               public static SKData Create (Stream stream, ulong length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       using (var managed = new SKManagedStream (stream))
+                               return Create (managed, length);
+               }
+
+               public static SKData Create (Stream stream, long length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       using (var managed = new SKManagedStream (stream))
+                               return Create (managed, length);
+               }
+
+               public static SKData Create (SKStream stream)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return Create (stream, stream.Length);
+               }
+
+               public static SKData Create (SKStream stream, int length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return GetObject<SKData> (SkiaApi.sk_data_new_from_stream (stream.Handle, (IntPtr) length));
+               }
+
+               public static SKData Create (SKStream stream, ulong length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return GetObject<SKData> (SkiaApi.sk_data_new_from_stream (stream.Handle, (IntPtr) length));
+               }
+
+               public static SKData Create (SKStream stream, long length)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return GetObject<SKData> (SkiaApi.sk_data_new_from_stream (stream.Handle, (IntPtr) length));
+               }
+
+               public static SKData Create (IntPtr address, int length)
+               {
+                       return Create (address, length, null, null);
+               }
+
+               public static SKData Create (IntPtr address, int length, SKDataReleaseDelegate releaseProc)
+               {
+                       return Create (address, length, releaseProc, null);
+               }
+
+               public static SKData Create (IntPtr address, int length, SKDataReleaseDelegate releaseProc, object context)
+               {
+                       var del = releaseProc != null && context != null
+                               ? new SKDataReleaseDelegate ((addr, _) => releaseProc (addr, context))
+                               : releaseProc;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.SKDataReleaseDelegateProxy, out _, out var ctx);
+                       return GetObject<SKData> (SkiaApi.sk_data_new_with_proc ((void*)address, (IntPtr)length, proxy, (void*)ctx));
+               }
+
+               internal static SKData FromCString (string str)
+               {
+                       var bytes = Encoding.ASCII.GetBytes (str ?? string.Empty);
+                       return SKData.CreateCopy (bytes, (ulong)(bytes.Length + 1)); // + 1 for the terminating char
+               }
+
+               // Subset
+
+               public SKData Subset (ulong offset, ulong length)
+               {
+                       if (SizeOf <IntPtr> () == 4) {
+                               if (length > UInt32.MaxValue)
+                                       throw new ArgumentOutOfRangeException (nameof (length), "The length exceeds the size of pointers.");
+                               if (offset > UInt32.MaxValue)
+                                       throw new ArgumentOutOfRangeException (nameof (offset), "The offset exceeds the size of pointers.");
+                       }
+                       return GetObject<SKData> (SkiaApi.sk_data_new_subset (Handle, (IntPtr) offset, (IntPtr) length));
+               }
+
+               // ToArray
+
+               public byte[] ToArray ()
+               {
+                       var array = AsSpan ().ToArray ();
+                       GC.KeepAlive (this);
+                       return array;
+               }
+
+               // properties
+
+               public bool IsEmpty => Size == 0;
+
+               public long Size => (long)SkiaApi.sk_data_get_size (Handle);
+
+               public IntPtr Data => (IntPtr)SkiaApi.sk_data_get_data (Handle);
+
+               // AsStream
+
+               public Stream AsStream () =>
+                       new SKDataStream (this, false);
+
+               public Stream AsStream (bool streamDisposesData) =>
+                       new SKDataStream (this, streamDisposesData);
+
+               // AsSpan
+
+               public ReadOnlySpan<byte> AsSpan ()
+               {
+                       return new ReadOnlySpan<byte> ((void*)Data, (int)Size);
+               }
+
+               // SaveTo
+
+               public void SaveTo (Stream target)
+               {
+                       if (target == null)
+                               throw new ArgumentNullException (nameof (target));
+
+                       var ptr = Data;
+                       var total = Size;
+                       var buffer = ArrayPool<byte>.Shared.Rent (CopyBufferSize);
+                       try {
+                               for (var left = total; left > 0;) {
+                                       var copyCount = (int)Math.Min (CopyBufferSize, left);
+                                       Marshal.Copy (ptr, buffer, 0, copyCount);
+                                       left -= copyCount;
+                                       ptr += copyCount;
+                                       target.Write (buffer, 0, copyCount);
+                               }
+                       } finally {
+                               ArrayPool<byte>.Shared.Return (buffer);
+                       }
+                       GC.KeepAlive (this);
+               }
+
+               //
+
+               private class SKDataStream : UnmanagedMemoryStream
+               {
+                       private SKData host;
+                       private readonly bool disposeHost;
+
+                       public unsafe SKDataStream (SKData host, bool disposeHost = false)
+                               : base((byte *) host.Data, host.Size)
+                       {
+                               this.host = host;
+                               this.disposeHost = disposeHost;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               base.Dispose (disposing);
+
+                               if (disposeHost) {
+                                       host?.Dispose ();
+                               }
+                               host = null;
+                       }
+               }
+
+               private sealed class SKDataStatic : SKData
+               {
+                       internal SKDataStatic (IntPtr x)
+                               : base (x, false)
+                       {
+                               IgnorePublicDispose = true;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKDocument.cs b/src/XSF/SkiaSharp/SKDocument.cs
new file mode 100644 (file)
index 0000000..ed0233d
--- /dev/null
@@ -0,0 +1,182 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public unsafe class SKDocument : SKObject, ISKReferenceCounted
+       {
+               public const float DefaultRasterDpi = 72.0f;
+
+               [Preserve]
+               internal SKDocument (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public void Abort () =>
+                       SkiaApi.sk_document_abort (Handle);
+
+               public SKCanvas BeginPage (float width, float height) =>
+                       OwnedBy (GetObject<SKCanvas> (SkiaApi.sk_document_begin_page (Handle, width, height, null), false), this);
+
+               public SKCanvas BeginPage (float width, float height, SKRect content) =>
+                       OwnedBy (GetObject<SKCanvas> (SkiaApi.sk_document_begin_page (Handle, width, height, &content), false), this);
+
+               public void EndPage () =>
+                       SkiaApi.sk_document_end_page (Handle);
+
+               public void Close () =>
+                       SkiaApi.sk_document_close (Handle);
+
+               // CreateXps
+
+               public static SKDocument CreateXps (string path) =>
+                       CreateXps (path, DefaultRasterDpi);
+
+               public static SKDocument CreateXps (Stream stream) =>
+                       CreateXps (stream, DefaultRasterDpi);
+
+               public static SKDocument CreateXps (SKWStream stream) =>
+                       CreateXps (stream, DefaultRasterDpi);
+
+               public static SKDocument CreateXps (string path, float dpi)
+               {
+                       if (path == null) {
+                               throw new ArgumentNullException (nameof (path));
+                       }
+
+                       var stream = SKFileWStream.OpenStream (path);
+                       return Owned (CreateXps (stream, dpi), stream);
+               }
+
+               public static SKDocument CreateXps (Stream stream, float dpi)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       var managed = new SKManagedWStream (stream);
+                       return Owned (CreateXps (managed, dpi), managed);
+               }
+
+               public static SKDocument CreateXps (SKWStream stream, float dpi)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       return Referenced (GetObject<SKDocument> (SkiaApi.sk_document_create_xps_from_stream (stream.Handle, dpi)), stream);
+               }
+
+               // CreatePdf
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreatePdf(SKWStream, SKDocumentPdfMetadata) instead.")]
+               public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata, float dpi)
+               {
+                       metadata.RasterDpi = dpi;
+                       return CreatePdf (stream, metadata);
+               }
+
+               public static SKDocument CreatePdf (string path)
+               {
+                       if (path == null) {
+                               throw new ArgumentNullException (nameof (path));
+                       }
+
+                       var stream = SKFileWStream.OpenStream (path);
+                       return Owned (CreatePdf (stream), stream);
+               }
+
+               public static SKDocument CreatePdf (Stream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       var managed = new SKManagedWStream (stream);
+                       return Owned (CreatePdf (managed), managed);
+               }
+
+               public static SKDocument CreatePdf (SKWStream stream)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       return Referenced (GetObject<SKDocument> (SkiaApi.sk_document_create_pdf_from_stream (stream.Handle)), stream);
+               }
+
+               public static SKDocument CreatePdf (string path, float dpi) =>
+                       CreatePdf (path, new SKDocumentPdfMetadata (dpi));
+
+               public static SKDocument CreatePdf (Stream stream, float dpi) =>
+                       CreatePdf (stream, new SKDocumentPdfMetadata (dpi));
+
+               public static SKDocument CreatePdf (SKWStream stream, float dpi) =>
+                       CreatePdf (stream, new SKDocumentPdfMetadata (dpi));
+
+               public static SKDocument CreatePdf (string path, SKDocumentPdfMetadata metadata)
+               {
+                       if (path == null) {
+                               throw new ArgumentNullException (nameof (path));
+                       }
+
+                       var stream = SKFileWStream.OpenStream (path);
+                       return Owned (CreatePdf (stream, metadata), stream);
+               }
+
+               public static SKDocument CreatePdf (Stream stream, SKDocumentPdfMetadata metadata)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       var managed = new SKManagedWStream (stream);
+                       return Owned (CreatePdf (managed, metadata), managed);
+               }
+
+               public static SKDocument CreatePdf (SKWStream stream, SKDocumentPdfMetadata metadata)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       using (var title = SKString.Create (metadata.Title))
+                       using (var author = SKString.Create (metadata.Author))
+                       using (var subject = SKString.Create (metadata.Subject))
+                       using (var keywords = SKString.Create (metadata.Keywords))
+                       using (var creator = SKString.Create (metadata.Creator))
+                       using (var producer = SKString.Create (metadata.Producer)) {
+                               var cmetadata = new SKDocumentPdfMetadataInternal {
+                                       fTitle = title?.Handle ?? IntPtr.Zero,
+                                       fAuthor = author?.Handle ?? IntPtr.Zero,
+                                       fSubject = subject?.Handle ?? IntPtr.Zero,
+                                       fKeywords = keywords?.Handle ?? IntPtr.Zero,
+                                       fCreator = creator?.Handle ?? IntPtr.Zero,
+                                       fProducer = producer?.Handle ?? IntPtr.Zero,
+                                       fRasterDPI = metadata.RasterDpi,
+                                       fPDFA = metadata.PdfA ? (byte)1 : (byte)0,
+                                       fEncodingQuality = metadata.EncodingQuality,
+                               };
+
+                               SKTimeDateTimeInternal creation;
+                               if (metadata.Creation != null) {
+                                       creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value);
+                                       cmetadata.fCreation = &creation;
+                               }
+                               SKTimeDateTimeInternal modified;
+                               if (metadata.Modified != null) {
+                                       modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value);
+                                       cmetadata.fModified = &modified;
+                               }
+
+                               return Referenced (GetObject<SKDocument> (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKDrawable.cs b/src/XSF/SkiaSharp/SKDrawable.cs
new file mode 100644 (file)
index 0000000..5fc68af
--- /dev/null
@@ -0,0 +1,134 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace SkiaSharp
+{
+       public unsafe class SKDrawable : SKObject, ISKReferenceCounted
+       {
+               private static readonly SKManagedDrawableDelegates delegates;
+
+               private int fromNative;
+
+               static SKDrawable ()
+               {
+                       delegates = new SKManagedDrawableDelegates {
+                               fDraw = DrawInternal,
+                               fGetBounds = GetBoundsInternal,
+                               fNewPictureSnapshot = NewPictureSnapshotInternal,
+                               fDestroy = DestroyInternal,
+                       };
+
+                       SkiaApi.sk_manageddrawable_set_procs (delegates);
+               }
+
+               protected SKDrawable ()
+                       : this (true)
+               {
+               }
+
+               protected SKDrawable (bool owns)
+                       : base (IntPtr.Zero, owns)
+               {
+                       var ctx = DelegateProxies.CreateUserData (this, true);
+                       Handle = SkiaApi.sk_manageddrawable_new ((void*)ctx);
+
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKDrawable instance.");
+                       }
+               }
+
+               [Preserve]
+               internal SKDrawable (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative ()
+               {
+                       if (Interlocked.CompareExchange (ref fromNative, 0, 0) == 0)
+                               SkiaApi.sk_drawable_unref (Handle);
+               }
+
+               public uint GenerationId => SkiaApi.sk_drawable_get_generation_id (Handle);
+
+               public SKRect Bounds {
+                       get {
+                               SKRect bounds;
+                               SkiaApi.sk_drawable_get_bounds (Handle, &bounds);
+                               return bounds;
+                       }
+               }
+
+               public void Draw (SKCanvas canvas, ref SKMatrix matrix)
+               {
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_drawable_draw (Handle, canvas.Handle, m);
+                       }
+               }
+
+               public void Draw (SKCanvas canvas, float x, float y)
+               {
+                       var matrix = SKMatrix.MakeTranslation (x, y);
+                       Draw (canvas, ref matrix);
+               }
+
+               // do not unref as this is a plain pointer return, not a reference counted pointer
+               public SKPicture Snapshot () =>
+                       GetObject<SKPicture> (SkiaApi.sk_drawable_new_picture_snapshot (Handle), unrefExisting: false);
+
+               public void NotifyDrawingChanged () =>
+                       SkiaApi.sk_drawable_notify_drawing_changed (Handle);
+
+               protected virtual void OnDraw (SKCanvas canvas)
+               {
+               }
+
+               protected virtual SKRect OnGetBounds () => SKRect.Empty;
+
+               protected virtual SKPicture OnSnapshot ()
+               {
+                       using (var recorder = new SKPictureRecorder ()) {
+                               var canvas = recorder.BeginRecording (Bounds);
+                               Draw (canvas, 0, 0);
+                               return recorder.EndRecording ();
+                       }
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedDrawableDrawProxyDelegate))]
+               private static void DrawInternal (IntPtr d, void* context, IntPtr canvas)
+               {
+                       var drawable = DelegateProxies.GetUserData<SKDrawable> ((IntPtr)context, out _);
+                       drawable.OnDraw (GetObject<SKCanvas> (canvas, false));
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedDrawableGetBoundsProxyDelegate))]
+               private static void GetBoundsInternal (IntPtr d, void* context, SKRect* rect)
+               {
+                       var drawable = DelegateProxies.GetUserData<SKDrawable> ((IntPtr)context, out _);
+                       var bounds = drawable.OnGetBounds ();
+                       *rect = bounds;
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedDrawableNewPictureSnapshotProxyDelegate))]
+               private static IntPtr NewPictureSnapshotInternal (IntPtr d, void* context)
+               {
+                       var drawable = DelegateProxies.GetUserData<SKDrawable> ((IntPtr)context, out _);
+                       return drawable.OnSnapshot ()?.Handle ?? IntPtr.Zero;
+               }
+
+               [MonoPInvokeCallback (typeof (SKManagedDrawableDestroyProxyDelegate))]
+               private static void DestroyInternal (IntPtr d, void* context)
+               {
+                       var drawable = DelegateProxies.GetUserData<SKDrawable> ((IntPtr)context, out var gch);
+                       if (drawable != null) {
+                               Interlocked.Exchange (ref drawable.fromNative, 1);
+                               drawable.Dispose ();
+                       }
+                       gch.Free ();
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKFontManager.cs b/src/XSF/SkiaSharp/SKFontManager.cs
new file mode 100644 (file)
index 0000000..ba286ad
--- /dev/null
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace SkiaSharp
+{
+       public unsafe class SKFontManager : SKObject, ISKReferenceCounted
+       {
+               private static readonly Lazy<SKFontManager> defaultManager;
+
+               static SKFontManager()
+               {
+                       defaultManager = new Lazy<SKFontManager> (() => new SKFontManagerStatic (SkiaApi.sk_fontmgr_ref_default ()));
+               }
+
+               internal static void EnsureStaticInstanceAreInitialized ()
+               {
+                       // IMPORTANT: do not remove to ensure that the static instances
+                       //            are initialized before any access is made to them
+               }
+
+               [Preserve]
+               internal SKFontManager (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static SKFontManager Default => defaultManager.Value;
+
+               public int FontFamilyCount => SkiaApi.sk_fontmgr_count_families (Handle);
+
+               public IEnumerable<string> FontFamilies {
+                       get {
+                               var count = FontFamilyCount;
+                               for (var i = 0; i < count; i++) {
+                                       yield return GetFamilyName (i);
+                               }
+                       }
+               }
+
+               public string GetFamilyName (int index)
+               {
+                       using (var str = new SKString ()) {
+                               SkiaApi.sk_fontmgr_get_family_name (Handle, index, str.Handle);
+                               return (string)str;
+                       }
+               }
+
+               public string[] GetFontFamilies () => FontFamilies.ToArray ();
+
+               public SKFontStyleSet GetFontStyles (int index)
+               {
+                       return GetObject<SKFontStyleSet> (SkiaApi.sk_fontmgr_create_styleset (Handle, index));
+               }
+
+               public SKFontStyleSet GetFontStyles (string familyName)
+               {
+                       return GetObject<SKFontStyleSet> (SkiaApi.sk_fontmgr_match_family (Handle, familyName));
+               }
+
+               public SKTypeface MatchFamily (string familyName, SKFontStyle style)
+               {
+                       if (style == null)
+                               throw new ArgumentNullException (nameof (style));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_match_family_style (Handle, familyName, style.Handle));
+               }
+
+               public SKTypeface MatchTypeface (SKTypeface face, SKFontStyle style)
+               {
+                       if (face == null)
+                               throw new ArgumentNullException (nameof (face));
+                       if (style == null)
+                               throw new ArgumentNullException (nameof (style));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_match_face_style (Handle, face.Handle, style.Handle));
+               }
+
+               public SKTypeface CreateTypeface (string path, int index = 0)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
+                       fixed (byte* u = utf8path) {
+                               return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_create_from_file (Handle, u, index));
+                       }
+               }
+
+               public SKTypeface CreateTypeface (Stream stream, int index = 0)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return CreateTypeface (new SKManagedStream (stream, true), index);
+               }
+
+               public SKTypeface CreateTypeface (SKStreamAsset stream, int index = 0)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       if (stream is SKManagedStream managed) {
+                               stream = managed.ToMemoryStream ();
+                               managed.Dispose ();
+                       }
+
+                       var typeface = GetObject<SKTypeface> (SkiaApi.sk_fontmgr_create_from_stream (Handle, stream.Handle, index));
+                       stream.RevokeOwnership (typeface);
+                       return typeface;
+               }
+
+               public SKTypeface CreateTypeface (SKData data, int index = 0)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_create_from_data (Handle, data.Handle, index));
+               }
+
+               public SKTypeface MatchCharacter (char character)
+               {
+                       return MatchCharacter (null, SKFontStyle.Normal, null, character);
+               }
+
+               public SKTypeface MatchCharacter (int character)
+               {
+                       return MatchCharacter (null, SKFontStyle.Normal, null, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, char character)
+               {
+                       return MatchCharacter (familyName, SKFontStyle.Normal, null, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, int character)
+               {
+                       return MatchCharacter (familyName, SKFontStyle.Normal, null, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, string[] bcp47, char character)
+               {
+                       return MatchCharacter (familyName, SKFontStyle.Normal, bcp47, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, string[] bcp47, int character)
+               {
+                       return MatchCharacter (familyName, SKFontStyle.Normal, bcp47, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant, string[] bcp47, char character)
+               {
+                       return MatchCharacter (familyName, new SKFontStyle (weight, width, slant), bcp47, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant, string[] bcp47, int character)
+               {
+                       return MatchCharacter (familyName, new SKFontStyle (weight, width, slant), bcp47, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, int weight, int width, SKFontStyleSlant slant, string[] bcp47, int character)
+               {
+                       return MatchCharacter (familyName, new SKFontStyle (weight, width, slant), bcp47, character);
+               }
+
+               public SKTypeface MatchCharacter (string familyName, SKFontStyle style, string[] bcp47, int character)
+               {
+                       if (style == null)
+                               throw new ArgumentNullException (nameof (style));
+
+                       // TODO: work around for https://bugs.chromium.org/p/skia/issues/detail?id=6196
+                       if (familyName == null)
+                               familyName = string.Empty;
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_match_family_style_character (Handle, familyName, style.Handle, bcp47, bcp47?.Length ?? 0, character));
+               }
+
+               public static SKFontManager CreateDefault ()
+               {
+                       return GetObject<SKFontManager> (SkiaApi.sk_fontmgr_create_default ());
+               }
+
+               private sealed class SKFontManagerStatic : SKFontManager
+               {
+                       internal SKFontManagerStatic (IntPtr x)
+                               : base (x, false)
+                       {
+                               IgnorePublicDispose = true;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKFontStyle.cs b/src/XSF/SkiaSharp/SKFontStyle.cs
new file mode 100644 (file)
index 0000000..d4b278a
--- /dev/null
@@ -0,0 +1,75 @@
+using System;
+
+namespace SkiaSharp
+{
+       public class SKFontStyle : SKObject
+       {
+               private static readonly Lazy<SKFontStyle> normal;
+               private static readonly Lazy<SKFontStyle> bold;
+               private static readonly Lazy<SKFontStyle> italic;
+               private static readonly Lazy<SKFontStyle> boldItalic;
+
+               static SKFontStyle()
+               {
+                       normal = new Lazy<SKFontStyle> (() => new SKFontStyleStatic (SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright));
+                       bold = new Lazy<SKFontStyle> (() => new SKFontStyleStatic (SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright));
+                       italic = new Lazy<SKFontStyle> (() => new SKFontStyleStatic (SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Italic));
+                       boldItalic = new Lazy<SKFontStyle> (() => new SKFontStyleStatic (SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Italic));
+               }
+
+               [Preserve]
+               internal SKFontStyle (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKFontStyle ()
+                       : this (SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright)
+               {
+               }
+
+               public SKFontStyle (SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant)
+                       : this ((int)weight, (int)width, slant)
+               {
+               }
+
+               public SKFontStyle (int weight, int width, SKFontStyleSlant slant)
+                       : this (SkiaApi.sk_fontstyle_new (weight, width, slant), true)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_fontstyle_delete (Handle);
+
+               public int Weight => SkiaApi.sk_fontstyle_get_weight (Handle);
+
+               public int Width => SkiaApi.sk_fontstyle_get_width (Handle);
+
+               public SKFontStyleSlant Slant => SkiaApi.sk_fontstyle_get_slant (Handle);
+
+               public static SKFontStyle Normal => normal.Value;
+
+               public static SKFontStyle Bold => bold.Value;
+
+               public static SKFontStyle Italic => italic.Value;
+
+               public static SKFontStyle BoldItalic => boldItalic.Value;
+
+               private sealed class SKFontStyleStatic : SKFontStyle
+               {
+                       internal SKFontStyleStatic (SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant)
+                               : base (weight, width, slant)
+                       {
+                               IgnorePublicDispose = true;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKFontStyleSet.cs b/src/XSF/SkiaSharp/SKFontStyleSet.cs
new file mode 100644 (file)
index 0000000..c666328
--- /dev/null
@@ -0,0 +1,70 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace SkiaSharp
+{
+       public class SKFontStyleSet : SKObject, ISKReferenceCounted, IEnumerable<SKFontStyle>, IReadOnlyCollection<SKFontStyle>, IReadOnlyList<SKFontStyle>
+       {
+               [Preserve]
+               internal SKFontStyleSet (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKFontStyleSet ()
+                       : this (SkiaApi.sk_fontstyleset_create_empty (), true)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public int Count => SkiaApi.sk_fontstyleset_get_count (Handle);
+
+               public SKFontStyle this[int index] => GetStyle (index);
+
+               public string GetStyleName (int index)
+               {
+                       using (var str = new SKString ()) {
+                               SkiaApi.sk_fontstyleset_get_style (Handle, index, IntPtr.Zero, str.Handle);
+                               return (string)str;
+                       }
+               }
+
+               public SKTypeface CreateTypeface (int index)
+               {
+                       if (index < 0 || index >= Count)
+                               throw new ArgumentOutOfRangeException ($"Index was out of range. Must be non-negative and less than the size of the set.", nameof (index));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontstyleset_create_typeface (Handle, index));
+               }
+
+               public SKTypeface CreateTypeface (SKFontStyle style)
+               {
+                       if (style == null)
+                               throw new ArgumentNullException (nameof (style));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_fontstyleset_match_style (Handle, style.Handle));
+               }
+
+               public IEnumerator<SKFontStyle> GetEnumerator () => GetStyles ().GetEnumerator ();
+
+               IEnumerator IEnumerable.GetEnumerator () => GetStyles ().GetEnumerator ();
+
+               private IEnumerable<SKFontStyle> GetStyles ()
+               {
+                       var count = Count;
+                       for (var i = 0; i < count; i++) {
+                               yield return GetStyle (i);
+                       }
+               }
+
+               private SKFontStyle GetStyle (int index)
+               {
+                       var fontStyle = new SKFontStyle ();
+                       SkiaApi.sk_fontstyleset_get_style (Handle, index, fontStyle.Handle, IntPtr.Zero);
+                       return fontStyle;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKFrontBufferedManagedStream.cs b/src/XSF/SkiaSharp/SKFrontBufferedManagedStream.cs
new file mode 100644 (file)
index 0000000..f39c54e
--- /dev/null
@@ -0,0 +1,187 @@
+using System;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public class SKFrontBufferedManagedStream : SKAbstractManagedStream
+       {
+               private SKStream stream;
+               private bool disposeStream;
+
+               private readonly bool hasLength;
+               private readonly int streamLength;
+               private readonly int bufferLength;
+               private byte[] frontBuffer;
+               private int bufferedSoFar;
+               private int offset;
+
+               public SKFrontBufferedManagedStream (Stream managedStream, int bufferSize)
+                       : this (managedStream, bufferSize, false)
+               {
+               }
+
+               public SKFrontBufferedManagedStream (Stream managedStream, int bufferSize, bool disposeUnderlyingStream)
+                       : this (new SKManagedStream (managedStream, disposeUnderlyingStream), bufferSize, true)
+               {
+               }
+
+               public SKFrontBufferedManagedStream (SKStream nativeStream, int bufferSize)
+                       : this (nativeStream, bufferSize, false)
+               {
+               }
+
+               public SKFrontBufferedManagedStream (SKStream nativeStream, int bufferSize, bool disposeUnderlyingStream)
+               {
+                       var length = nativeStream.HasLength ? nativeStream.Length : 0;
+                       var position = nativeStream.HasPosition ? nativeStream.Position : 0;
+
+                       disposeStream = disposeUnderlyingStream;
+                       stream = nativeStream;
+                       hasLength = nativeStream.HasPosition && nativeStream.HasLength;
+                       streamLength = length - position;
+                       offset = 0;
+                       bufferedSoFar = 0;
+                       bufferLength = bufferSize;
+                       frontBuffer = new byte[bufferSize];
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeManaged ()
+               {
+                       if (disposeStream && stream != null) {
+                               stream.Dispose ();
+                               stream = null;
+                       }
+
+                       base.DisposeManaged ();
+               }
+
+               protected override IntPtr OnRead (IntPtr buffer, IntPtr size)
+               {
+                       var start = offset;
+
+                       // 1. read any data that was previously buffered
+                       if ((int)size > 0 && offset < bufferedSoFar)
+                       {
+                               var copied = Math.Min ((int)size, bufferedSoFar - offset);
+
+                               if (buffer != IntPtr.Zero)
+                               {
+                                       Marshal.Copy (frontBuffer, offset, buffer, copied);
+                                       buffer += copied;
+                               }
+
+                               offset += copied;
+                               size -= copied;
+                       }
+
+                       bool isAtEnd = false;
+
+                       // 2. buffer any more data, and then read it
+                       if ((int)size > 0 && bufferedSoFar < bufferLength)
+                       {
+                               var bytesToBuffer = Math.Min ((int)size, bufferLength - bufferedSoFar);
+
+                               var tempBuffer = Marshal.AllocCoTaskMem (bytesToBuffer);
+                               var bytesRead = stream.Read (tempBuffer, bytesToBuffer);
+                               Marshal.Copy (tempBuffer, frontBuffer, offset, bytesRead);
+                               Marshal.FreeCoTaskMem (tempBuffer);
+
+                               isAtEnd = bytesRead < bytesToBuffer;
+
+                               bufferedSoFar += bytesRead;
+                               if (buffer != IntPtr.Zero)
+                               {
+                                       Marshal.Copy (frontBuffer, offset, buffer, bytesRead);
+                                       buffer += bytesRead;
+                               }
+
+                               offset += bytesRead;
+                               size -= bytesRead;
+                       }
+
+                       // 3. read the rest directly
+                       if ((int)size > 0 && !isAtEnd)
+                       {
+                               var bytesRead = stream.Read (buffer, (int)size);
+
+                               // past the buffer, so dispose it
+                               if (bytesRead > 0)
+                               {
+                                       frontBuffer = null;
+                               }
+
+                               offset += bytesRead;
+                               size -= bytesRead;
+                       }
+
+                       return (IntPtr)(offset - start);
+               }
+
+               protected override IntPtr OnPeek (IntPtr buffer, IntPtr size)
+               {
+                       if (offset >= bufferLength)
+                       {
+                               // this stream is not able to buffer.
+                               return (IntPtr)0;
+                       }
+
+                       // keep track of the offset so we can return to it.
+                       var start = offset;
+
+                       var bytesToCopy = Math.Min ((int)size, bufferLength - offset);
+                       var bytesRead = Read (buffer, bytesToCopy);
+
+                       // return to the original position
+                       offset = start;
+
+                       return (IntPtr)bytesRead;
+               }
+
+               protected override bool OnIsAtEnd ()
+               {
+                       if (offset < bufferedSoFar)
+                       {
+                               // even if the underlying stream is at the end, this stream has been
+                               // rewound after buffering, so it is not at the end.
+                               return false;
+                       }
+
+                       return stream.IsAtEnd;
+               }
+
+               protected override bool OnRewind ()
+               {
+                       // only allow a rewind if we have not exceeded the buffer.
+                       if (offset <= bufferLength)
+                       {
+                               offset = 0;
+                               return true;
+                       }
+
+                       return false;
+               }
+
+               protected override bool OnHasLength () => hasLength;
+
+               protected override IntPtr OnGetLength () => (IntPtr)streamLength;
+
+               // seeking is not supported
+               protected override bool OnHasPosition () => false;
+
+               // seeking is not supported
+               protected override IntPtr OnGetPosition () => (IntPtr)0;
+
+               // seeking is not supported
+               protected override bool OnSeek (IntPtr position) => false;
+
+               // seeking is not supported
+               protected override bool OnMove (int offset) => false;
+
+               // duplicating or forking is not supported
+               protected override IntPtr OnCreateNew () => IntPtr.Zero;
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKFrontBufferedStream.cs b/src/XSF/SkiaSharp/SKFrontBufferedStream.cs
new file mode 100644 (file)
index 0000000..3d913f0
--- /dev/null
@@ -0,0 +1,213 @@
+using System;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public class SKFrontBufferedStream : Stream
+       {
+               public const int DefaultBufferSize = 4096;
+
+               private readonly long totalBufferSize;
+               private readonly long totalLength;
+               private readonly bool disposeStream;
+               private Stream underlyingStream;
+               
+               private long currentOffset;
+               private long bufferedSoFar;
+               private byte[] internalBuffer;
+
+               public SKFrontBufferedStream(Stream stream)
+                       : this(stream, DefaultBufferSize, false)
+               {
+               }
+
+               public SKFrontBufferedStream(Stream stream, long bufferSize)
+                       : this(stream, bufferSize, false)
+               {
+               }
+
+               public SKFrontBufferedStream(Stream stream, bool disposeUnderlyingStream)
+                       : this(stream, DefaultBufferSize, disposeUnderlyingStream)
+               {
+               }
+
+               public SKFrontBufferedStream(Stream stream, long bufferSize, bool disposeUnderlyingStream)
+               {
+                       underlyingStream = stream;
+                       totalBufferSize = bufferSize;
+                       totalLength = stream.CanSeek ? stream.Length : -1;
+                       disposeStream = disposeUnderlyingStream;
+               }
+
+               public override bool CanRead => true;
+
+               public override bool CanSeek => true; // we can seek if we are in the buffer
+
+               public override bool CanWrite => false; // we don't write
+
+               public override long Length => totalLength;
+
+               public override long Position
+               {
+                       get { return currentOffset; }
+                       set { Seek(value, SeekOrigin.Begin); }
+               }
+
+               public override void Flush()
+               {
+                       // we don't write
+               }
+
+               public override int Read(byte[] buffer, int offset, int count)
+               {
+                       var start = currentOffset;
+
+                       if (internalBuffer == null && currentOffset < totalBufferSize)
+                       {
+                               // create the buffer now, since we are going to be writing to it
+                               internalBuffer = new byte[totalBufferSize];
+                       }
+
+                       // try read any data from the buffer
+                       if (currentOffset < bufferedSoFar)
+                       {
+                               var bytesCopied = ReadFromBuffer(buffer, offset, count);
+                               count -= bytesCopied;
+                               offset += bytesCopied;
+                       }
+
+                       // read from the stream and buffer that
+                       if (count > 0 && bufferedSoFar < totalBufferSize)
+                       {
+                               var buffered = BufferAndWriteTo(buffer, offset, count);
+                               count -= buffered;
+                               offset += buffered;
+                       }
+
+                       // just read from the stream
+                       if (count > 0)
+                       {
+                               var direct = ReadDirectlyFromStream(buffer, offset, count);
+                               count -= direct;
+                               offset += direct;
+
+                               if (direct > 0)
+                               {
+                                       // if we are here, we are past the buffer and can't go back
+                                       internalBuffer = null;
+                               }
+                       }
+
+                       return (int)(currentOffset - start);
+               }
+
+               public override long Seek(long offset, SeekOrigin origin)
+               {
+                       // we are outside the buffer, so throw
+                       if (currentOffset > totalBufferSize)
+                       {
+                               throw new InvalidOperationException("The position cannot be changed once the stream has moved past the buffer.");
+                       }
+
+                       // find the absolute position
+                       var absolute = offset;
+                       if (origin == SeekOrigin.Current)
+                       {
+                               absolute = Position + offset;
+                       }
+                       else if (origin == SeekOrigin.End)
+                       {
+                               if (Length == -1)
+                               {
+                                       throw new InvalidOperationException("Can't seek from end as the underlying stream is not seekable.");
+                               }
+                               absolute = Length + offset;
+                       }
+
+                       // move to the position
+                       if (absolute <= currentOffset)
+                       {
+                               // we are moving back, so just move the local cursor
+                               currentOffset = absolute;
+                       }
+                       else
+                       {
+                               // we are moving forward, so we have to read into the buffer
+                               var toMove = absolute - currentOffset;
+                               currentOffset += Read(null, 0, (int)toMove);
+                       }
+
+                       return Position;
+               }
+
+               public override void SetLength(long value)
+               {
+                       // we don't write
+               }
+
+               public override void Write(byte[] buffer, int offset, int count)
+               {
+                       // we don't write
+               }
+
+               private int ReadFromBuffer(byte[] dst, int offset, int size)
+               {
+                       var bytesToCopy = Math.Min(size, (int)(bufferedSoFar - currentOffset));
+
+                       if (dst != null && offset < dst.Length)
+                       {
+                               Buffer.BlockCopy(internalBuffer, (int)currentOffset, dst, offset, bytesToCopy);
+                       }
+
+                       currentOffset += bytesToCopy;
+
+                       return bytesToCopy;
+               }
+
+               private int BufferAndWriteTo(byte[] dst, int offset, int size)
+               {
+                       var bytesToBuffer = Math.Min(size, (int)(totalBufferSize - bufferedSoFar));
+                       var buffered = underlyingStream.Read(internalBuffer, (int)currentOffset, bytesToBuffer);
+
+                       if (dst != null && offset < dst.Length)
+                       {
+                               Buffer.BlockCopy(internalBuffer, (int)currentOffset, dst, offset, buffered);
+                       }
+
+                       bufferedSoFar += buffered;
+                       currentOffset = bufferedSoFar;
+
+                       return buffered;
+               }
+
+               private int ReadDirectlyFromStream(byte[] dst, int offset, int size)
+               {
+                       long bytesReadDirectly = 0;
+
+                       if (dst == null)
+                       {
+                               bytesReadDirectly = underlyingStream.Seek(size, SeekOrigin.Current);
+                       }
+                       else
+                       {
+                               bytesReadDirectly = underlyingStream.Read(dst, offset, size);
+                       }
+
+                       currentOffset += bytesReadDirectly;
+
+                       return (int)bytesReadDirectly;
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       base.Dispose(disposing);
+
+                       internalBuffer = null;
+                       if (disposeStream && underlyingStream != null)
+                       {
+                               underlyingStream.Dispose();
+                       }
+                       underlyingStream = null;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKImage.cs b/src/XSF/SkiaSharp/SKImage.cs
new file mode 100644 (file)
index 0000000..891de2c
--- /dev/null
@@ -0,0 +1,654 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       // TODO: `MakeCrossContextFromEncoded`
+       // TODO: `MakeFromYUVTexturesCopy` and `MakeFromNV12TexturesCopy`
+       // TODO: `FromPicture` with bit depth and color space
+       // TODO: `GetTextureHandle`
+       // TODO: `MakeColorSpace`
+
+       public unsafe class SKImage : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal SKImage (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // create brand new image
+
+               public static SKImage Create (SKImageInfo info)
+               {
+                       var pixels = Marshal.AllocCoTaskMem (info.BytesSize);
+                       using (var pixmap = new SKPixmap (info, pixels)) {
+                               // don't use the managed version as that is just extra overhead which isn't necessary
+                               return GetObject<SKImage> (SkiaApi.sk_image_new_raster (pixmap.Handle, DelegateProxies.SKImageRasterReleaseDelegateProxyForCoTaskMem, null));
+                       }
+               }
+
+               // create a new image from a copy of pixel data
+
+               public static SKImage FromPixelCopy (SKImageInfo info, SKStream pixels) =>
+                       FromPixelCopy (info, pixels, info.RowBytes);
+
+               public static SKImage FromPixelCopy (SKImageInfo info, SKStream pixels, int rowBytes)
+               {
+                       if (pixels == null)
+                               throw new ArgumentNullException (nameof (pixels));
+                       using (var data = SKData.Create (pixels)) {
+                               return FromPixels (info, data, rowBytes);
+                       }
+               }
+
+               public static SKImage FromPixelCopy (SKImageInfo info, Stream pixels) =>
+                       FromPixelCopy (info, pixels, info.RowBytes);
+
+               public static SKImage FromPixelCopy (SKImageInfo info, Stream pixels, int rowBytes)
+               {
+                       if (pixels == null)
+                               throw new ArgumentNullException (nameof (pixels));
+                       using (var data = SKData.Create (pixels)) {
+                               return FromPixels (info, data, rowBytes);
+                       }
+               }
+
+               public static SKImage FromPixelCopy (SKImageInfo info, byte[] pixels) =>
+                       FromPixelCopy (info, pixels, info.RowBytes);
+
+               public static SKImage FromPixelCopy (SKImageInfo info, byte[] pixels, int rowBytes)
+               {
+                       if (pixels == null)
+                               throw new ArgumentNullException (nameof (pixels));
+                       using (var data = SKData.CreateCopy (pixels)) {
+                               return FromPixels (info, data, rowBytes);
+                       }
+               }
+
+               public static SKImage FromPixelCopy (SKImageInfo info, IntPtr pixels) =>
+                       FromPixelCopy (info, pixels, info.RowBytes);
+
+               public static SKImage FromPixelCopy (SKImageInfo info, IntPtr pixels, int rowBytes)
+               {
+                       if (pixels == IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (pixels));
+
+                       var nInfo = SKImageInfoNative.FromManaged (ref info);
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_raster_copy (&nInfo, (void*)pixels, (IntPtr)rowBytes));
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use FromPixelCopy(SKImageInfo, IntPtr, int) instead.")]
+               public static SKImage FromPixelCopy (SKImageInfo info, IntPtr pixels, int rowBytes, SKColorTable ctable) =>
+                       FromPixelCopy (info, pixels, rowBytes);
+
+               public static SKImage FromPixelCopy (SKPixmap pixmap)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_raster_copy_with_pixmap (pixmap.Handle));
+               }
+
+               public static SKImage FromPixelCopy (SKImageInfo info, ReadOnlySpan<byte> pixels) =>
+                       FromPixelCopy (info, pixels, info.RowBytes);
+
+               public static SKImage FromPixelCopy (SKImageInfo info, ReadOnlySpan<byte> pixels, int rowBytes)
+               {
+                       if (pixels == null)
+                               throw new ArgumentNullException (nameof (pixels));
+                       using (var data = SKData.CreateCopy (pixels)) {
+                               return FromPixels (info, data, rowBytes);
+                       }
+               }
+
+               // create a new image around existing pixel data
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromPixels (SKImageInfo, SKData, int) instead.")]
+               public static SKImage FromPixelData (SKImageInfo info, SKData data, int rowBytes)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_raster_data (&cinfo, data.Handle, (IntPtr)rowBytes));
+               }
+
+               public static SKImage FromPixels (SKImageInfo info, SKData data) =>
+                       FromPixels (info, data, info.RowBytes);
+
+               public static SKImage FromPixels (SKImageInfo info, SKData data, int rowBytes)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_raster_data (&cinfo, data.Handle, (IntPtr)rowBytes));
+               }
+
+               public static SKImage FromPixels (SKImageInfo info, IntPtr pixels)
+               {
+                       using (var pixmap = new SKPixmap (info, pixels, info.RowBytes)) {
+                               return FromPixels (pixmap, null, null);
+                       }
+               }
+
+               public static SKImage FromPixels (SKImageInfo info, IntPtr pixels, int rowBytes)
+               {
+                       using (var pixmap = new SKPixmap (info, pixels, rowBytes)) {
+                               return FromPixels (pixmap, null, null);
+                       }
+               }
+
+               public static SKImage FromPixels (SKPixmap pixmap)
+               {
+                       return FromPixels (pixmap, null, null);
+               }
+
+               public static SKImage FromPixels (SKPixmap pixmap, SKImageRasterReleaseDelegate releaseProc)
+               {
+                       return FromPixels (pixmap, releaseProc, null);
+               }
+
+               public static SKImage FromPixels (SKPixmap pixmap, SKImageRasterReleaseDelegate releaseProc, object releaseContext)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+
+                       var del = releaseProc != null && releaseContext != null
+                               ? new SKImageRasterReleaseDelegate ((addr, _) => releaseProc (addr, releaseContext))
+                               : releaseProc;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.SKImageRasterReleaseDelegateProxy, out _, out var ctx);
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_raster (pixmap.Handle, proxy, (void*)ctx));
+               }
+
+               // create a new image from encoded data
+
+               public static SKImage FromEncodedData (SKData data, SKRectI subset)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       var handle = SkiaApi.sk_image_new_from_encoded (data.Handle, &subset);
+                       return GetObject<SKImage> (handle);
+               }
+
+               public static SKImage FromEncodedData (SKData data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       var handle = SkiaApi.sk_image_new_from_encoded (data.Handle, null);
+                       return GetObject<SKImage> (handle);
+               }
+
+               public static SKImage FromEncodedData (ReadOnlySpan<byte> data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+                       if (data.Length == 0)
+                               throw new ArgumentException ("The data buffer was empty.");
+
+                       using (var skdata = SKData.CreateCopy (data)) {
+                               return FromEncodedData (skdata);
+                       }
+               }
+
+               public static SKImage FromEncodedData (byte[] data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+                       if (data.Length == 0)
+                               throw new ArgumentException ("The data buffer was empty.");
+
+                       using (var skdata = SKData.CreateCopy (data)) {
+                               return FromEncodedData (skdata);
+                       }
+               }
+
+               public static SKImage FromEncodedData (SKStream data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       using (var skdata = SKData.Create (data)) {
+                               if (skdata == null)
+                                       return null;
+                               return FromEncodedData (skdata);
+                       }
+               }
+
+               public static SKImage FromEncodedData (Stream data)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       using (var skdata = SKData.Create (data)) {
+                               if (skdata == null)
+                                       return null;
+                               return FromEncodedData (skdata);
+                       }
+               }
+
+               public static SKImage FromEncodedData (string filename)
+               {
+                       if (filename == null)
+                               throw new ArgumentNullException (nameof (filename));
+
+                       using (var skdata = SKData.Create (filename)) {
+                               if (skdata == null)
+                                       return null;
+                               return FromEncodedData (skdata);
+                       }
+               }
+
+               // create a new image from a bitmap
+
+               public static SKImage FromBitmap (SKBitmap bitmap)
+               {
+                       if (bitmap == null)
+                               throw new ArgumentNullException (nameof (bitmap));
+
+                       var image = GetObject<SKImage> (SkiaApi.sk_image_new_from_bitmap (bitmap.Handle));
+                       GC.KeepAlive (bitmap);
+                       return image;
+               }
+
+               // create a new image from a GPU texture
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType) instead.")]
+               public static SKImage FromTexture (GRContext context, GRBackendTextureDesc desc)
+               {
+                       return FromTexture (context, desc, SKAlphaType.Premul, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType) instead.")]
+               public static SKImage FromTexture (GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha)
+               {
+                       return FromTexture (context, desc, alpha, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType, SKColorSpace, SKImageTextureReleaseDelegate) instead.")]
+               public static SKImage FromTexture (GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc)
+               {
+                       return FromTexture (context, desc, alpha, releaseProc, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType, SKColorSpace, SKImageTextureReleaseDelegate, object) instead.")]
+               public static SKImage FromTexture (GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+
+                       var texture = new GRBackendTexture (desc);
+                       return FromTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null, releaseProc, releaseContext);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType) instead.")]
+               public static SKImage FromTexture (GRContext context, GRGlBackendTextureDesc desc)
+               {
+                       return FromTexture (context, desc, SKAlphaType.Premul, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType) instead.")]
+               public static SKImage FromTexture (GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha)
+               {
+                       return FromTexture (context, desc, alpha, null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType, SKColorSpace, SKImageTextureReleaseDelegate) instead.")]
+               public static SKImage FromTexture (GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc)
+               {
+                       return FromTexture (context, desc, alpha, releaseProc, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType, SKColorSpace, SKImageTextureReleaseDelegate, object) instead.")]
+               public static SKImage FromTexture (GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
+               {
+                       var texture = new GRBackendTexture (desc);
+                       return FromTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null, releaseProc, releaseContext);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, SKColorType colorType)
+               {
+                       return FromTexture (context, texture, GRSurfaceOrigin.BottomLeft, colorType, SKAlphaType.Premul, null, null, null);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType)
+               {
+                       return FromTexture (context, texture, origin, colorType, SKAlphaType.Premul, null, null, null);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha)
+               {
+                       return FromTexture (context, texture, origin, colorType, alpha, null, null, null);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, SKColorSpace colorspace)
+               {
+                       return FromTexture (context, texture, origin, colorType, alpha, colorspace, null, null);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, SKColorSpace colorspace, SKImageTextureReleaseDelegate releaseProc)
+               {
+                       return FromTexture (context, texture, origin, colorType, alpha, colorspace, releaseProc, null);
+               }
+
+               public static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, SKColorSpace colorspace, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+                       if (texture == null)
+                               throw new ArgumentNullException (nameof (texture));
+
+                       var cs = colorspace == null ? IntPtr.Zero : colorspace.Handle;
+                       var del = releaseProc != null && releaseContext != null
+                               ? new SKImageTextureReleaseDelegate ((_) => releaseProc (releaseContext))
+                               : releaseProc;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.SKImageTextureReleaseDelegateProxy, out _, out var ctx);
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_from_texture (context.Handle, texture.Handle, origin, colorType, alpha, cs, proxy, (void*)ctx));
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromAdoptedTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType) instead.")]
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTextureDesc desc)
+               {
+                       return FromAdoptedTexture (context, desc, SKAlphaType.Premul);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromAdoptedTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType) instead.")]
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha)
+               {
+                       var texture = new GRBackendTexture (desc);
+                       return FromAdoptedTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromAdoptedTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType) instead.")]
+               public static SKImage FromAdoptedTexture (GRContext context, GRGlBackendTextureDesc desc)
+               {
+                       return FromAdoptedTexture (context, desc, SKAlphaType.Premul);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromAdoptedTexture(GRContext, GRBackendTexture, GRSurfaceOrigin, SKColorType, SKAlphaType) instead.")]
+               public static SKImage FromAdoptedTexture (GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha)
+               {
+                       var texture = new GRBackendTexture (desc);
+                       return FromAdoptedTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null);
+               }
+
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTexture texture, SKColorType colorType)
+               {
+                       return FromAdoptedTexture (context, texture, GRSurfaceOrigin.BottomLeft, colorType, SKAlphaType.Premul, null);
+               }
+
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType)
+               {
+                       return FromAdoptedTexture (context, texture, origin, colorType, SKAlphaType.Premul, null);
+               }
+
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha)
+               {
+                       return FromAdoptedTexture (context, texture, origin, colorType, alpha, null);
+               }
+
+               public static SKImage FromAdoptedTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, SKColorSpace colorspace)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+                       if (texture == null)
+                               throw new ArgumentNullException (nameof (texture));
+
+                       var cs = colorspace == null ? IntPtr.Zero : colorspace.Handle;
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_from_adopted_texture (context.Handle, texture.Handle, origin, colorType, alpha, cs));
+               }
+
+               // create a new image from a picture
+
+               public static SKImage FromPicture (SKPicture picture, SKSizeI dimensions) =>
+                       FromPicture (picture, dimensions, null, null);
+
+               public static SKImage FromPicture (SKPicture picture, SKSizeI dimensions, SKMatrix matrix) =>
+                       FromPicture (picture, dimensions, &matrix, null);
+
+               public static SKImage FromPicture (SKPicture picture, SKSizeI dimensions, SKPaint paint) =>
+                       FromPicture (picture, dimensions, null, paint);
+
+               public static SKImage FromPicture (SKPicture picture, SKSizeI dimensions, SKMatrix matrix, SKPaint paint) =>
+                       FromPicture (picture, dimensions, &matrix, paint);
+
+               private static SKImage FromPicture (SKPicture picture, SKSizeI dimensions, SKMatrix* matrix, SKPaint paint)
+               {
+                       if (picture == null)
+                               throw new ArgumentNullException (nameof (picture));
+
+                       var p = paint?.Handle ?? IntPtr.Zero;
+                       return GetObject<SKImage> (SkiaApi.sk_image_new_from_picture (picture.Handle, &dimensions, matrix, p));
+               }
+
+               public SKData Encode () =>
+                       GetObject<SKData> (SkiaApi.sk_image_encode (Handle));
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public SKData Encode (SKPixelSerializer serializer)
+               {
+                       if (serializer == null)
+                               throw new ArgumentNullException (nameof (serializer));
+
+                       // try old data
+                       var encoded = EncodedData;
+                       if (encoded != null) {
+                               if (serializer.UseEncodedData (encoded.Data, (ulong)encoded.Size)) {
+                                       return encoded;
+                               } else {
+                                       encoded.Dispose ();
+                                       encoded = null;
+                               }
+                       }
+
+                       // get new data (raster)
+                       if (!IsTextureBacked) {
+                               using (var pixmap = PeekPixels ()) {
+                                       return serializer.Encode (pixmap);
+                               }
+                       }
+
+                       // get new data (texture / gpu)
+                       // this involves a copy from gpu to cpu first
+                       if (IsTextureBacked) {
+                               var info = new SKImageInfo (Width, Height, ColorType, AlphaType, ColorSpace);
+                               using (var temp = new SKBitmap (info))
+                               using (var pixmap = temp.PeekPixels ()) {
+                                       if (pixmap != null && ReadPixels (pixmap, 0, 0)) {
+                                               return serializer.Encode (pixmap);
+                                       }
+                               }
+                       }
+
+                       // some error
+                       return null;
+               }
+
+               public SKData Encode (SKEncodedImageFormat format, int quality)
+               {
+                       return GetObject<SKData> (SkiaApi.sk_image_encode_specific (Handle, format, quality));
+               }
+
+               public int Width =>
+                       SkiaApi.sk_image_get_width (Handle);
+
+               public int Height =>
+                       SkiaApi.sk_image_get_height (Handle);
+
+               public uint UniqueId =>
+                       SkiaApi.sk_image_get_unique_id (Handle);
+
+               public SKAlphaType AlphaType =>
+                       SkiaApi.sk_image_get_alpha_type (Handle);
+
+               public SKColorType ColorType =>
+                       SkiaApi.sk_image_get_color_type (Handle);
+
+               public SKColorSpace ColorSpace =>
+                       GetObject<SKColorSpace> (SkiaApi.sk_image_get_colorspace (Handle));
+
+               public bool IsAlphaOnly =>
+                       SkiaApi.sk_image_is_alpha_only (Handle);
+
+               public SKData EncodedData =>
+                       GetObject<SKData> (SkiaApi.sk_image_ref_encoded (Handle));
+
+               // ToShader
+
+               public SKShader ToShader () =>
+                       ToShader (SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public SKShader ToShader (SKShaderTileMode tileX, SKShaderTileMode tileY) =>
+                       GetObject<SKShader> (SkiaApi.sk_image_make_shader (Handle, tileX, tileY, null));
+
+               public SKShader ToShader (SKShaderTileMode tileX, SKShaderTileMode tileY, SKMatrix localMatrix) =>
+                       GetObject<SKShader> (SkiaApi.sk_image_make_shader (Handle, tileX, tileY, &localMatrix));
+
+               // PeekPixels
+
+               public bool PeekPixels (SKPixmap pixmap)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+
+                       var result = SkiaApi.sk_image_peek_pixels (Handle, pixmap.Handle);
+                       if (result)
+                               pixmap.pixelSource = this;
+                       return result;
+               }
+
+               public SKPixmap PeekPixels ()
+               {
+                       var pixmap = new SKPixmap ();
+                       if (!PeekPixels (pixmap)) {
+                               pixmap.Dispose ();
+                               pixmap = null;
+                       }
+                       return pixmap;
+               }
+
+               public bool IsTextureBacked =>
+                       SkiaApi.sk_image_is_texture_backed (Handle);
+
+               public bool IsLazyGenerated =>
+                       SkiaApi.sk_image_is_lazy_generated (Handle);
+
+               public bool IsValid (GRContext context) =>
+                       SkiaApi.sk_image_is_valid (Handle, context?.Handle ?? IntPtr.Zero);
+
+               // ReadPixels
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels) =>
+                       ReadPixels (dstInfo, dstPixels, dstInfo.RowBytes, 0, 0, SKImageCachingHint.Allow);
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes) =>
+                       ReadPixels (dstInfo, dstPixels, dstRowBytes, 0, 0, SKImageCachingHint.Allow);
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY) =>
+                       ReadPixels (dstInfo, dstPixels, dstRowBytes, srcX, srcY, SKImageCachingHint.Allow);
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY, SKImageCachingHint cachingHint)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref dstInfo);
+                       return SkiaApi.sk_image_read_pixels (Handle, &cinfo, (void*)dstPixels, (IntPtr)dstRowBytes, srcX, srcY, cachingHint);
+               }
+
+               public bool ReadPixels (SKPixmap pixmap) =>
+                       ReadPixels (pixmap, 0, 0, SKImageCachingHint.Allow);
+
+               public bool ReadPixels (SKPixmap pixmap, int srcX, int srcY) =>
+                       ReadPixels (pixmap, srcX, srcY, SKImageCachingHint.Allow);
+
+               public bool ReadPixels (SKPixmap pixmap, int srcX, int srcY, SKImageCachingHint cachingHint)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+
+                       return SkiaApi.sk_image_read_pixels_into_pixmap (Handle, pixmap.Handle, srcX, srcY, cachingHint);
+               }
+
+               // ScalePixels
+
+               public bool ScalePixels (SKPixmap dst, SKFilterQuality quality)
+               {
+                       return ScalePixels (dst, quality, SKImageCachingHint.Allow);
+               }
+
+               public bool ScalePixels (SKPixmap dst, SKFilterQuality quality, SKImageCachingHint cachingHint)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       return SkiaApi.sk_image_scale_pixels (Handle, dst.Handle, quality, cachingHint);
+               }
+
+               // Subset
+
+               public SKImage Subset (SKRectI subset)
+               {
+                       return GetObject<SKImage> (SkiaApi.sk_image_make_subset (Handle, &subset));
+               }
+
+               // ToRasterImage
+
+               public SKImage ToRasterImage () =>
+                       ToRasterImage (false);
+
+               public SKImage ToRasterImage (bool ensurePixelData) =>
+                       ensurePixelData
+                               ? GetObject<SKImage> (SkiaApi.sk_image_make_raster_image (Handle))
+                               : GetObject<SKImage> (SkiaApi.sk_image_make_non_texture_image (Handle));
+
+               // ToTextureImage
+
+               public SKImage ToTextureImage (GRContext context) =>
+                       ToTextureImage (context, null);
+
+               public SKImage ToTextureImage (GRContext context, SKColorSpace colorspace)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+
+                       return GetObject<SKImage> (SkiaApi.sk_image_make_texture_image (Handle, context.Handle, colorspace?.Handle ?? IntPtr.Zero));
+               }
+
+               // ApplyImageFilter
+
+               public SKImage ApplyImageFilter (SKImageFilter filter, SKRectI subset, SKRectI clipBounds, out SKRectI outSubset, out SKPoint outOffset)
+               {
+                       var image = ApplyImageFilter (filter, subset, clipBounds, out outSubset, out SKPointI outOffsetActual);
+                       outOffset = outOffsetActual;
+                       return image;
+               }
+
+               public SKImage ApplyImageFilter (SKImageFilter filter, SKRectI subset, SKRectI clipBounds, out SKRectI outSubset, out SKPointI outOffset)
+               {
+                       if (filter == null)
+                               throw new ArgumentNullException (nameof (filter));
+
+                       fixed (SKRectI* os = &outSubset)
+                       fixed (SKPointI* oo = &outOffset) {
+                               return GetObject<SKImage> (SkiaApi.sk_image_make_with_filter (Handle, filter.Handle, &subset, &clipBounds, os, oo));
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKImageFilter.cs b/src/XSF/SkiaSharp/SKImageFilter.cs
new file mode 100644 (file)
index 0000000..d639070
--- /dev/null
@@ -0,0 +1,306 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       // TODO: `asAColorFilter`
+       // TODO: `countInputs`, `getInput`
+       // TODO: `cropRectIsSet`, `getCropRect`
+       // TODO: `computeFastBounds`, `canComputeFastBounds`
+
+       public unsafe class SKImageFilter : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal SKImageFilter(IntPtr handle, bool owns)
+                       : base(handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // CreateMatrix
+
+               public static SKImageFilter CreateMatrix(SKMatrix matrix, SKFilterQuality quality, SKImageFilter input = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_matrix(&matrix, quality, input == null ? IntPtr.Zero : input.Handle));
+               }
+
+               // CreateAlphaThreshold
+
+               public static SKImageFilter CreateAlphaThreshold(SKRectI region, float innerThreshold, float outerThreshold, SKImageFilter input = null)
+               {
+                       var reg = new SKRegion ();
+                       reg.SetRect (region);
+                       return CreateAlphaThreshold (reg, innerThreshold, outerThreshold, input);
+
+               }
+
+               public static SKImageFilter CreateAlphaThreshold(SKRegion region, float innerThreshold, float outerThreshold, SKImageFilter input = null)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_alpha_threshold(region.Handle, innerThreshold, outerThreshold, input == null ? IntPtr.Zero : input.Handle));
+               }
+
+               // CreateBlur
+
+               public static SKImageFilter CreateBlur(float sigmaX, float sigmaY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_blur(sigmaX, sigmaY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateColorFilter
+
+               public static SKImageFilter CreateColorFilter(SKColorFilter cf, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (cf == null)
+                               throw new ArgumentNullException(nameof(cf));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_color_filter(cf.Handle, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateCompose
+
+               public static SKImageFilter CreateCompose(SKImageFilter outer, SKImageFilter inner)
+               {
+                       if (outer == null)
+                               throw new ArgumentNullException(nameof(outer));
+                       if (inner == null)
+                               throw new ArgumentNullException(nameof(inner));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_compose(outer.Handle, inner.Handle));
+               }
+
+               // CreateDisplacementMapEffect
+
+               public static SKImageFilter CreateDisplacementMapEffect(SKDisplacementMapEffectChannelSelectorType xChannelSelector, SKDisplacementMapEffectChannelSelectorType yChannelSelector, float scale, SKImageFilter displacement, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (displacement == null)
+                               throw new ArgumentNullException(nameof(displacement));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_displacement_map_effect(xChannelSelector, yChannelSelector, scale, displacement.Handle, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateDropShadow
+
+               public static SKImageFilter CreateDropShadow(float dx, float dy, float sigmaX, float sigmaY, SKColor color, SKDropShadowImageFilterShadowMode shadowMode, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_drop_shadow(dx, dy, sigmaX, sigmaY, (uint)color, shadowMode, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // Create*LitDiffuse
+
+               public static SKImageFilter CreateDistantLitDiffuse(SKPoint3 direction, SKColor lightColor, float surfaceScale, float kd, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_distant_lit_diffuse(&direction, (uint)lightColor, surfaceScale, kd, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               public static SKImageFilter CreatePointLitDiffuse(SKPoint3 location, SKColor lightColor, float surfaceScale, float kd, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_point_lit_diffuse(&location, (uint)lightColor, surfaceScale, kd, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               public static SKImageFilter CreateSpotLitDiffuse(SKPoint3 location, SKPoint3 target, float specularExponent, float cutoffAngle, SKColor lightColor, float surfaceScale, float kd, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_spot_lit_diffuse(&location, &target, specularExponent, cutoffAngle, (uint)lightColor, surfaceScale, kd, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // Create*LitSpecular
+
+               public static SKImageFilter CreateDistantLitSpecular(SKPoint3 direction, SKColor lightColor, float surfaceScale, float ks, float shininess, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_distant_lit_specular(&direction, (uint)lightColor, surfaceScale, ks, shininess, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               public static SKImageFilter CreatePointLitSpecular(SKPoint3 location, SKColor lightColor, float surfaceScale, float ks, float shininess, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_point_lit_specular(&location, (uint)lightColor, surfaceScale, ks, shininess, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               public static SKImageFilter CreateSpotLitSpecular(SKPoint3 location, SKPoint3 target, float specularExponent, float cutoffAngle, SKColor lightColor, float surfaceScale, float ks, float shininess, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_spot_lit_specular(&location, &target, specularExponent, cutoffAngle, (uint)lightColor, surfaceScale, ks, shininess, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateMagnifier
+
+               public static SKImageFilter CreateMagnifier(SKRect src, float inset, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_magnifier(&src, inset, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateMatrixConvolution
+
+               public static SKImageFilter CreateMatrixConvolution(SKSizeI kernelSize, float[] kernel, float gain, float bias, SKPointI kernelOffset, SKMatrixConvolutionTileMode tileMode, bool convolveAlpha, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (kernel == null)
+                               throw new ArgumentNullException(nameof(kernel));
+                       if (kernel.Length != kernelSize.Width * kernelSize.Height)
+                               throw new ArgumentException("Kernel length must match the dimensions of the kernel size (Width * Height).", nameof(kernel));
+                       fixed (float* k = kernel) {
+                               return GetObject<SKImageFilter> (SkiaApi.sk_imagefilter_new_matrix_convolution (&kernelSize, k, gain, bias, &kernelOffset, tileMode, convolveAlpha, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+                       }
+               }
+
+               // CreateMerge
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete("Use CreateMerge(SKImageFilter, SKImageFilter, SKImageFilter.CropRect) instead.")]
+               public static SKImageFilter CreateMerge(SKImageFilter first, SKImageFilter second, SKBlendMode mode, SKImageFilter.CropRect cropRect = null)
+               {
+                       return CreateMerge(new [] { first, second }, cropRect);
+               }
+
+               public static SKImageFilter CreateMerge(SKImageFilter first, SKImageFilter second, SKImageFilter.CropRect cropRect = null)
+               {
+                       return CreateMerge(new [] { first, second }, cropRect);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete("Use CreateMerge(SKImageFilter[], SKImageFilter.CropRect) instead.")]
+               public static SKImageFilter CreateMerge(SKImageFilter[] filters, SKBlendMode[] modes, SKImageFilter.CropRect cropRect = null)
+               {
+                       return CreateMerge (filters, cropRect);
+               }
+
+               public static SKImageFilter CreateMerge(SKImageFilter[] filters, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (filters == null)
+                               throw new ArgumentNullException(nameof(filters));
+                       var handles = new IntPtr[filters.Length];
+                       for (int i = 0; i < filters.Length; i++)
+                       {
+                               handles[i] = filters[i]?.Handle ?? IntPtr.Zero;
+                       }
+                       fixed (IntPtr* h = handles) {
+                               return GetObject<SKImageFilter> (SkiaApi.sk_imagefilter_new_merge (h, filters.Length, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+                       }
+               }
+
+               // CreateDilate
+
+               public static SKImageFilter CreateDilate(int radiusX, int radiusY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_dilate(radiusX, radiusY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateErode
+
+               public static SKImageFilter CreateErode(int radiusX, int radiusY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_erode(radiusX, radiusY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateOffset
+
+               public static SKImageFilter CreateOffset(float dx, float dy, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_offset(dx, dy, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreatePicture
+
+               public static SKImageFilter CreatePicture(SKPicture picture)
+               {
+                       if (picture == null)
+                               throw new ArgumentNullException(nameof(picture));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_picture(picture.Handle));
+               }
+
+               public static SKImageFilter CreatePicture(SKPicture picture, SKRect cropRect)
+               {
+                       if (picture == null)
+                               throw new ArgumentNullException(nameof(picture));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_picture_with_croprect(picture.Handle, &cropRect));
+               }
+
+               // CreateTile
+
+               public static SKImageFilter CreateTile(SKRect src, SKRect dst, SKImageFilter input)
+               {
+                       if (input == null)
+                               throw new ArgumentNullException(nameof(input));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_tile(&src, &dst, input.Handle));
+               }
+
+               // CreateBlendMode
+
+               public static SKImageFilter CreateBlendMode(SKBlendMode mode, SKImageFilter background, SKImageFilter foreground = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (background == null)
+                               throw new ArgumentNullException(nameof(background));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_xfermode(mode, background.Handle, foreground == null ? IntPtr.Zero : foreground.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateArithmetic
+
+               public static SKImageFilter CreateArithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, SKImageFilter background, SKImageFilter foreground = null, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (background == null)
+                               throw new ArgumentNullException(nameof(background));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_arithmetic(k1, k2, k3, k4, enforcePMColor, background.Handle, foreground == null ? IntPtr.Zero : foreground.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               // CreateImage
+
+               public static SKImageFilter CreateImage(SKImage image)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException(nameof(image));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_image_source_default(image.Handle));
+               }
+
+               public static SKImageFilter CreateImage(SKImage image, SKRect src, SKRect dst, SKFilterQuality filterQuality)
+               {
+                       if (image == null)
+                               throw new ArgumentNullException(nameof(image));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_image_source(image.Handle, &src, &dst, filterQuality));
+               }
+
+               // CreatePaint
+
+               public static SKImageFilter CreatePaint(SKPaint paint, SKImageFilter.CropRect cropRect = null)
+               {
+                       if (paint == null)
+                               throw new ArgumentNullException(nameof(paint));
+                       return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_paint(paint.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
+               }
+
+               //
+
+               public class CropRect : SKObject
+               {
+                       internal CropRect(IntPtr handle, bool owns)
+                               : base(handle, owns)
+                       {
+                       }
+
+                       public CropRect()
+                               : this(SkiaApi.sk_imagefilter_croprect_new(), true)
+                       {
+                       }
+
+                       public CropRect(SKRect rect, SKCropRectFlags flags = SKCropRectFlags.HasAll)
+                               : this(SkiaApi.sk_imagefilter_croprect_new_with_rect(&rect, (uint)flags), true)
+                       {
+                       }
+
+                       protected override void Dispose (bool disposing) =>
+                               base.Dispose (disposing);
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_imagefilter_croprect_destructor (Handle);
+
+                       public SKRect Rect
+                       {
+                               get
+                               {
+                                       SKRect rect;
+                                       SkiaApi.sk_imagefilter_croprect_get_rect (Handle, &rect);
+                                       return rect;
+                               }
+                       }
+
+                       public SKCropRectFlags Flags =>
+                               (SKCropRectFlags)SkiaApi.sk_imagefilter_croprect_get_flags (Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKImageInfo.cs b/src/XSF/SkiaSharp/SKImageInfo.cs
new file mode 100644 (file)
index 0000000..d1d51dc
--- /dev/null
@@ -0,0 +1,182 @@
+using System;
+
+namespace SkiaSharp
+{
+       internal partial struct SKImageInfoNative
+       {
+               public static void UpdateNative (ref SKImageInfo managed, ref SKImageInfoNative native)
+               {
+                       native.colorspace = managed.ColorSpace?.Handle ?? IntPtr.Zero;
+                       native.width = managed.Width;
+                       native.height = managed.Height;
+                       native.colorType = managed.ColorType;
+                       native.alphaType = managed.AlphaType;
+               }
+
+               public static SKImageInfoNative FromManaged (ref SKImageInfo managed) =>
+                       new SKImageInfoNative {
+                               colorspace = managed.ColorSpace?.Handle ?? IntPtr.Zero,
+                               width = managed.Width,
+                               height = managed.Height,
+                               colorType = managed.ColorType,
+                               alphaType = managed.AlphaType,
+                       };
+
+               public static SKImageInfo ToManaged (ref SKImageInfoNative native) =>
+                       new SKImageInfo {
+                               ColorSpace = SKObject.GetObject<SKColorSpace> (native.colorspace),
+                               Width = native.width,
+                               Height = native.height,
+                               ColorType = native.colorType,
+                               AlphaType = native.alphaType,
+                       };
+       }
+
+       public unsafe struct SKImageInfo : IEquatable<SKImageInfo>
+       {
+               public static readonly SKImageInfo Empty;
+               public static readonly SKColorType PlatformColorType;
+               public static readonly int PlatformColorAlphaShift;
+               public static readonly int PlatformColorRedShift;
+               public static readonly int PlatformColorGreenShift;
+               public static readonly int PlatformColorBlueShift;
+
+               static SKImageInfo ()
+               {
+                       PlatformColorType = SkiaApi.sk_colortype_get_default_8888 ();
+
+                       fixed (int* a = &PlatformColorAlphaShift)
+                       fixed (int* r = &PlatformColorRedShift)
+                       fixed (int* g = &PlatformColorGreenShift)
+                       fixed (int* b = &PlatformColorBlueShift) {
+                               SkiaApi.sk_color_get_bit_shift (a, r, g, b);
+                       }
+               }
+
+               public int Width { get; set; }
+
+               public int Height { get; set; }
+
+               public SKColorType ColorType { get; set; }
+
+               public SKAlphaType AlphaType { get; set; }
+
+               public SKColorSpace ColorSpace { get; set; }
+
+               public SKImageInfo (int width, int height)
+               {
+                       Width = width;
+                       Height = height;
+                       ColorType = PlatformColorType;
+                       AlphaType = SKAlphaType.Premul;
+                       ColorSpace = null;
+               }
+
+               public SKImageInfo (int width, int height, SKColorType colorType)
+               {
+                       Width = width;
+                       Height = height;
+                       ColorType = colorType;
+                       AlphaType = SKAlphaType.Premul;
+                       ColorSpace = null;
+               }
+
+               public SKImageInfo (int width, int height, SKColorType colorType, SKAlphaType alphaType)
+               {
+                       Width = width;
+                       Height = height;
+                       ColorType = colorType;
+                       AlphaType = alphaType;
+                       ColorSpace = null;
+               }
+
+               public SKImageInfo (int width, int height, SKColorType colorType, SKAlphaType alphaType, SKColorSpace colorspace)
+               {
+                       Width = width;
+                       Height = height;
+                       ColorType = colorType;
+                       AlphaType = alphaType;
+                       ColorSpace = colorspace;
+               }
+
+               public readonly int BytesPerPixel =>
+                       ColorType.GetBytesPerPixel ();
+
+               public readonly int BitsPerPixel => BytesPerPixel * 8;
+
+               public readonly int BytesSize => Width * Height * BytesPerPixel;
+
+               public readonly long BytesSize64 => (long)Width * (long)Height * (long)BytesPerPixel;
+
+               public readonly int RowBytes => Width * BytesPerPixel;
+
+               public readonly long RowBytes64 => (long)Width * (long)BytesPerPixel;
+
+               public readonly bool IsEmpty => Width <= 0 || Height <= 0;
+
+               public readonly bool IsOpaque => AlphaType == SKAlphaType.Opaque;
+
+               public readonly SKSizeI Size => new SKSizeI (Width, Height);
+
+               public readonly SKRectI Rect => SKRectI.Create (Width, Height);
+
+               public readonly SKImageInfo WithSize (SKSizeI size) =>
+                       WithSize (size.Width, size.Height);
+
+               public readonly SKImageInfo WithSize (int width, int height)
+               {
+                       var copy = this;
+                       copy.Width = width;
+                       copy.Height = height;
+                       return copy;
+               }
+
+               public readonly SKImageInfo WithColorType (SKColorType newColorType)
+               {
+                       var copy = this;
+                       copy.ColorType = newColorType;
+                       return copy;
+               }
+
+               public readonly SKImageInfo WithColorSpace (SKColorSpace newColorSpace)
+               {
+                       var copy = this;
+                       copy.ColorSpace = newColorSpace;
+                       return copy;
+               }
+
+               public readonly SKImageInfo WithAlphaType (SKAlphaType newAlphaType)
+               {
+                       var copy = this;
+                       copy.AlphaType = newAlphaType;
+                       return copy;
+               }
+
+               public readonly bool Equals (SKImageInfo obj) =>
+                       ColorSpace == obj.ColorSpace &&
+                       Width == obj.Width &&
+                       Height == obj.Height &&
+                       ColorType == obj.ColorType &&
+                       AlphaType == obj.AlphaType;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKImageInfo f && Equals (f);
+
+               public static bool operator == (SKImageInfo left, SKImageInfo right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKImageInfo left, SKImageInfo right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (ColorSpace);
+                       hash.Add (Width);
+                       hash.Add (Height);
+                       hash.Add (ColorType);
+                       hash.Add (AlphaType);
+                       return hash.ToHashCode ();
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKManagedStream.cs b/src/XSF/SkiaSharp/SKManagedStream.cs
new file mode 100644 (file)
index 0000000..c2fccb6
--- /dev/null
@@ -0,0 +1,252 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace SkiaSharp
+{
+       public class SKManagedStream : SKAbstractManagedStream
+       {
+               private Stream stream;
+
+               private bool isAsEnd;
+               private bool disposeStream;
+               private bool wasCopied;
+
+               private WeakReference parent;
+               private WeakReference child;
+
+               public SKManagedStream (Stream managedStream)
+                       : this (managedStream, false)
+               {
+               }
+
+               public SKManagedStream (Stream managedStream, bool disposeManagedStream)
+                       : base (true)
+               {
+                       stream = managedStream ?? throw new ArgumentNullException (nameof (managedStream));
+                       disposeStream = disposeManagedStream;
+               }
+
+               public int CopyTo (SKWStream destination)
+               {
+                       if (destination == null)
+                               throw new ArgumentNullException (nameof (destination));
+
+                       var total = 0;
+                       int len;
+                       var buffer = ArrayPool<byte>.Shared.Rent (SKData.CopyBufferSize);
+                       try {
+                               while ((len = stream.Read (buffer, 0, buffer.Length)) > 0) {
+                                       destination.Write (buffer, len);
+                                       total += len;
+                               }
+                       } finally {
+                               ArrayPool<byte>.Shared.Return (buffer);
+                       }
+                       destination.Flush ();
+                       return total;
+               }
+
+               public SKStreamAsset ToMemoryStream ()
+               {
+                       using (var native = new SKDynamicMemoryWStream ()) {
+                               CopyTo (native);
+                               return native.DetachAsStream ();
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeManaged ()
+               {
+                       var childStream = child?.Target as SKManagedStream;
+                       var parentStream = parent?.Target as SKManagedStream;
+
+                       if (childStream != null && parentStream != null) {
+                               // remove this stream from the list by connecting the parent with the child
+                               childStream.parent = parent;
+                               parentStream.child = child;
+                       } else if (childStream != null) {
+                               // transfer ownership to child
+                               childStream.parent = null;
+                       } else if (parentStream != null) {
+                               // transfer ownership back to parent
+                               parentStream.child = null;
+                               parentStream.wasCopied = false;
+                               parentStream.disposeStream = disposeStream;
+
+                               disposeStream = false;
+                       }
+
+                       parent = null;
+                       child = null;
+
+                       if (disposeStream && stream != null) {
+                               stream.Dispose ();
+                               stream = null;
+                       }
+
+                       base.DisposeManaged ();
+               }
+
+               private IntPtr OnReadManagedStream (IntPtr buffer, IntPtr size)
+               {
+                       byte[] managedBuffer;
+                       using (var reader = new BinaryReader (stream, Encoding.UTF8, true)) {
+                               managedBuffer = reader.ReadBytes ((int)size);
+                       }
+                       var result = managedBuffer.Length;
+                       if (buffer != IntPtr.Zero) {
+                               Marshal.Copy (managedBuffer, 0, buffer, result);
+                       }
+                       if (!stream.CanSeek && (int)size > 0 && result <= (int)size) {
+                               isAsEnd = true;
+                       }
+                       return (IntPtr)result;
+               }
+
+               protected override IntPtr OnRead (IntPtr buffer, IntPtr size)
+               {
+                       VerifyOriginal ();
+
+                       return OnReadManagedStream (buffer, size);
+               }
+
+               protected override IntPtr OnPeek (IntPtr buffer, IntPtr size)
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return (IntPtr)0;
+                       }
+                       var oldPos = stream.Position;
+                       var result = OnReadManagedStream (buffer, size);
+                       stream.Position = oldPos;
+                       return result;
+               }
+
+               protected override bool OnIsAtEnd ()
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return isAsEnd;
+                       }
+                       return stream.Position >= stream.Length;
+               }
+
+               protected override bool OnHasPosition ()
+               {
+                       VerifyOriginal ();
+
+                       return stream.CanSeek;
+               }
+
+               protected override bool OnHasLength ()
+               {
+                       VerifyOriginal ();
+
+                       return stream.CanSeek;
+               }
+
+               protected override bool OnRewind ()
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return false;
+                       }
+                       stream.Position = 0;
+                       return true;
+               }
+
+               protected override IntPtr OnGetPosition ()
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return (IntPtr)0;
+                       }
+                       return (IntPtr)stream.Position;
+               }
+
+               protected override IntPtr OnGetLength ()
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return (IntPtr)0;
+                       }
+                       return (IntPtr)stream.Length;
+               }
+
+               protected override bool OnSeek (IntPtr position)
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return false;
+                       }
+                       stream.Position = (long)position;
+                       return true;
+               }
+
+               protected override bool OnMove (int offset)
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek) {
+                               return false;
+                       }
+                       stream.Position += offset;
+                       return true;
+               }
+
+               protected override IntPtr OnCreateNew ()
+               {
+                       VerifyOriginal ();
+
+                       return IntPtr.Zero;
+               }
+
+               protected override IntPtr OnDuplicate ()
+               {
+                       VerifyOriginal ();
+
+                       if (!stream.CanSeek)
+                               return IntPtr.Zero;
+
+                       var newStream = new SKManagedStream (stream, disposeStream);
+                       newStream.parent = new WeakReference (this);
+
+                       wasCopied = true;
+                       disposeStream = false;
+                       child = new WeakReference (newStream);
+
+                       stream.Position = 0;
+
+                       return newStream.Handle;
+               }
+
+               protected override IntPtr OnFork ()
+               {
+                       VerifyOriginal ();
+
+                       var newStream = new SKManagedStream (stream, disposeStream);
+
+                       wasCopied = true;
+                       disposeStream = false;
+
+                       return newStream.Handle;
+               }
+
+               private void VerifyOriginal ()
+               {
+                       if (wasCopied)
+                               throw new InvalidOperationException ("This stream was duplicated or forked and cannot be read anymore.");
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKManagedWStream.cs b/src/XSF/SkiaSharp/SKManagedWStream.cs
new file mode 100644 (file)
index 0000000..c0ce398
--- /dev/null
@@ -0,0 +1,68 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public class SKManagedWStream : SKAbstractManagedWStream
+       {
+               private Stream stream;
+               private readonly bool disposeStream;
+
+               public SKManagedWStream (Stream managedStream)
+                       : this (managedStream, false)
+               {
+               }
+
+               public SKManagedWStream (Stream managedStream, bool disposeManagedStream)
+                       : this (managedStream, disposeManagedStream, true)
+               {
+               }
+
+               private SKManagedWStream (Stream managedStream, bool disposeManagedStream, bool owns)
+                       : base (owns)
+               {
+                       stream = managedStream;
+                       disposeStream = disposeManagedStream;
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeManaged ()
+               {
+                       if (disposeStream && stream != null) {
+                               stream.Dispose ();
+                               stream = null;
+                       }
+
+                       base.DisposeManaged ();
+               }
+
+               protected override bool OnWrite (IntPtr buffer, IntPtr size)
+               {
+                       var count = (int)size;
+                       var managedBuffer = ArrayPool<byte>.Shared.Rent (count);
+                       try {
+                               if (buffer != IntPtr.Zero) {
+                                       Marshal.Copy (buffer, managedBuffer, 0, count);
+                               }
+                               stream.Write (managedBuffer, 0, count);
+                       } finally {
+                               ArrayPool<byte>.Shared.Return (managedBuffer);
+                       }
+                       return true;
+               }
+
+               protected override void OnFlush ()
+               {
+                       stream.Flush ();
+               }
+
+               protected override IntPtr OnBytesWritten ()
+               {
+                       return (IntPtr)stream.Position;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKMask.cs b/src/XSF/SkiaSharp/SKMask.cs
new file mode 100644 (file)
index 0000000..4458fd2
--- /dev/null
@@ -0,0 +1,182 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public unsafe partial struct SKMask
+       {
+               public SKMask (IntPtr image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format)
+               {
+                       fBounds = bounds;
+                       fRowBytes = rowBytes;
+                       fFormat = format;
+                       fImage = (byte*)image;
+               }
+
+               public SKMask (SKRectI bounds, UInt32 rowBytes, SKMaskFormat format)
+               {
+                       fBounds = bounds;
+                       fRowBytes = rowBytes;
+                       fFormat = format;
+                       fImage = null;
+               }
+
+               // properties
+
+               public IntPtr Image {
+                       readonly get => (IntPtr)fImage;
+                       set => fImage = (byte*)value;
+               }
+
+               public Span<byte> GetImageSpan () =>
+                       new Span<byte> ((void*)Image, (int)ComputeTotalImageSize ());
+
+               public SKRectI Bounds {
+                       readonly get => fBounds;
+                       set => fBounds = value;
+               }
+
+               public UInt32 RowBytes {
+                       readonly get => fRowBytes;
+                       set => fRowBytes = value;
+               }
+
+               public SKMaskFormat Format {
+                       readonly get => fFormat;
+                       set => fFormat = value;
+               }
+
+               public readonly bool IsEmpty {
+                       get {
+                               fixed (SKMask* t = &this) {
+                                       return SkiaApi.sk_mask_is_empty (t);
+                               }
+                       }
+               }
+
+               // allocate / free
+
+               public long AllocateImage ()
+               {
+                       fixed (SKMask* t = &this) {
+                               var size = SkiaApi.sk_mask_compute_total_image_size (t);
+                               fImage = SkiaApi.sk_mask_alloc_image (size);
+                               return (long)size;
+                       }
+               }
+
+               public void FreeImage ()
+               {
+                       if (fImage != null) {
+                               SKMask.FreeImage ((IntPtr)fImage);
+                               fImage = null;
+                       }
+               }
+
+               // Compute*
+
+               public readonly long ComputeImageSize ()
+               {
+                       fixed (SKMask* t = &this) {
+                               return (long)SkiaApi.sk_mask_compute_image_size (t);
+                       }
+               }
+
+               public readonly long ComputeTotalImageSize ()
+               {
+                       fixed (SKMask* t = &this) {
+                               return (long)SkiaApi.sk_mask_compute_total_image_size (t);
+                       }
+               }
+
+               // GetAddr*
+
+               public readonly byte GetAddr1 (int x, int y)
+               {
+                       fixed (SKMask* t = &this) {
+                               return SkiaApi.sk_mask_get_addr_1 (t, x, y);
+                       }
+               }
+
+               public readonly byte GetAddr8 (int x, int y)
+               {
+                       fixed (SKMask* t = &this) {
+                               return SkiaApi.sk_mask_get_addr_8 (t, x, y);
+                       }
+               }
+
+               public readonly UInt16 GetAddr16 (int x, int y)
+               {
+                       fixed (SKMask* t = &this) {
+                               return SkiaApi.sk_mask_get_addr_lcd_16 (t, x, y);
+                       }
+               }
+
+               public readonly UInt32 GetAddr32 (int x, int y)
+               {
+                       fixed (SKMask* t = &this) {
+                               return SkiaApi.sk_mask_get_addr_32 (t, x, y);
+                       }
+               }
+
+               public readonly IntPtr GetAddr (int x, int y)
+               {
+                       fixed (SKMask* t = &this) {
+                               return (IntPtr)SkiaApi.sk_mask_get_addr (t, x, y);
+                       }
+               }
+
+               // statics
+
+               public static IntPtr AllocateImage (long size) =>
+                       (IntPtr)SkiaApi.sk_mask_alloc_image ((IntPtr)size);
+
+               public static void FreeImage (IntPtr image) =>
+                       SkiaApi.sk_mask_free_image ((byte*)image);
+
+               public static SKMask Create (byte[] image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format) =>
+                       Create (image.AsSpan (), bounds, rowBytes, format);
+
+               public static SKMask Create (ReadOnlySpan<byte> image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format)
+               {
+                       // create the mask
+                       var mask = new SKMask (bounds, rowBytes, format);
+
+                       // calculate the size
+                       var imageSize = (int)mask.ComputeTotalImageSize ();
+
+                       // is there the right amount of space in the mask
+                       if (image.Length != imageSize) {
+                               // Note: buffer.Length must match bounds.Height * rowBytes
+                               var expectedSize = bounds.Height * rowBytes;
+                               var message = $"Length of image ({image.Length}) does not match the computed size of the mask ({expectedSize}). Check the {nameof (bounds)} and {nameof (rowBytes)}.";
+                               throw new ArgumentException (message);
+                       }
+
+                       // allocate and copy the image data
+                       mask.AllocateImage ();
+                       image.CopyTo (new Span<byte> ((void*)mask.Image, imageSize));
+
+                       // return the mask
+                       return mask;
+               }
+       }
+
+       public class SKAutoMaskFreeImage : IDisposable
+       {
+               private IntPtr image;
+
+               public SKAutoMaskFreeImage (IntPtr maskImage)
+               {
+                       image = maskImage;
+               }
+
+               public void Dispose ()
+               {
+                       if (image != IntPtr.Zero) {
+                               SKMask.FreeImage (image);
+                               image = IntPtr.Zero;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKMaskFilter.cs b/src/XSF/SkiaSharp/SKMaskFilter.cs
new file mode 100644 (file)
index 0000000..d3e4255
--- /dev/null
@@ -0,0 +1,96 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Flags]
+       [Obsolete]
+       public enum SKBlurMaskFilterFlags
+       {
+               None = 0x00,
+               IgnoreTransform = 0x01,
+               HighQuality = 0x02,
+               All = IgnoreTransform | HighQuality,
+       }
+
+
+       // TODO: `getFormat`
+       // TODO: `computeFastBounds`
+
+       public unsafe class SKMaskFilter : SKObject, ISKReferenceCounted
+       {
+               private const float BlurSigmaScale = 0.57735f;
+               public const int TableMaxLength = 256;
+
+               [Preserve]
+               internal SKMaskFilter (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static float ConvertRadiusToSigma (float radius)
+               {
+                       return radius > 0 ? BlurSigmaScale * radius + 0.5f : 0.0f;
+               }
+
+               public static float ConvertSigmaToRadius (float sigma)
+               {
+                       return sigma > 0.5f ? (sigma - 0.5f) / BlurSigmaScale : 0.0f;
+               }
+
+               public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma)
+               {
+                       return GetObject<SKMaskFilter> (SkiaApi.sk_maskfilter_new_blur (blurStyle, sigma));
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateBlur(SKBlurStyle, float) instead.")]
+               public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKBlurMaskFilterFlags flags)
+               {
+                       return CreateBlur (blurStyle, sigma, SKRect.Empty, true);
+               }
+
+               public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKRect occluder)
+               {
+                       return CreateBlur (blurStyle, sigma, occluder, true);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateBlur(SKBlurStyle, float, SKRect) instead.")]
+               public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKRect occluder, SKBlurMaskFilterFlags flags)
+               {
+                       return CreateBlur (blurStyle, sigma, occluder, true);
+               }
+
+               public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, SKRect occluder, bool respectCTM)
+               {
+                       return GetObject<SKMaskFilter> (SkiaApi.sk_maskfilter_new_blur_with_flags (blurStyle, sigma, &occluder, respectCTM));
+               }
+
+               public static SKMaskFilter CreateTable (byte[] table)
+               {
+                       if (table == null)
+                               throw new ArgumentNullException (nameof (table));
+                       if (table.Length != TableMaxLength)
+                               throw new ArgumentException ("Table must have a length of {SKColorTable.MaxLength}.", nameof (table));
+                       fixed (byte* t = table) {
+                               return GetObject<SKMaskFilter> (SkiaApi.sk_maskfilter_new_table (t));
+                       }
+               }
+
+               public static SKMaskFilter CreateGamma (float gamma)
+               {
+                       return GetObject<SKMaskFilter> (SkiaApi.sk_maskfilter_new_gamma (gamma));
+               }
+
+               public static SKMaskFilter CreateClip (byte min, byte max)
+               {
+                       return GetObject<SKMaskFilter> (SkiaApi.sk_maskfilter_new_clip (min, max));
+               }
+       }
+}
+
diff --git a/src/XSF/SkiaSharp/SKMatrix.cs b/src/XSF/SkiaSharp/SKMatrix.cs
new file mode 100644 (file)
index 0000000..76f9768
--- /dev/null
@@ -0,0 +1,565 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe partial struct SKMatrix
+       {
+               internal const float DegreesToRadians = (float)Math.PI / 180.0f;
+
+               public readonly static SKMatrix Empty;
+
+               public readonly static SKMatrix Identity = new SKMatrix { scaleX = 1, scaleY = 1, persp2 = 1 };
+
+               private class Indices
+               {
+                       public const int ScaleX = 0;
+                       public const int SkewX = 1;
+                       public const int TransX = 2;
+                       public const int SkewY = 3;
+                       public const int ScaleY = 4;
+                       public const int TransY = 5;
+                       public const int Persp0 = 6;
+                       public const int Persp1 = 7;
+                       public const int Persp2 = 8;
+
+                       public const int Count = 9;
+               }
+
+               public SKMatrix (float[] values)
+               {
+                       if (values == null)
+                               throw new ArgumentNullException (nameof (values));
+                       if (values.Length != Indices.Count)
+                               throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (values));
+
+                       scaleX = values[Indices.ScaleX];
+                       skewX = values[Indices.SkewX];
+                       transX = values[Indices.TransX];
+
+                       skewY = values[Indices.SkewY];
+                       scaleY = values[Indices.ScaleY];
+                       transY = values[Indices.TransY];
+
+                       persp0 = values[Indices.Persp0];
+                       persp1 = values[Indices.Persp1];
+                       persp2 = values[Indices.Persp2];
+               }
+
+               public SKMatrix (
+                       float scaleX, float skewX, float transX,
+                       float skewY, float scaleY, float transY,
+                       float persp0, float persp1, float persp2)
+               {
+                       this.scaleX = scaleX;
+                       this.skewX = skewX;
+                       this.transX = transX;
+                       this.skewY = skewY;
+                       this.scaleY = scaleY;
+                       this.transY = transY;
+                       this.persp0 = persp0;
+                       this.persp1 = persp1;
+                       this.persp2 = persp2;
+               }
+
+               public readonly bool IsIdentity => Equals (Identity);
+
+               // Values
+
+               public float[] Values {
+                       readonly get =>
+                               new float[9] {
+                                       scaleX, skewX, transX,
+                                       skewY, scaleY, transY,
+                                       persp0, persp1, persp2
+                               };
+                       set {
+                               if (value == null)
+                                       throw new ArgumentNullException (nameof (Values));
+                               if (value.Length != Indices.Count)
+                                       throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (Values));
+
+                               scaleX = value[Indices.ScaleX];
+                               skewX = value[Indices.SkewX];
+                               transX = value[Indices.TransX];
+
+                               skewY = value[Indices.SkewY];
+                               scaleY = value[Indices.ScaleY];
+                               transY = value[Indices.TransY];
+
+                               persp0 = value[Indices.Persp0];
+                               persp1 = value[Indices.Persp1];
+                               persp2 = value[Indices.Persp2];
+                       }
+               }
+
+               public readonly void GetValues (float[] values)
+               {
+                       if (values == null)
+                               throw new ArgumentNullException (nameof (values));
+                       if (values.Length != Indices.Count)
+                               throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (values));
+
+                       values[Indices.ScaleX] = scaleX;
+                       values[Indices.SkewX] = skewX;
+                       values[Indices.TransX] = transX;
+
+                       values[Indices.SkewY] = skewY;
+                       values[Indices.ScaleY] = scaleY;
+                       values[Indices.TransY] = transY;
+
+                       values[Indices.Persp0] = persp0;
+                       values[Indices.Persp1] = persp1;
+                       values[Indices.Persp2] = persp2;
+               }
+
+               // Create*
+
+               public static SKMatrix CreateIdentity () =>
+                       new SKMatrix { scaleX = 1, scaleY = 1, persp2 = 1 };
+
+               public static SKMatrix CreateTranslation (float x, float y)
+               {
+                       if (x == 0 && y == 0)
+                               return Identity;
+
+                       return new SKMatrix {
+                               scaleX = 1,
+                               scaleY = 1,
+                               transX = x,
+                               transY = y,
+                               persp2 = 1,
+                       };
+               }
+
+               public static SKMatrix CreateScale (float x, float y)
+               {
+                       if (x == 1 && y == 1)
+                               return Identity;
+
+                       return new SKMatrix {
+                               scaleX = x,
+                               scaleY = y,
+                               persp2 = 1,
+                       };
+               }
+
+               public static SKMatrix CreateScale (float x, float y, float pivotX, float pivotY)
+               {
+                       if (x == 1 && y == 1)
+                               return Identity;
+
+                       var tx = pivotX - x * pivotX;
+                       var ty = pivotY - y * pivotY;
+
+                       return new SKMatrix {
+                               scaleX = x,
+                               scaleY = y,
+                               transX = tx,
+                               transY = ty,
+                               persp2 = 1,
+                       };
+               }
+
+               public static SKMatrix CreateRotation (float radians)
+               {
+                       if (radians == 0)
+                               return Identity;
+
+                       var sin = (float)Math.Sin (radians);
+                       var cos = (float)Math.Cos (radians);
+
+                       var matrix = Identity;
+                       SetSinCos (ref matrix, sin, cos);
+                       return matrix;
+               }
+
+               public static SKMatrix CreateRotation (float radians, float pivotX, float pivotY)
+               {
+                       if (radians == 0)
+                               return Identity;
+
+                       var sin = (float)Math.Sin (radians);
+                       var cos = (float)Math.Cos (radians);
+
+                       var matrix = Identity;
+                       SetSinCos (ref matrix, sin, cos, pivotX, pivotY);
+                       return matrix;
+               }
+
+               public static SKMatrix CreateRotationDegrees (float degrees)
+               {
+                       if (degrees == 0)
+                               return Identity;
+
+                       return CreateRotation (degrees * DegreesToRadians);
+               }
+
+               public static SKMatrix CreateRotationDegrees (float degrees, float pivotX, float pivotY)
+               {
+                       if (degrees == 0)
+                               return Identity;
+
+                       return CreateRotation (degrees * DegreesToRadians, pivotX, pivotY);
+               }
+
+               public static SKMatrix CreateSkew (float x, float y)
+               {
+                       if (x == 0 && y == 0)
+                               return Identity;
+
+                       return new SKMatrix {
+                               scaleX = 1,
+                               skewX = x,
+                               skewY = y,
+                               scaleY = 1,
+                               persp2 = 1,
+                       };
+               }
+
+               // Make*
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeIdentity () =>
+                       CreateIdentity ();
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeScale (float sx, float sy) =>
+                       CreateScale (sx, sy);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeScale (float sx, float sy, float pivotX, float pivotY) =>
+                       CreateScale (sx, sy, pivotX, pivotY);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeTranslation (float dx, float dy) =>
+                       CreateTranslation (dx, dy);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeRotation (float radians) =>
+                       CreateRotation (radians);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeRotation (float radians, float pivotx, float pivoty) =>
+                       CreateRotation (radians, pivotx, pivoty);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeRotationDegrees (float degrees) =>
+                       CreateRotationDegrees (degrees);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeRotationDegrees (float degrees, float pivotx, float pivoty) =>
+                       CreateRotationDegrees (degrees, pivotx, pivoty);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix MakeSkew (float sx, float sy) =>
+                       CreateSkew (sx, sy);
+
+               // Set*
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public void SetScaleTranslate (float sx, float sy, float tx, float ty)
+               {
+                       scaleX = sx;
+                       skewX = 0;
+                       transX = tx;
+
+                       skewY = 0;
+                       scaleY = sy;
+                       transY = ty;
+
+                       persp0 = 0;
+                       persp1 = 0;
+                       persp2 = 1;
+               }
+
+               // Rotate
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRotation(float, float, float) instead.")]
+               public static void Rotate (ref SKMatrix matrix, float radians, float pivotx, float pivoty)
+               {
+                       var sin = (float)Math.Sin (radians);
+                       var cos = (float)Math.Cos (radians);
+                       SetSinCos (ref matrix, sin, cos, pivotx, pivoty);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRotationDegrees(float, float, float) instead.")]
+               public static void RotateDegrees (ref SKMatrix matrix, float degrees, float pivotx, float pivoty)
+               {
+                       var sin = (float)Math.Sin (degrees * DegreesToRadians);
+                       var cos = (float)Math.Cos (degrees * DegreesToRadians);
+                       SetSinCos (ref matrix, sin, cos, pivotx, pivoty);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRotation(float) instead.")]
+               public static void Rotate (ref SKMatrix matrix, float radians)
+               {
+                       var sin = (float)Math.Sin (radians);
+                       var cos = (float)Math.Cos (radians);
+                       SetSinCos (ref matrix, sin, cos);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateRotationDegrees(float) instead.")]
+               public static void RotateDegrees (ref SKMatrix matrix, float degrees)
+               {
+                       var sin = (float)Math.Sin (degrees * DegreesToRadians);
+                       var cos = (float)Math.Cos (degrees * DegreesToRadians);
+                       SetSinCos (ref matrix, sin, cos);
+               }
+
+               // Invert
+
+               public readonly bool IsInvertible {
+                       get {
+                               fixed (SKMatrix* t = &this) {
+                                       return SkiaApi.sk_matrix_try_invert (t, null);
+                               }
+                       }
+               }
+
+               public readonly bool TryInvert (out SKMatrix inverse)
+               {
+                       fixed (SKMatrix* i = &inverse)
+                       fixed (SKMatrix* t = &this) {
+                               return SkiaApi.sk_matrix_try_invert (t, i);
+                       }
+               }
+
+               public readonly SKMatrix Invert ()
+               {
+                       if (TryInvert (out var matrix))
+                               return matrix;
+
+                       return Empty;
+               }
+
+               // *Concat
+
+               public static SKMatrix Concat (SKMatrix first, SKMatrix second)
+               {
+                       SKMatrix target;
+                       SkiaApi.sk_matrix_concat (&target, &first, &second);
+                       return target;
+               }
+
+               public readonly SKMatrix PreConcat (SKMatrix matrix)
+               {
+                       var target = this;
+                       SkiaApi.sk_matrix_pre_concat (&target, &matrix);
+                       return target;
+               }
+
+               public readonly SKMatrix PostConcat (SKMatrix matrix)
+               {
+                       var target = this;
+                       SkiaApi.sk_matrix_post_concat (&target, &matrix);
+                       return target;
+               }
+
+               public static void Concat (ref SKMatrix target, SKMatrix first, SKMatrix second)
+               {
+                       fixed (SKMatrix* t = &target) {
+                               SkiaApi.sk_matrix_concat (t, &first, &second);
+                       }
+               }
+
+               public static void Concat (ref SKMatrix target, ref SKMatrix first, ref SKMatrix second)
+               {
+                       fixed (SKMatrix* t = &target)
+                       fixed (SKMatrix* f = &first)
+                       fixed (SKMatrix* s = &second) {
+                               SkiaApi.sk_matrix_concat (t, f, s);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use PreConcat(SKMatrix) instead.")]
+               public static void PreConcat (ref SKMatrix target, SKMatrix matrix)
+               {
+                       fixed (SKMatrix* t = &target) {
+                               SkiaApi.sk_matrix_pre_concat (t, &matrix);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use PreConcat(SKMatrix) instead.")]
+               public static void PreConcat (ref SKMatrix target, ref SKMatrix matrix)
+               {
+                       fixed (SKMatrix* t = &target)
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_matrix_pre_concat (t, m);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use PostConcat(SKMatrix) instead.")]
+               public static void PostConcat (ref SKMatrix target, SKMatrix matrix)
+               {
+                       fixed (SKMatrix* t = &target) {
+                               SkiaApi.sk_matrix_post_concat (t, &matrix);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use PostConcat(SKMatrix) instead.")]
+               public static void PostConcat (ref SKMatrix target, ref SKMatrix matrix)
+               {
+                       fixed (SKMatrix* t = &target)
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_matrix_post_concat (t, m);
+                       }
+               }
+
+               // MapRect
+
+               public readonly SKRect MapRect (SKRect source)
+               {
+                       SKRect dest;
+                       fixed (SKMatrix* m = &this) {
+                               SkiaApi.sk_matrix_map_rect (m, &dest, &source);
+                       }
+                       return dest;
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use MapRect(SKRect) instead.")]
+               public static void MapRect (ref SKMatrix matrix, out SKRect dest, ref SKRect source)
+               {
+                       fixed (SKMatrix* m = &matrix)
+                       fixed (SKRect* d = &dest)
+                       fixed (SKRect* s = &source) {
+                               SkiaApi.sk_matrix_map_rect (m, d, s);
+                       }
+               }
+
+               // MapPoints
+
+               public readonly SKPoint MapPoint (SKPoint point) =>
+                       MapPoint (point.X, point.Y);
+
+               public readonly SKPoint MapPoint (float x, float y)
+               {
+                       SKPoint result;
+                       fixed (SKMatrix* t = &this) {
+                               SkiaApi.sk_matrix_map_xy (t, x, y, &result);
+                       }
+                       return result;
+               }
+
+               public readonly void MapPoints (SKPoint[] result, SKPoint[] points)
+               {
+                       if (result == null)
+                               throw new ArgumentNullException (nameof (result));
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+                       if (result.Length != points.Length)
+                               throw new ArgumentException ("Buffers must be the same size.");
+
+                       fixed (SKMatrix* t = &this)
+                       fixed (SKPoint* rp = result)
+                       fixed (SKPoint* pp = points) {
+                               SkiaApi.sk_matrix_map_points (t, rp, pp, result.Length);
+                       }
+               }
+
+               public readonly SKPoint[] MapPoints (SKPoint[] points)
+               {
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+
+                       var res = new SKPoint[points.Length];
+                       MapPoints (res, points);
+                       return res;
+               }
+
+               // MapVectors
+
+               public readonly SKPoint MapVector (SKPoint vector) =>
+                       MapVector (vector.X, vector.Y);
+
+               public readonly SKPoint MapVector (float x, float y)
+               {
+                       SKPoint result;
+                       fixed (SKMatrix* t = &this) {
+                               SkiaApi.sk_matrix_map_vector (t, x, y, &result);
+                       }
+                       return result;
+               }
+
+               public readonly void MapVectors (SKPoint[] result, SKPoint[] vectors)
+               {
+                       if (result == null)
+                               throw new ArgumentNullException (nameof (result));
+                       if (vectors == null)
+                               throw new ArgumentNullException (nameof (vectors));
+                       if (result.Length != vectors.Length)
+                               throw new ArgumentException ("Buffers must be the same size.");
+
+                       fixed (SKMatrix* t = &this)
+                       fixed (SKPoint* rp = result)
+                       fixed (SKPoint* pp = vectors) {
+                               SkiaApi.sk_matrix_map_vectors (t, rp, pp, result.Length);
+                       }
+               }
+
+               public readonly SKPoint[] MapVectors (SKPoint[] vectors)
+               {
+                       if (vectors == null)
+                               throw new ArgumentNullException (nameof (vectors));
+
+                       var res = new SKPoint[vectors.Length];
+                       MapVectors (res, vectors);
+                       return res;
+               }
+
+               // MapRadius
+
+               public readonly float MapRadius (float radius)
+               {
+                       fixed (SKMatrix* t = &this) {
+                               return SkiaApi.sk_matrix_map_radius (t, radius);
+                       }
+               }
+
+               // private
+
+               private static void SetSinCos (ref SKMatrix matrix, float sin, float cos)
+               {
+                       matrix.scaleX = cos;
+                       matrix.skewX = -sin;
+                       matrix.transX = 0;
+                       matrix.skewY = sin;
+                       matrix.scaleY = cos;
+                       matrix.transY = 0;
+                       matrix.persp0 = 0;
+                       matrix.persp1 = 0;
+                       matrix.persp2 = 1;
+               }
+
+               private static void SetSinCos (ref SKMatrix matrix, float sin, float cos, float pivotx, float pivoty)
+               {
+                       float oneMinusCos = 1 - cos;
+
+                       matrix.scaleX = cos;
+                       matrix.skewX = -sin;
+                       matrix.transX = Dot (sin, pivoty, oneMinusCos, pivotx);
+                       matrix.skewY = sin;
+                       matrix.scaleY = cos;
+                       matrix.transY = Dot (-sin, pivotx, oneMinusCos, pivoty);
+                       matrix.persp0 = 0;
+                       matrix.persp1 = 0;
+                       matrix.persp2 = 1;
+               }
+
+               private static float Dot (float a, float b, float c, float d) =>
+                       a * b + c * d;
+
+               private static float Cross (float a, float b, float c, float d) =>
+                       a * b - c * d;
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKMatrix44.cs b/src/XSF/SkiaSharp/SKMatrix44.cs
new file mode 100644 (file)
index 0000000..2fbe4c6
--- /dev/null
@@ -0,0 +1,431 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class SKMatrix44 : SKObject
+       {
+               [Preserve]
+               internal SKMatrix44 (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_matrix44_destroy (Handle);
+
+               public SKMatrix44 ()
+                       : this (SkiaApi.sk_matrix44_new (), true)
+               {
+                       if (Handle == IntPtr.Zero)
+                               throw new InvalidOperationException ("Unable to create a new SKMatrix44 instance.");
+               }
+
+               public SKMatrix44 (SKMatrix44 src)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       Handle = SkiaApi.sk_matrix44_new_copy (src.Handle);
+
+                       if (Handle == IntPtr.Zero)
+                               throw new InvalidOperationException ("Unable to create a new SKMatrix44 instance.");
+               }
+
+               public SKMatrix44 (SKMatrix44 a, SKMatrix44 b)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (a == null)
+                               throw new ArgumentNullException (nameof (a));
+                       if (b == null)
+                               throw new ArgumentNullException (nameof (b));
+
+                       Handle = SkiaApi.sk_matrix44_new_concat (a.Handle, b.Handle);
+
+                       if (Handle == IntPtr.Zero)
+                               throw new InvalidOperationException ("Unable to create a new SKMatrix44 instance.");
+               }
+
+               public SKMatrix44 (SKMatrix src)
+                       : this (SkiaApi.sk_matrix44_new_matrix (&src), true)
+               {
+                       if (Handle == IntPtr.Zero)
+                               throw new InvalidOperationException ("Unable to create a new SKMatrix44 instance.");
+               }
+
+               // properties
+
+               public SKMatrix Matrix {
+                       get {
+                               SKMatrix matrix;
+                               SkiaApi.sk_matrix44_to_matrix (Handle, &matrix);
+                               return matrix;
+                       }
+               }
+
+               public SKMatrix44TypeMask Type =>
+                       SkiaApi.sk_matrix44_get_type (Handle);
+
+               public float this[int row, int column] {
+                       get => SkiaApi.sk_matrix44_get (Handle, row, column);
+                       set => SkiaApi.sk_matrix44_set (Handle, row, column, value);
+               }
+
+               // Create*
+
+               public static SKMatrix44 CreateIdentity ()
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetIdentity ();
+                       return matrix;
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               public static SKMatrix44 CreateTranslate (float x, float y, float z) =>
+                       CreateTranslation (x, y, z);
+
+               public static SKMatrix44 CreateTranslation (float x, float y, float z)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetTranslate (x, y, z);
+                       return matrix;
+               }
+
+               public static SKMatrix44 CreateScale (float x, float y, float z)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetScale (x, y, z);
+                       return matrix;
+               }
+
+               public static SKMatrix44 CreateRotation (float x, float y, float z, float radians)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetRotationAbout (x, y, z, radians);
+                       return matrix;
+               }
+
+               public static SKMatrix44 CreateRotationDegrees (float x, float y, float z, float degrees)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetRotationAboutDegrees (x, y, z, degrees);
+                       return matrix;
+               }
+
+               // From
+
+               public static SKMatrix44 FromRowMajor (float[] src)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetRowMajor (src);
+                       return matrix;
+               }
+
+               public static SKMatrix44 FromColumnMajor (float[] src)
+               {
+                       var matrix = new SKMatrix44 ();
+                       matrix.SetColumnMajor (src);
+                       return matrix;
+               }
+
+               // To*
+
+               public float[] ToColumnMajor ()
+               {
+                       var dst = new float[16];
+                       ToColumnMajor (dst);
+                       return dst;
+               }
+
+               public void ToColumnMajor (float[] dst)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (dst.Length != 16)
+                               throw new ArgumentException ("The destination array must be 16 entries.", nameof (dst));
+
+                       fixed (float* d = dst) {
+                               SkiaApi.sk_matrix44_as_col_major (Handle, d);
+                       }
+               }
+
+               public float[] ToRowMajor ()
+               {
+                       var dst = new float[16];
+                       ToRowMajor (dst);
+                       return dst;
+               }
+
+               public void ToRowMajor (float[] dst)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (dst.Length != 16)
+                               throw new ArgumentException ("The destination array must be 16 entries.", nameof (dst));
+
+                       fixed (float* d = dst) {
+                               SkiaApi.sk_matrix44_as_row_major (Handle, d);
+                       }
+               }
+
+               // Equal
+
+               public static bool Equal (SKMatrix44 left, SKMatrix44 right)
+               {
+                       if (left == null)
+                               throw new ArgumentNullException (nameof (left));
+                       if (right == null)
+                               throw new ArgumentNullException (nameof (right));
+
+                       return SkiaApi.sk_matrix44_equals (left.Handle, right.Handle);
+               }
+
+               // Set*
+
+               public void SetIdentity () =>
+                       SkiaApi.sk_matrix44_set_identity (Handle);
+
+               public void SetColumnMajor (float[] src)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+                       if (src.Length != 16)
+                               throw new ArgumentException ("The source array must be 16 entries.", nameof (src));
+
+                       fixed (float* s = src) {
+                               SkiaApi.sk_matrix44_set_col_major (Handle, s);
+                       }
+               }
+
+               public void SetRowMajor (float[] src)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+                       if (src.Length != 16)
+                               throw new ArgumentException ("The source array must be 16 entries.", nameof (src));
+
+                       fixed (float* s = src) {
+                               SkiaApi.sk_matrix44_set_row_major (Handle, s);
+                       }
+               }
+
+               public void Set3x3ColumnMajor (float[] src)
+               {
+                       if (src.Length != 9)
+                               throw new ArgumentException ("The source array must be 9 entries.", nameof (src));
+
+                       var row = stackalloc float[9] { src[0], src[3], src[6], src[1], src[4], src[7], src[2], src[5], src[8] };
+                       SkiaApi.sk_matrix44_set_3x3_row_major (Handle, row);
+               }
+
+               public void Set3x3RowMajor (float[] src)
+               {
+                       if (src.Length != 9)
+                               throw new ArgumentException ("The source array must be 9 entries.", nameof (src));
+
+                       fixed (float* s = src) {
+                               SkiaApi.sk_matrix44_set_3x3_row_major (Handle, s);
+                       }
+               }
+
+               public void SetTranslate (float dx, float dy, float dz) =>
+                       SkiaApi.sk_matrix44_set_translate (Handle, dx, dy, dz);
+
+               public void SetScale (float sx, float sy, float sz) =>
+                       SkiaApi.sk_matrix44_set_scale (Handle, sx, sy, sz);
+
+               public void SetRotationAboutDegrees (float x, float y, float z, float degrees) =>
+                       SkiaApi.sk_matrix44_set_rotate_about_degrees (Handle, x, y, z, degrees);
+
+               public void SetRotationAbout (float x, float y, float z, float radians) =>
+                       SkiaApi.sk_matrix44_set_rotate_about_radians (Handle, x, y, z, radians);
+
+               public void SetRotationAboutUnit (float x, float y, float z, float radians) =>
+                       SkiaApi.sk_matrix44_set_rotate_about_radians_unit (Handle, x, y, z, radians);
+
+               public void SetConcat (SKMatrix44 a, SKMatrix44 b)
+               {
+                       if (a == null)
+                               throw new ArgumentNullException (nameof (a));
+                       if (b == null)
+                               throw new ArgumentNullException (nameof (b));
+
+                       SkiaApi.sk_matrix44_set_concat (Handle, a.Handle, b.Handle);
+               }
+
+               // Pre* / Post*
+
+               public void PreTranslate (float dx, float dy, float dz) =>
+                       SkiaApi.sk_matrix44_pre_translate (Handle, dx, dy, dz);
+
+               public void PostTranslate (float dx, float dy, float dz) =>
+                       SkiaApi.sk_matrix44_post_translate (Handle, dx, dy, dz);
+
+               public void PreScale (float sx, float sy, float sz) =>
+                       SkiaApi.sk_matrix44_pre_scale (Handle, sx, sy, sz);
+
+               public void PostScale (float sx, float sy, float sz) =>
+                       SkiaApi.sk_matrix44_post_scale (Handle, sx, sy, sz);
+
+               public void PreConcat (SKMatrix44 m)
+               {
+                       if (m == null)
+                               throw new ArgumentNullException (nameof (m));
+
+                       SkiaApi.sk_matrix44_pre_concat (Handle, m.Handle);
+               }
+
+               public void PostConcat (SKMatrix44 m)
+               {
+                       if (m == null)
+                               throw new ArgumentNullException (nameof (m));
+
+                       SkiaApi.sk_matrix44_post_concat (Handle, m.Handle);
+               }
+
+               // Invert
+
+               public bool IsInvertible =>
+                       SkiaApi.sk_matrix44_invert (Handle, IntPtr.Zero);
+
+               public SKMatrix44 Invert ()
+               {
+                       var inverse = new SKMatrix44 ();
+                       if (!Invert (inverse)) {
+                               inverse.Dispose ();
+                               inverse = null;
+                       }
+                       return inverse;
+               }
+
+               public bool Invert (SKMatrix44 inverse)
+               {
+                       if (inverse == null)
+                               throw new ArgumentNullException (nameof (inverse));
+
+                       return SkiaApi.sk_matrix44_invert (Handle, inverse.Handle);
+               }
+
+               // Transpose
+
+               public void Transpose () =>
+                       SkiaApi.sk_matrix44_transpose (Handle);
+
+               // MapScalars
+
+               public float[] MapScalars (float x, float y, float z, float w)
+               {
+                       var srcVector4 = new float[4] { x, y, z, w };
+                       var dstVector4 = new float[4];
+                       MapScalars (srcVector4, dstVector4);
+                       return dstVector4;
+               }
+
+               public float[] MapScalars (float[] srcVector4)
+               {
+                       var dstVector4 = new float[4];
+                       MapScalars (srcVector4, dstVector4);
+                       return dstVector4;
+               }
+
+               public void MapScalars (float[] srcVector4, float[] dstVector4)
+               {
+                       if (srcVector4 == null)
+                               throw new ArgumentNullException (nameof (srcVector4));
+                       if (srcVector4.Length != 4)
+                               throw new ArgumentException ("The source vector array must be 4 entries.", nameof (srcVector4));
+                       if (dstVector4 == null)
+                               throw new ArgumentNullException (nameof (dstVector4));
+                       if (dstVector4.Length != 4)
+                               throw new ArgumentException ("The destination vector array must be 4 entries.", nameof (dstVector4));
+
+                       fixed (float* s = srcVector4)
+                       fixed (float* d = dstVector4) {
+                               SkiaApi.sk_matrix44_map_scalars (Handle, s, d);
+                       }
+               }
+
+               // MapPoints
+
+               public SKPoint MapPoint (SKPoint src) =>
+                       MapPoints (new[] { src })[0];
+
+               public SKPoint[] MapPoints (SKPoint[] src)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       var count = src.Length;
+                       var src2Length = count * 2;
+                       //var src4Length = count * 4;
+
+                       var src2 = new float[src2Length];
+                       for (int i = 0, i2 = 0; i < count; i++, i2 += 2) {
+                               src2[i2] = src[i].X;
+                               src2[i2 + 1] = src[i].Y;
+                       }
+
+                       var dst4 = MapVector2 (src2);
+
+                       var dst = new SKPoint[count];
+                       for (int i = 0, i4 = 0; i < count; i++, i4 += 4) {
+                               dst[i].X = dst4[i4];
+                               dst[i].Y = dst4[i4 + 1];
+                       }
+
+                       return dst;
+               }
+
+               // MapVector2
+
+               public float[] MapVector2 (float[] src2)
+               {
+                       if (src2 == null)
+                               throw new ArgumentNullException (nameof (src2));
+                       if (src2.Length % 2 != 0)
+                               throw new ArgumentException ("The source vector array must be a set of pairs.", nameof (src2));
+
+                       var dst4 = new float[src2.Length * 2];
+                       MapVector2 (src2, dst4);
+                       return dst4;
+               }
+
+               public void MapVector2 (float[] src2, float[] dst4)
+               {
+                       if (src2 == null)
+                               throw new ArgumentNullException (nameof (src2));
+                       if (src2.Length % 2 != 0)
+                               throw new ArgumentException ("The source vector array must be a set of pairs.", nameof (src2));
+                       if (dst4 == null)
+                               throw new ArgumentNullException (nameof (dst4));
+                       if (dst4.Length % 4 != 0)
+                               throw new ArgumentException ("The destination vector array must be a set quads.", nameof (dst4));
+                       if (src2.Length / 2 != dst4.Length / 4)
+                               throw new ArgumentException ("The source vector array must have the same number of pairs as the destination vector array has quads.", nameof (dst4));
+
+                       fixed (float* s = src2)
+                       fixed (float* d = dst4) {
+                               SkiaApi.sk_matrix44_map2 (Handle, s, src2.Length / 2, d);
+                       }
+               }
+
+               // Preserves2DAxisAlignment
+
+               public bool Preserves2DAxisAlignment (float epsilon) =>
+                       SkiaApi.sk_matrix44_preserves_2d_axis_alignment (Handle, epsilon);
+
+               // Determinant
+
+               public double Determinant () =>
+                       SkiaApi.sk_matrix44_determinant (Handle);
+
+               // operators
+
+               public static implicit operator SKMatrix44 (SKMatrix matrix) =>
+                       new SKMatrix44 (matrix);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKNWayCanvas.cs b/src/XSF/SkiaSharp/SKNWayCanvas.cs
new file mode 100644 (file)
index 0000000..fddf509
--- /dev/null
@@ -0,0 +1,40 @@
+using System;
+
+namespace SkiaSharp
+{
+       public class SKNWayCanvas : SKNoDrawCanvas
+       {
+               [Preserve]
+               internal SKNWayCanvas (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKNWayCanvas (int width, int height)
+                       : this (IntPtr.Zero, true)
+               {
+                       Handle = SkiaApi.sk_nway_canvas_new (width, height);
+               }
+
+               public void AddCanvas (SKCanvas canvas)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException (nameof (canvas));
+
+                       SkiaApi.sk_nway_canvas_add_canvas (Handle, canvas.Handle);
+               }
+
+               public void RemoveCanvas (SKCanvas canvas)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException (nameof (canvas));
+
+                       SkiaApi.sk_nway_canvas_remove_canvas (Handle, canvas.Handle);
+               }
+
+               public void RemoveAll ()
+               {
+                       SkiaApi.sk_nway_canvas_remove_all (Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKNoDrawCanvas.cs b/src/XSF/SkiaSharp/SKNoDrawCanvas.cs
new file mode 100644 (file)
index 0000000..05cfaf1
--- /dev/null
@@ -0,0 +1,19 @@
+using System;
+
+namespace SkiaSharp
+{
+       public class SKNoDrawCanvas : SKCanvas
+       {
+               [Preserve]
+               internal SKNoDrawCanvas (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKNoDrawCanvas (int width, int height)
+                       : this (IntPtr.Zero, true)
+               {
+                       Handle = SkiaApi.sk_nodraw_canvas_new (width, height);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKObject.cs b/src/XSF/SkiaSharp/SKObject.cs
new file mode 100644 (file)
index 0000000..f880c5d
--- /dev/null
@@ -0,0 +1,331 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace SkiaSharp
+{
+       public abstract class SKObject : SKNativeObject
+       {
+               private readonly object locker = new object ();
+
+               private ConcurrentDictionary<IntPtr, SKObject> ownedObjects;
+               private ConcurrentDictionary<IntPtr, SKObject> keepAliveObjects;
+
+               internal ConcurrentDictionary<IntPtr, SKObject> OwnedObjects {
+                       get {
+                               if (ownedObjects == null) {
+                                       lock (locker) {
+                                               ownedObjects ??= new ConcurrentDictionary<IntPtr, SKObject> ();
+                                       }
+                               }
+                               return ownedObjects;
+                       }
+               }
+
+               internal ConcurrentDictionary<IntPtr, SKObject> KeepAliveObjects {
+                       get {
+                               if (keepAliveObjects == null) {
+                                       lock (locker) {
+                                               keepAliveObjects ??= new ConcurrentDictionary<IntPtr, SKObject> ();
+                                       }
+                               }
+                               return keepAliveObjects;
+                       }
+               }
+
+               static SKObject ()
+               {
+                       SKColorSpace.EnsureStaticInstanceAreInitialized ();
+                       SKData.EnsureStaticInstanceAreInitialized ();
+                       SKFontManager.EnsureStaticInstanceAreInitialized ();
+                       SKTypeface.EnsureStaticInstanceAreInitialized ();
+               }
+
+               [Preserve]
+               internal SKObject (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public override IntPtr Handle {
+                       get => base.Handle;
+                       protected set {
+                               if (value == IntPtr.Zero) {
+                                       DeregisterHandle (Handle, this);
+                                       base.Handle = value;
+                               } else {
+                                       base.Handle = value;
+                                       RegisterHandle (Handle, this);
+                               }
+                       }
+               }
+
+               protected override void DisposeManaged ()
+               {
+                       if (ownedObjects is ConcurrentDictionary<IntPtr, SKObject> dic) {
+                               foreach (var child in dic) {
+                                       child.Value.DisposeInternal ();
+                               }
+                               dic.Clear ();
+                       }
+                       KeepAliveObjects?.Clear ();
+               }
+
+               protected override void DisposeNative ()
+               {
+                       if (this is ISKReferenceCounted refcnt)
+                               refcnt.SafeUnRef ();
+               }
+
+               internal static TSkiaObject GetObject<TSkiaObject> (IntPtr handle, bool owns = true, bool unrefExisting = true, bool refNew = false)
+                       where TSkiaObject : SKObject
+               {
+                       if (handle == IntPtr.Zero)
+                               return null;
+
+                       return HandleDictionary.GetObject<TSkiaObject, TSkiaObject> (handle, owns, unrefExisting, refNew);
+               }
+
+               internal static TSkiaObject GetObject<TSkiaObject, TSkiaImplementation> (IntPtr handle, bool owns = true, bool unrefExisting = true, bool refNew = false)
+                       where TSkiaObject : SKObject
+                       where TSkiaImplementation : SKObject, TSkiaObject
+               {
+                       if (handle == IntPtr.Zero)
+                               return null;
+
+                       return HandleDictionary.GetObject<TSkiaObject, TSkiaImplementation> (handle, owns, unrefExisting, refNew);
+               }
+
+               internal static void RegisterHandle (IntPtr handle, SKObject instance)
+               {
+                       if (handle == IntPtr.Zero || instance == null)
+                               return;
+
+                       HandleDictionary.RegisterHandle (handle, instance);
+               }
+
+               internal static void DeregisterHandle (IntPtr handle, SKObject instance)
+               {
+                       if (handle == IntPtr.Zero)
+                               return;
+
+                       HandleDictionary.DeregisterHandle (handle, instance);
+               }
+
+               internal static bool GetInstance<TSkiaObject> (IntPtr handle, out TSkiaObject instance)
+                       where TSkiaObject : SKObject
+               {
+                       if (handle == IntPtr.Zero) {
+                               instance = null;
+                               return false;
+                       }
+
+                       return HandleDictionary.GetInstance<TSkiaObject> (handle, out instance);
+               }
+
+               // indicate that the ownership of this object is now in the hands of
+               // the native object
+               internal void RevokeOwnership (SKObject newOwner)
+               {
+                       OwnsHandle = false;
+                       IgnorePublicDispose = true;
+
+                       if (newOwner == null)
+                               DisposeInternal ();
+                       else
+                               newOwner.OwnedObjects[Handle] = this;
+               }
+
+               // indicate that the child is controlled by the native code and
+               // the managed wrapper should be disposed when the owner is
+               internal static T OwnedBy<T> (T child, SKObject owner)
+                       where T : SKObject
+               {
+                       if (child != null) {
+                               owner.OwnedObjects[child.Handle] = child;
+                       }
+
+                       return child;
+               }
+
+               // indicate that the child was created by the managed code and
+               // should be disposed when the owner is
+               internal static T Owned<T> (T owner, SKObject child)
+                       where T : SKObject
+               {
+                       if (child != null) {
+                               if (owner != null)
+                                       owner.OwnedObjects[child.Handle] = child;
+                               else
+                                       child.Dispose ();
+                       }
+
+                       return owner;
+               }
+
+               // indicate that the chile should not be garbage collected while
+               // the owner still lives
+               internal static T Referenced<T> (T owner, SKObject child)
+                       where T : SKObject
+               {
+                       if (child != null && owner != null)
+                               owner.KeepAliveObjects[child.Handle] = child;
+
+                       return owner;
+               }
+
+               internal static int SizeOf<T> () =>
+                       Marshal.SizeOf(typeof(T));
+
+               internal static T PtrToStructure<T> (IntPtr intPtr) =>
+                       (T)Marshal.PtrToStructure(intPtr, typeof(T));
+
+               internal static T[] PtrToStructureArray<T> (IntPtr intPtr, int count)
+               {
+                       var items = new T[count];
+                       var size = SizeOf<T> ();
+                       for (var i = 0; i < count; i++) {
+                               var newPtr = new IntPtr (intPtr.ToInt64 () + (i * size));
+                               items[i] = PtrToStructure<T> (newPtr);
+                       }
+                       return items;
+               }
+
+               internal static T PtrToStructure<T> (IntPtr intPtr, int index)
+               {
+                       var size = SizeOf<T> ();
+                       var newPtr = new IntPtr (intPtr.ToInt64 () + (index * size));
+                       return PtrToStructure<T> (newPtr);
+               }
+       }
+
+       public abstract class SKNativeObject : IDisposable
+       {
+               internal bool fromFinalizer = false;
+
+               private int isDisposed = 0;
+
+               internal SKNativeObject (IntPtr handle)
+                       : this (handle, true)
+               {
+               }
+
+               internal SKNativeObject (IntPtr handle, bool ownsHandle)
+               {
+                       Handle = handle;
+                       OwnsHandle = ownsHandle;
+               }
+
+               ~SKNativeObject ()
+               {
+                       fromFinalizer = true;
+
+                       Dispose (false);
+               }
+
+               public virtual IntPtr Handle { get; protected set; }
+
+               protected internal virtual bool OwnsHandle { get; protected set; }
+
+               protected internal bool IgnorePublicDispose { get; protected set; }
+
+               protected internal bool IsDisposed => isDisposed == 1;
+
+               protected virtual void DisposeManaged ()
+               {
+                       // dispose of any managed resources
+               }
+
+               protected virtual void DisposeNative ()
+               {
+                       // dispose of any unmanaged resources
+               }
+
+               protected virtual void Dispose (bool disposing)
+               {
+                       if (Interlocked.CompareExchange (ref isDisposed, 1, 0) != 0)
+                               return;
+
+                       if (Handle != IntPtr.Zero && OwnsHandle)
+                               DisposeNative ();
+
+                       if (disposing)
+                               DisposeManaged ();
+
+                       Handle = IntPtr.Zero;
+               }
+
+               public void Dispose ()
+               {
+                       if (IgnorePublicDispose)
+                               return;
+
+                       DisposeInternal ();
+               }
+
+               protected internal void DisposeInternal ()
+               {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+       }
+
+       internal static class SKObjectExtensions
+       {
+               public static bool IsUnique (this IntPtr handle, bool isVirtual)
+               {
+                       if (isVirtual)
+                               return SkiaApi.sk_refcnt_unique (handle);
+                       else
+                               return SkiaApi.sk_nvrefcnt_unique (handle);
+               }
+
+               public static int GetReferenceCount (this IntPtr handle, bool isVirtual)
+               {
+                       if (isVirtual)
+                               return SkiaApi.sk_refcnt_get_ref_count (handle);
+                       else
+                               return SkiaApi.sk_nvrefcnt_get_ref_count (handle);
+               }
+
+               public static void SafeRef (this ISKReferenceCounted obj)
+               {
+                       if (obj is ISKNonVirtualReferenceCounted nvrefcnt)
+                               nvrefcnt.ReferenceNative ();
+                       else
+                               SkiaApi.sk_refcnt_safe_unref(obj.Handle);
+               }
+
+               public static void SafeUnRef (this ISKReferenceCounted obj)
+               {
+                       if (obj is ISKNonVirtualReferenceCounted nvrefcnt)
+                               nvrefcnt.UnreferenceNative ();
+                       else
+                               SkiaApi.sk_refcnt_safe_unref(obj.Handle);
+               }
+
+               public static int GetReferenceCount (this ISKReferenceCounted obj)
+               {
+                       if (obj is ISKNonVirtualReferenceCounted)
+                               return SkiaApi.sk_nvrefcnt_get_ref_count (obj.Handle);
+                       else
+                               return SkiaApi.sk_refcnt_get_ref_count (obj.Handle);
+               }
+       }
+
+       internal interface ISKReferenceCounted
+       {
+               IntPtr Handle { get; }
+       }
+
+       internal interface ISKNonVirtualReferenceCounted : ISKReferenceCounted
+       {
+               void ReferenceNative ();
+
+               void UnreferenceNative ();
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKOverdrawCanvas.cs b/src/XSF/SkiaSharp/SKOverdrawCanvas.cs
new file mode 100644 (file)
index 0000000..7e5b61a
--- /dev/null
@@ -0,0 +1,22 @@
+using System;
+
+namespace SkiaSharp
+{
+       public class SKOverdrawCanvas : SKNWayCanvas
+       {
+               [Preserve]
+               internal SKOverdrawCanvas (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKOverdrawCanvas (SKCanvas canvas)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException (nameof (canvas));
+
+                       Handle = SkiaApi.sk_overdraw_canvas_new (canvas.Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPMColor.cs b/src/XSF/SkiaSharp/SKPMColor.cs
new file mode 100644 (file)
index 0000000..4998327
--- /dev/null
@@ -0,0 +1,79 @@
+using System;
+
+namespace SkiaSharp
+{
+       public readonly unsafe struct SKPMColor : IEquatable<SKPMColor>
+       {
+               private readonly uint color;
+
+               public SKPMColor (uint value)
+               {
+                       color = value;
+               }
+
+               public readonly byte Alpha => (byte)((color >> SKImageInfo.PlatformColorAlphaShift) & 0xff);
+               public readonly byte Red => (byte)((color >> SKImageInfo.PlatformColorRedShift) & 0xff);
+               public readonly byte Green => (byte)((color >> SKImageInfo.PlatformColorGreenShift) & 0xff);
+               public readonly byte Blue => (byte)((color >> SKImageInfo.PlatformColorBlueShift) & 0xff);
+
+               // PreMultiply
+
+               public static SKPMColor PreMultiply (SKColor color) =>
+                       SkiaApi.sk_color_premultiply ((uint)color);
+
+               public static SKPMColor[] PreMultiply (SKColor[] colors)
+               {
+                       var pmcolors = new SKPMColor[colors.Length];
+                       fixed (SKColor* c = colors)
+                       fixed (SKPMColor* pm = pmcolors) {
+                               SkiaApi.sk_color_premultiply_array ((uint*)c, colors.Length, (uint*)pm);
+                       }
+                       return pmcolors;
+               }
+
+               // UnPreMultiply
+
+               public static SKColor UnPreMultiply (SKPMColor pmcolor) =>
+                       SkiaApi.sk_color_unpremultiply ((uint)pmcolor);
+
+               public static SKColor[] UnPreMultiply (SKPMColor[] pmcolors)
+               {
+                       var colors = new SKColor[pmcolors.Length];
+                       fixed (SKColor* c = colors)
+                       fixed (SKPMColor* pm = pmcolors) {
+                               SkiaApi.sk_color_unpremultiply_array ((uint*)pm, pmcolors.Length, (uint*)c);
+                       }
+                       return colors;
+               }
+
+               public static explicit operator SKPMColor (SKColor color) =>
+                       SKPMColor.PreMultiply (color);
+
+               public static explicit operator SKColor (SKPMColor color) =>
+                       SKPMColor.UnPreMultiply (color);
+
+               public readonly override string ToString () =>
+                       $"#{Alpha:x2}{Red:x2}{Green:x2}{Blue:x2}";
+
+               public readonly bool Equals (SKPMColor obj) =>
+                       obj.color == color;
+
+               public readonly override bool Equals (object other) =>
+                       other is SKPMColor f && Equals (f);
+
+               public static bool operator == (SKPMColor left, SKPMColor right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKPMColor left, SKPMColor right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode () =>
+                       color.GetHashCode ();
+
+               public static implicit operator SKPMColor (uint color) =>
+                       new SKPMColor (color);
+
+               public static explicit operator uint (SKPMColor color) =>
+                       color.color;
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPaint.cs b/src/XSF/SkiaSharp/SKPaint.cs
new file mode 100644 (file)
index 0000000..839dd51
--- /dev/null
@@ -0,0 +1,819 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPaint : SKObject
+       {
+               [Preserve]
+               internal SKPaint (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKPaint ()
+                       : this (SkiaApi.sk_paint_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKPaint instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_paint_delete (Handle);
+
+               // Reset
+
+               public void Reset ()
+               {
+                       SkiaApi.sk_paint_reset (Handle);
+               }
+
+               // properties
+
+               public bool IsAntialias {
+                       get => SkiaApi.sk_paint_is_antialias (Handle);
+                       set => SkiaApi.sk_paint_set_antialias (Handle, value);
+               }
+
+               public bool IsDither {
+                       get => SkiaApi.sk_paint_is_dither (Handle);
+                       set => SkiaApi.sk_paint_set_dither (Handle, value);
+               }
+
+               public bool IsVerticalText {
+                       get => SkiaApi.sk_paint_is_verticaltext (Handle);
+                       set => SkiaApi.sk_paint_set_verticaltext (Handle, value);
+               }
+
+               public bool IsLinearText {
+                       get => SkiaApi.sk_paint_is_linear_text (Handle);
+                       set => SkiaApi.sk_paint_set_linear_text (Handle, value);
+               }
+
+               public bool SubpixelText {
+                       get => SkiaApi.sk_paint_is_subpixel_text (Handle);
+                       set => SkiaApi.sk_paint_set_subpixel_text (Handle, value);
+               }
+
+               public bool LcdRenderText {
+                       get => SkiaApi.sk_paint_is_lcd_render_text (Handle);
+                       set => SkiaApi.sk_paint_set_lcd_render_text (Handle, value);
+               }
+
+               public bool IsEmbeddedBitmapText {
+                       get => SkiaApi.sk_paint_is_embedded_bitmap_text (Handle);
+                       set => SkiaApi.sk_paint_set_embedded_bitmap_text (Handle, value);
+               }
+
+               public bool IsAutohinted {
+                       get => SkiaApi.sk_paint_is_autohinted (Handle);
+                       set => SkiaApi.sk_paint_set_autohinted (Handle, value);
+               }
+
+               public SKPaintHinting HintingLevel {
+                       get => SkiaApi.sk_paint_get_hinting (Handle);
+                       set => SkiaApi.sk_paint_set_hinting (Handle, value);
+               }
+
+               public bool FakeBoldText {
+                       get => SkiaApi.sk_paint_is_fake_bold_text (Handle);
+                       set => SkiaApi.sk_paint_set_fake_bold_text (Handle, value);
+               }
+
+               public bool DeviceKerningEnabled {
+                       get => SkiaApi.sk_paint_is_dev_kern_text (Handle);
+                       set => SkiaApi.sk_paint_set_dev_kern_text (Handle, value);
+               }
+
+               public bool IsStroke {
+                       get => Style != SKPaintStyle.Fill;
+                       set => Style = value ? SKPaintStyle.Stroke : SKPaintStyle.Fill;
+               }
+
+               public SKPaintStyle Style {
+                       get => SkiaApi.sk_paint_get_style (Handle);
+                       set => SkiaApi.sk_paint_set_style (Handle, value);
+               }
+
+               public SKColor Color {
+                       get => SkiaApi.sk_paint_get_color (Handle);
+                       set => SkiaApi.sk_paint_set_color (Handle, (uint)value);
+               }
+
+               public float StrokeWidth {
+                       get => SkiaApi.sk_paint_get_stroke_width (Handle);
+                       set => SkiaApi.sk_paint_set_stroke_width (Handle, value);
+               }
+
+               public float StrokeMiter {
+                       get => SkiaApi.sk_paint_get_stroke_miter (Handle);
+                       set => SkiaApi.sk_paint_set_stroke_miter (Handle, value);
+               }
+
+               public SKStrokeCap StrokeCap {
+                       get => SkiaApi.sk_paint_get_stroke_cap (Handle);
+                       set => SkiaApi.sk_paint_set_stroke_cap (Handle, value);
+               }
+
+               public SKStrokeJoin StrokeJoin {
+                       get => SkiaApi.sk_paint_get_stroke_join (Handle);
+                       set => SkiaApi.sk_paint_set_stroke_join (Handle, value);
+               }
+
+               public SKShader Shader {
+                       get => GetObject<SKShader> (SkiaApi.sk_paint_get_shader (Handle));
+                       set => SkiaApi.sk_paint_set_shader (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKMaskFilter MaskFilter {
+                       get => GetObject<SKMaskFilter> (SkiaApi.sk_paint_get_maskfilter (Handle));
+                       set => SkiaApi.sk_paint_set_maskfilter (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKColorFilter ColorFilter {
+                       get => GetObject<SKColorFilter> (SkiaApi.sk_paint_get_colorfilter (Handle));
+                       set => SkiaApi.sk_paint_set_colorfilter (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKImageFilter ImageFilter {
+                       get => GetObject<SKImageFilter> (SkiaApi.sk_paint_get_imagefilter (Handle));
+                       set => SkiaApi.sk_paint_set_imagefilter (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public SKBlendMode BlendMode {
+                       get => SkiaApi.sk_paint_get_blendmode (Handle);
+                       set => SkiaApi.sk_paint_set_blendmode (Handle, value);
+               }
+
+               public SKFilterQuality FilterQuality {
+                       get => SkiaApi.sk_paint_get_filter_quality (Handle);
+                       set => SkiaApi.sk_paint_set_filter_quality (Handle, value);
+               }
+
+               public SKTypeface Typeface {
+                       get => GetObject<SKTypeface> (SkiaApi.sk_paint_get_typeface (Handle));
+                       set => SkiaApi.sk_paint_set_typeface (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               public float TextSize {
+                       get => SkiaApi.sk_paint_get_textsize (Handle);
+                       set => SkiaApi.sk_paint_set_textsize (Handle, value);
+               }
+
+               public SKTextAlign TextAlign {
+                       get => SkiaApi.sk_paint_get_text_align (Handle);
+                       set => SkiaApi.sk_paint_set_text_align (Handle, value);
+               }
+
+               public SKTextEncoding TextEncoding {
+                       get => SkiaApi.sk_paint_get_text_encoding (Handle);
+                       set => SkiaApi.sk_paint_set_text_encoding (Handle, value);
+               }
+
+               public float TextScaleX {
+                       get => SkiaApi.sk_paint_get_text_scale_x (Handle);
+                       set => SkiaApi.sk_paint_set_text_scale_x (Handle, value);
+               }
+
+               public float TextSkewX {
+                       get => SkiaApi.sk_paint_get_text_skew_x (Handle);
+                       set => SkiaApi.sk_paint_set_text_skew_x (Handle, value);
+               }
+
+               public SKPathEffect PathEffect {
+                       get => GetObject<SKPathEffect> (SkiaApi.sk_paint_get_path_effect (Handle));
+                       set => SkiaApi.sk_paint_set_path_effect (Handle, value == null ? IntPtr.Zero : value.Handle);
+               }
+
+               // FontSpacing
+
+               public float FontSpacing =>
+                       SkiaApi.sk_paint_get_fontmetrics (Handle, null, 0);
+
+               // FontMetrics
+
+               public SKFontMetrics FontMetrics {
+                       get {
+                               GetFontMetrics (out var metrics);
+                               return metrics;
+                       }
+               }
+
+               public float GetFontMetrics (out SKFontMetrics metrics, float scale = 0f)
+               {
+                       fixed (SKFontMetrics* m = &metrics) {
+                               return SkiaApi.sk_paint_get_fontmetrics (Handle, m, scale);
+                       }
+               }
+
+               // Clone
+
+               public SKPaint Clone () =>
+                       GetObject<SKPaint> (SkiaApi.sk_paint_clone (Handle));
+
+               // MeasureText
+
+               public float MeasureText (string text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return MeasureText (bytes);
+               }
+
+               public float MeasureText (byte[] text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* t = text) {
+                               return MeasureText ((IntPtr)t, (IntPtr)text.Length);
+                       }
+               }
+
+               public float MeasureText (IntPtr buffer, int length) =>
+                       MeasureText (buffer, (IntPtr)length);
+
+               public float MeasureText (IntPtr buffer, IntPtr length)
+               {
+                       if (buffer == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       return SkiaApi.sk_paint_measure_text (Handle, (void*)buffer, length, null);
+               }
+
+               public float MeasureText (string text, ref SKRect bounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return MeasureText (bytes, ref bounds);
+               }
+
+               public float MeasureText (byte[] text, ref SKRect bounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* t = text) {
+                               return MeasureText ((IntPtr)t, (IntPtr)text.Length, ref bounds);
+                       }
+               }
+
+               public float MeasureText (IntPtr buffer, int length, ref SKRect bounds) =>
+                       MeasureText (buffer, (IntPtr)length, ref bounds);
+
+               public float MeasureText (IntPtr buffer, IntPtr length, ref SKRect bounds)
+               {
+                       if (buffer == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       fixed (SKRect* b = &bounds) {
+                               return SkiaApi.sk_paint_measure_text (Handle, (void*)buffer, length, b);
+                       }
+               }
+
+               // BreakText
+
+               public long BreakText (string text, float maxWidth) =>
+                       BreakText (text, maxWidth, out _, out _);
+
+               public long BreakText (string text, float maxWidth, out float measuredWidth) =>
+                       BreakText (text, maxWidth, out measuredWidth, out _);
+
+               public long BreakText (string text, float maxWidth, out float measuredWidth, out string measuredText)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       var byteLength = (int)BreakText (bytes, maxWidth, out measuredWidth);
+                       if (byteLength == 0) {
+                               measuredText = string.Empty;
+                               return 0;
+                       }
+                       if (byteLength == bytes.Length) {
+                               measuredText = text;
+                               return text.Length;
+                       }
+                       measuredText = StringUtilities.GetString (bytes, 0, byteLength, TextEncoding);
+                       return measuredText.Length;
+               }
+
+               public long BreakText (byte[] text, float maxWidth) =>
+                       BreakText (text, maxWidth, out _);
+
+               public long BreakText (byte[] text, float maxWidth, out float measuredWidth)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* t = text) {
+                               return BreakText ((IntPtr)t, (IntPtr)text.Length, maxWidth, out measuredWidth);
+                       }
+               }
+
+               public long BreakText (IntPtr buffer, int length, float maxWidth) =>
+                       BreakText (buffer, (IntPtr)length, maxWidth, out _);
+
+               public long BreakText (IntPtr buffer, IntPtr length, float maxWidth) =>
+                       BreakText (buffer, length, maxWidth, out _);
+
+               public long BreakText (IntPtr buffer, int length, float maxWidth, out float measuredWidth) =>
+                       BreakText (buffer, (IntPtr)length, maxWidth, out measuredWidth);
+
+               public long BreakText (IntPtr buffer, IntPtr length, float maxWidth, out float measuredWidth)
+               {
+                       if (buffer == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       fixed (float* mw = &measuredWidth) {
+                               return (long)SkiaApi.sk_paint_break_text (Handle, (void*)buffer, length, maxWidth, mw);
+                       }
+               }
+
+               // GetTextPath
+
+               public SKPath GetTextPath (string text, float x, float y)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetTextPath (bytes, x, y);
+               }
+
+               public SKPath GetTextPath (byte[] text, float x, float y)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* t = text) {
+                               return GetTextPath ((IntPtr)t, (IntPtr)text.Length, x, y);
+                       }
+               }
+
+               public SKPath GetTextPath (IntPtr buffer, int length, float x, float y) =>
+                       GetTextPath (buffer, (IntPtr)length, x, y);
+
+               public SKPath GetTextPath (IntPtr buffer, IntPtr length, float x, float y)
+               {
+                       if (buffer == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       return GetObject<SKPath> (SkiaApi.sk_paint_get_text_path (Handle, (void*)buffer, length, x, y));
+               }
+
+               public SKPath GetTextPath (string text, SKPoint[] points)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetTextPath (bytes, points);
+               }
+
+               public SKPath GetTextPath (byte[] text, SKPoint[] points)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* t = text) {
+                               return GetTextPath ((IntPtr)t, (IntPtr)text.Length, points);
+                       }
+               }
+
+               public SKPath GetTextPath (IntPtr buffer, int length, SKPoint[] points) =>
+                       GetTextPath (buffer, (IntPtr)length, points);
+
+               public SKPath GetTextPath (IntPtr buffer, IntPtr length, SKPoint[] points)
+               {
+                       if (buffer == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (buffer));
+
+                       fixed (SKPoint* p = points) {
+                               return GetObject<SKPath> (SkiaApi.sk_paint_get_pos_text_path (Handle, (void*)buffer, length, p));
+                       }
+               }
+
+               // GetFillPath
+
+               public SKPath GetFillPath (SKPath src)
+                       => GetFillPath (src, 1f);
+
+               public SKPath GetFillPath (SKPath src, float resScale)
+               {
+                       var dst = new SKPath ();
+
+                       if (GetFillPath (src, dst, resScale)) {
+                               return dst;
+                       } else {
+                               dst.Dispose ();
+                               return null;
+                       }
+               }
+
+               public SKPath GetFillPath (SKPath src, SKRect cullRect)
+                       => GetFillPath (src, cullRect, 1f);
+
+               public SKPath GetFillPath (SKPath src, SKRect cullRect, float resScale)
+               {
+                       var dst = new SKPath ();
+
+                       if (GetFillPath (src, dst, cullRect, resScale)) {
+                               return dst;
+                       } else {
+                               dst.Dispose ();
+                               return null;
+                       }
+               }
+
+               public bool GetFillPath (SKPath src, SKPath dst)
+                       => GetFillPath (src, dst, 1f);
+
+               public bool GetFillPath (SKPath src, SKPath dst, float resScale)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_paint_get_fill_path (Handle, src.Handle, dst.Handle, null, resScale);
+               }
+
+               public bool GetFillPath (SKPath src, SKPath dst, SKRect cullRect)
+                       => GetFillPath (src, dst, cullRect, 1f);
+
+               public bool GetFillPath (SKPath src, SKPath dst, SKRect cullRect, float resScale)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_paint_get_fill_path (Handle, src.Handle, dst.Handle, &cullRect, resScale);
+               }
+
+               // CountGlyphs
+
+               public int CountGlyphs (string text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return CountGlyphs (bytes);
+               }
+
+               public int CountGlyphs (byte[] text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return CountGlyphs ((IntPtr)p, (IntPtr)text.Length);
+                       }
+               }
+
+               public int CountGlyphs (IntPtr text, int length) =>
+                       CountGlyphs (text, (IntPtr)length);
+
+               public int CountGlyphs (IntPtr text, IntPtr length)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       return SkiaApi.sk_paint_count_text (Handle, (void*)text, length);
+               }
+
+               // GetGlyphs
+
+               public ushort[] GetGlyphs (string text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetGlyphs (bytes);
+               }
+
+               public ushort[] GetGlyphs (byte[] text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetGlyphs ((IntPtr)p, (IntPtr)text.Length);
+                       }
+               }
+
+               public ushort[] GetGlyphs (IntPtr text, int length) =>
+                       GetGlyphs (text, (IntPtr)length);
+
+               public ushort[] GetGlyphs (IntPtr text, IntPtr length)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var n = SkiaApi.sk_paint_text_to_glyphs (Handle, (void*)text, length, null);
+
+                       if (n <= 0) {
+                               return new ushort[0];
+                       }
+
+                       var glyphs = new ushort[n];
+                       fixed (ushort* gp = glyphs) {
+                               SkiaApi.sk_paint_text_to_glyphs (Handle, (void*)text, length, gp);
+                       }
+                       return glyphs;
+               }
+
+               // ContainsGlyphs
+
+               public bool ContainsGlyphs (string text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return ContainsGlyphs (bytes);
+               }
+
+               public bool ContainsGlyphs (byte[] text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return ContainsGlyphs ((IntPtr)p, (IntPtr)text.Length);
+                       }
+               }
+
+               public bool ContainsGlyphs (IntPtr text, int length) =>
+                       ContainsGlyphs (text, (IntPtr)length);
+
+               public bool ContainsGlyphs (IntPtr text, IntPtr length)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       return SkiaApi.sk_paint_contains_text (Handle, (void*)text, length);
+               }
+
+               // GetGlyphWidths
+
+               public float[] GetGlyphWidths (string text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetGlyphWidths (bytes);
+               }
+
+               public float[] GetGlyphWidths (byte[] text)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetGlyphWidths ((IntPtr)p, (IntPtr)text.Length);
+                       }
+               }
+
+               public float[] GetGlyphWidths (IntPtr text, int length) =>
+                       GetGlyphWidths (text, (IntPtr)length);
+
+               public float[] GetGlyphWidths (IntPtr text, IntPtr length)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var n = SkiaApi.sk_paint_get_text_widths (Handle, (void*)text, length, null, null);
+
+                       if (n <= 0) {
+                               return new float[0];
+                       }
+
+                       var widths = new float[n];
+                       fixed (float* wp = widths) {
+                               SkiaApi.sk_paint_get_text_widths (Handle, (void*)text, length, wp, null);
+                       }
+                       return widths;
+               }
+
+               public float[] GetGlyphWidths (string text, out SKRect[] bounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetGlyphWidths (bytes, out bounds);
+               }
+
+               public float[] GetGlyphWidths (byte[] text, out SKRect[] bounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetGlyphWidths ((IntPtr)p, (IntPtr)text.Length, out bounds);
+                       }
+               }
+
+               public float[] GetGlyphWidths (IntPtr text, int length, out SKRect[] bounds) =>
+                       GetGlyphWidths (text, (IntPtr)length, out bounds);
+
+               public float[] GetGlyphWidths (IntPtr text, IntPtr length, out SKRect[] bounds)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var n = SkiaApi.sk_paint_get_text_widths (Handle, (void*)text, length, null, null);
+
+                       if (n <= 0) {
+                               bounds = new SKRect[0];
+                               return new float[0];
+                       }
+
+                       var widths = new float[n];
+                       bounds = new SKRect[n];
+                       fixed (float* wp = widths)
+                       fixed (SKRect* bp = bounds) {
+                               SkiaApi.sk_paint_get_text_widths (Handle, (void*)text, length, wp, bp);
+                       }
+                       return widths;
+               }
+
+               // GetTextIntercepts
+
+               public float[] GetTextIntercepts (string text, float x, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetTextIntercepts (bytes, x, y, upperBounds, lowerBounds);
+               }
+
+               public float[] GetTextIntercepts (byte[] text, float x, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetTextIntercepts ((IntPtr)p, (IntPtr)text.Length, x, y, upperBounds, lowerBounds);
+                       }
+               }
+
+               public float[] GetTextIntercepts (IntPtr text, int length, float x, float y, float upperBounds, float lowerBounds) =>
+                       GetTextIntercepts (text, (IntPtr)length, x, y, upperBounds, lowerBounds);
+
+
+               public float[] GetTextIntercepts (IntPtr text, IntPtr length, float x, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bounds = new[] { upperBounds, lowerBounds };
+
+                       fixed (float* b = bounds) {
+                               var n = SkiaApi.sk_paint_get_text_intercepts (Handle, (void*)text, length, x, y, b, null);
+
+                               if (n <= 0) {
+                                       return new float[0];
+                               }
+
+                               var intervals = new float[n];
+                               fixed (float* ip = intervals) {
+                                       SkiaApi.sk_paint_get_text_intercepts (Handle, (void*)text, length, x, y, b, ip);
+                               }
+                               return intervals;
+                       }
+               }
+
+               // GetTextIntercepts (SKTextBlob)
+
+               public float[] GetTextIntercepts (SKTextBlob text, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bounds = new[] { upperBounds, lowerBounds };
+
+                       fixed (float* b = bounds) {
+                               var n = SkiaApi.sk_paint_get_pos_text_blob_intercepts (Handle, text.Handle, b, null);
+
+                               if (n <= 0) {
+                                       return new float[0];
+                               }
+
+                               var intervals = new float[n];
+                               fixed (float* ip = intervals) {
+                                       SkiaApi.sk_paint_get_pos_text_blob_intercepts (Handle, text.Handle, b, ip);
+                               }
+                               return intervals;
+                       }
+               }
+
+               // GetPositionedTextIntercepts
+
+               public float[] GetPositionedTextIntercepts (string text, SKPoint[] positions, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetPositionedTextIntercepts (bytes, positions, upperBounds, lowerBounds);
+               }
+
+               public float[] GetPositionedTextIntercepts (byte[] text, SKPoint[] positions, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetPositionedTextIntercepts ((IntPtr)p, (IntPtr)text.Length, positions, upperBounds, lowerBounds);
+                       }
+               }
+
+               public float[] GetPositionedTextIntercepts (IntPtr text, int length, SKPoint[] positions, float upperBounds, float lowerBounds) =>
+                       GetPositionedTextIntercepts (text, (IntPtr)length, positions, upperBounds, lowerBounds);
+
+
+               public float[] GetPositionedTextIntercepts (IntPtr text, IntPtr length, SKPoint[] positions, float upperBounds, float lowerBounds)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bounds = new[] { upperBounds, lowerBounds };
+
+                       fixed (float* b = bounds)
+                       fixed (SKPoint* p = positions) {
+                               var n = SkiaApi.sk_paint_get_pos_text_intercepts (Handle, (void*)text, length, p, b, null);
+
+                               if (n <= 0) {
+                                       return new float[0];
+                               }
+
+                               var intervals = new float[n];
+                               fixed (float* ip = intervals) {
+                                       SkiaApi.sk_paint_get_pos_text_intercepts (Handle, (void*)text, length, p, b, ip);
+                               }
+                               return intervals;
+                       }
+               }
+
+               // GetHorizontalTextIntercepts
+
+               public float[] GetHorizontalTextIntercepts (string text, float[] xpositions, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bytes = StringUtilities.GetEncodedText (text, TextEncoding);
+                       return GetHorizontalTextIntercepts (bytes, xpositions, y, upperBounds, lowerBounds);
+               }
+
+               public float[] GetHorizontalTextIntercepts (byte[] text, float[] xpositions, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == null)
+                               throw new ArgumentNullException (nameof (text));
+
+                       fixed (byte* p = text) {
+                               return GetHorizontalTextIntercepts ((IntPtr)p, (IntPtr)text.Length, xpositions, y, upperBounds, lowerBounds);
+                       }
+               }
+
+               public float[] GetHorizontalTextIntercepts (IntPtr text, int length, float[] xpositions, float y, float upperBounds, float lowerBounds) =>
+                       GetHorizontalTextIntercepts (text, (IntPtr)length, xpositions, y, upperBounds, lowerBounds);
+
+               public float[] GetHorizontalTextIntercepts (IntPtr text, IntPtr length, float[] xpositions, float y, float upperBounds, float lowerBounds)
+               {
+                       if (text == IntPtr.Zero && length != IntPtr.Zero)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var bounds = new[] { upperBounds, lowerBounds };
+
+                       fixed (float* x = xpositions)
+                       fixed (float* b = bounds) {
+                               var n = SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, (void*)text, length, x, y, b, null);
+
+                               if (n <= 0) {
+                                       return new float[0];
+                               }
+
+                               var intervals = new float[n];
+                               fixed (float* ip = intervals) {
+                                       SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, (void*)text, length, x, y, b, ip);
+                               }
+                               return intervals;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPath.cs b/src/XSF/SkiaSharp/SKPath.cs
new file mode 100644 (file)
index 0000000..741e4a6
--- /dev/null
@@ -0,0 +1,576 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPath : SKObject
+       {
+               [Preserve]
+               internal SKPath (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKPath ()
+                       : this (SkiaApi.sk_path_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKPath instance.");
+                       }
+               }
+
+               public SKPath (SKPath path)
+                       : this (SkiaApi.sk_path_clone (path.Handle), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to copy the SKPath instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_path_delete (Handle);
+
+               public SKPathFillType FillType {
+                       get => SkiaApi.sk_path_get_filltype (Handle);
+                       set => SkiaApi.sk_path_set_filltype (Handle, value);
+               }
+
+               public SKPathConvexity Convexity {
+                       get => SkiaApi.sk_path_get_convexity (Handle);
+                       set => SkiaApi.sk_path_set_convexity (Handle, value);
+               }
+
+               public bool IsConvex => Convexity == SKPathConvexity.Convex;
+
+               public bool IsConcave => Convexity == SKPathConvexity.Concave;
+
+               public bool IsEmpty => VerbCount == 0;
+
+               public bool IsOval => SkiaApi.sk_path_is_oval (Handle, null);
+
+               public bool IsRoundRect => SkiaApi.sk_path_is_rrect (Handle, IntPtr.Zero);
+
+               public bool IsLine => SkiaApi.sk_path_is_line (Handle, null);
+
+               public bool IsRect => SkiaApi.sk_path_is_rect (Handle, null, null, null);
+
+               public SKPathSegmentMask SegmentMasks => (SKPathSegmentMask)SkiaApi.sk_path_get_segment_masks (Handle);
+
+               public int VerbCount => SkiaApi.sk_path_count_verbs (Handle);
+
+               public int PointCount => SkiaApi.sk_path_count_points (Handle);
+
+               public SKPoint this[int index] => GetPoint (index);
+
+               public SKPoint[] Points => GetPoints (PointCount);
+
+               public SKPoint LastPoint {
+                       get {
+                               SKPoint point;
+                               SkiaApi.sk_path_get_last_point (Handle, &point);
+                               return point;
+                       }
+               }
+
+               public SKRect Bounds {
+                       get {
+                               SKRect rect;
+                               SkiaApi.sk_path_get_bounds (Handle, &rect);
+                               return rect;
+                       }
+               }
+
+               public SKRect TightBounds {
+                       get {
+                               if (GetTightBounds (out var rect)) {
+                                       return rect;
+                               } else {
+                                       return SKRect.Empty;
+                               }
+                       }
+               }
+
+               public SKRect GetOvalBounds ()
+               {
+                       SKRect bounds;
+                       if (SkiaApi.sk_path_is_oval (Handle, &bounds)) {
+                               return bounds;
+                       } else {
+                               return SKRect.Empty;
+                       }
+               }
+
+               public SKRoundRect GetRoundRect ()
+               {
+                       var rrect = new SKRoundRect ();
+                       var result = SkiaApi.sk_path_is_rrect (Handle, rrect.Handle);
+                       if (result) {
+                               return rrect;
+                       } else {
+                               rrect.Dispose ();
+                               return null;
+                       }
+               }
+
+               public SKPoint[] GetLine ()
+               {
+                       var temp = new SKPoint[2];
+                       fixed (SKPoint* t = temp) {
+                               var result = SkiaApi.sk_path_is_line (Handle, t);
+                               if (result) {
+                                       return temp;
+                               } else {
+                                       return null;
+                               }
+                       }
+               }
+
+               public SKRect GetRect () =>
+                       GetRect (out var isClosed, out var direction);
+
+               public SKRect GetRect (out bool isClosed, out SKPathDirection direction)
+               {
+                       byte c;
+                       fixed (SKPathDirection* d = &direction) {
+                               SKRect rect;
+                               var result = SkiaApi.sk_path_is_rect (Handle, &rect, &c, d);
+                               isClosed = c > 0;
+                               if (result) {
+                                       return rect;
+                               } else {
+                                       return SKRect.Empty;
+                               }
+                       }
+               }
+
+               public SKPoint GetPoint (int index)
+               {
+                       if (index < 0 || index >= PointCount)
+                               throw new ArgumentOutOfRangeException (nameof (index));
+
+                       SKPoint point;
+                       SkiaApi.sk_path_get_point (Handle, index, &point);
+                       return point;
+               }
+
+               public SKPoint[] GetPoints (int max)
+               {
+                       var points = new SKPoint[max];
+                       GetPoints (points, max);
+                       return points;
+               }
+
+               public int GetPoints (SKPoint[] points, int max)
+               {
+                       fixed (SKPoint* p = points) {
+                               return SkiaApi.sk_path_get_points (Handle, p, max);
+                       }
+               }
+
+               public bool Contains (float x, float y) =>
+                       SkiaApi.sk_path_contains (Handle, x, y);
+
+               public void Offset (SKPoint offset) =>
+                       Offset (offset.X, offset.Y);
+
+               public void Offset (float dx, float dy) =>
+                       Transform (SKMatrix.MakeTranslation (dx, dy));
+
+               public void MoveTo (SKPoint point) =>
+                       SkiaApi.sk_path_move_to (Handle, point.X, point.Y);
+
+               public void MoveTo (float x, float y) =>
+                       SkiaApi.sk_path_move_to (Handle, x, y);
+
+               public void RMoveTo (SKPoint point) =>
+                       SkiaApi.sk_path_rmove_to (Handle, point.X, point.Y);
+
+               public void RMoveTo (float dx, float dy) =>
+                       SkiaApi.sk_path_rmove_to (Handle, dx, dy);
+
+               public void LineTo (SKPoint point) =>
+                       SkiaApi.sk_path_line_to (Handle, point.X, point.Y);
+
+               public void LineTo (float x, float y) =>
+                       SkiaApi.sk_path_line_to (Handle, x, y);
+
+               public void RLineTo (SKPoint point) =>
+                       SkiaApi.sk_path_rline_to (Handle, point.X, point.Y);
+
+               public void RLineTo (float dx, float dy) =>
+                       SkiaApi.sk_path_rline_to (Handle, dx, dy);
+
+               public void QuadTo (SKPoint point0, SKPoint point1) =>
+                       SkiaApi.sk_path_quad_to (Handle, point0.X, point0.Y, point1.X, point1.Y);
+
+               public void QuadTo (float x0, float y0, float x1, float y1) =>
+                       SkiaApi.sk_path_quad_to (Handle, x0, y0, x1, y1);
+
+               public void RQuadTo (SKPoint point0, SKPoint point1) =>
+                       SkiaApi.sk_path_rquad_to (Handle, point0.X, point0.Y, point1.X, point1.Y);
+
+               public void RQuadTo (float dx0, float dy0, float dx1, float dy1) =>
+                       SkiaApi.sk_path_rquad_to (Handle, dx0, dy0, dx1, dy1);
+
+               public void ConicTo (SKPoint point0, SKPoint point1, float w) =>
+                       SkiaApi.sk_path_conic_to (Handle, point0.X, point0.Y, point1.X, point1.Y, w);
+
+               public void ConicTo (float x0, float y0, float x1, float y1, float w) =>
+                       SkiaApi.sk_path_conic_to (Handle, x0, y0, x1, y1, w);
+
+               public void RConicTo (SKPoint point0, SKPoint point1, float w) =>
+                       SkiaApi.sk_path_rconic_to (Handle, point0.X, point0.Y, point1.X, point1.Y, w);
+
+               public void RConicTo (float dx0, float dy0, float dx1, float dy1, float w) =>
+                       SkiaApi.sk_path_rconic_to (Handle, dx0, dy0, dx1, dy1, w);
+
+               public void CubicTo (SKPoint point0, SKPoint point1, SKPoint point2) =>
+                       SkiaApi.sk_path_cubic_to (Handle, point0.X, point0.Y, point1.X, point1.Y, point2.X, point2.Y);
+
+               public void CubicTo (float x0, float y0, float x1, float y1, float x2, float y2) =>
+                       SkiaApi.sk_path_cubic_to (Handle, x0, y0, x1, y1, x2, y2);
+
+               public void RCubicTo (SKPoint point0, SKPoint point1, SKPoint point2) =>
+                       SkiaApi.sk_path_rcubic_to (Handle, point0.X, point0.Y, point1.X, point1.Y, point2.X, point2.Y);
+
+               public void RCubicTo (float dx0, float dy0, float dx1, float dy1, float dx2, float dy2) =>
+                       SkiaApi.sk_path_rcubic_to (Handle, dx0, dy0, dx1, dy1, dx2, dy2);
+
+               public void ArcTo (SKPoint r, float xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, SKPoint xy) =>
+                       SkiaApi.sk_path_arc_to (Handle, r.X, r.Y, xAxisRotate, largeArc, sweep, xy.X, xy.Y);
+
+               public void ArcTo (float rx, float ry, float xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, float x, float y) =>
+                       SkiaApi.sk_path_arc_to (Handle, rx, ry, xAxisRotate, largeArc, sweep, x, y);
+
+               public void ArcTo (SKRect oval, float startAngle, float sweepAngle, bool forceMoveTo) =>
+                       SkiaApi.sk_path_arc_to_with_oval (Handle, &oval, startAngle, sweepAngle, forceMoveTo);
+
+               public void ArcTo (SKPoint point1, SKPoint point2, float radius) =>
+                       SkiaApi.sk_path_arc_to_with_points (Handle, point1.X, point1.Y, point2.X, point2.Y, radius);
+
+               public void ArcTo (float x1, float y1, float x2, float y2, float radius) =>
+                       SkiaApi.sk_path_arc_to_with_points (Handle, x1, y1, x2, y2, radius);
+
+               public void RArcTo (SKPoint r, float xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, SKPoint xy) =>
+                       SkiaApi.sk_path_rarc_to (Handle, r.X, r.Y, xAxisRotate, largeArc, sweep, xy.X, xy.Y);
+
+               public void RArcTo (float rx, float ry, float xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, float x, float y) =>
+                       SkiaApi.sk_path_rarc_to (Handle, rx, ry, xAxisRotate, largeArc, sweep, x, y);
+
+               public void Close () =>
+                       SkiaApi.sk_path_close (Handle);
+
+               public void Rewind () =>
+                       SkiaApi.sk_path_rewind (Handle);
+
+               public void Reset () =>
+                       SkiaApi.sk_path_reset (Handle);
+
+               public void AddRect (SKRect rect, SKPathDirection direction = SKPathDirection.Clockwise) =>
+                       SkiaApi.sk_path_add_rect (Handle, &rect, direction);
+
+               public void AddRect (SKRect rect, SKPathDirection direction, uint startIndex)
+               {
+                       if (startIndex > 3)
+                               throw new ArgumentOutOfRangeException (nameof (startIndex), "Starting index must be in the range of 0..3 (inclusive).");
+
+                       SkiaApi.sk_path_add_rect_start (Handle, &rect, direction, startIndex);
+               }
+
+               public void AddRoundRect (SKRoundRect rect, SKPathDirection direction = SKPathDirection.Clockwise)
+               {
+                       if (rect == null)
+                               throw new ArgumentNullException (nameof (rect));
+                       SkiaApi.sk_path_add_rrect (Handle, rect.Handle, direction);
+               }
+
+               public void AddRoundRect (SKRoundRect rect, SKPathDirection direction, uint startIndex)
+               {
+                       if (rect == null)
+                               throw new ArgumentNullException (nameof (rect));
+                       SkiaApi.sk_path_add_rrect_start (Handle, rect.Handle, direction, startIndex);
+               }
+
+               public void AddOval (SKRect rect, SKPathDirection direction = SKPathDirection.Clockwise) =>
+                       SkiaApi.sk_path_add_oval (Handle, &rect, direction);
+
+               public void AddArc (SKRect oval, float startAngle, float sweepAngle) =>
+                       SkiaApi.sk_path_add_arc (Handle, &oval, startAngle, sweepAngle);
+
+               public bool GetBounds (out SKRect rect)
+               {
+                       var isEmpty = IsEmpty;
+                       if (isEmpty) {
+                               rect = SKRect.Empty;
+                       } else {
+                               fixed (SKRect* r = &rect) {
+                                       SkiaApi.sk_path_get_bounds (Handle, r);
+                               }
+                       }
+                       return !isEmpty;
+               }
+
+               public SKRect ComputeTightBounds ()
+               {
+                       SKRect rect;
+                       SkiaApi.sk_path_compute_tight_bounds (Handle, &rect);
+                       return rect;
+               }
+
+               public void Transform (SKMatrix matrix) =>
+                       SkiaApi.sk_path_transform (Handle, &matrix);
+
+               public void Transform (SKMatrix matrix, SKPath destination)
+               {
+                       if (destination == null)
+                               throw new ArgumentNullException (nameof (destination));
+
+                       SkiaApi.sk_path_transform_to_dest (Handle, &matrix, destination.Handle);
+               }
+
+               public void AddPath (SKPath other, float dx, float dy, SKPathAddMode mode = SKPathAddMode.Append)
+               {
+                       if (other == null)
+                               throw new ArgumentNullException (nameof (other));
+
+                       SkiaApi.sk_path_add_path_offset (Handle, other.Handle, dx, dy, mode);
+               }
+
+               public void AddPath (SKPath other, ref SKMatrix matrix, SKPathAddMode mode = SKPathAddMode.Append)
+               {
+                       if (other == null)
+                               throw new ArgumentNullException (nameof (other));
+
+                       fixed (SKMatrix* m = &matrix) {
+                               SkiaApi.sk_path_add_path_matrix (Handle, other.Handle, m, mode);
+                       }
+               }
+
+               public void AddPath (SKPath other, SKPathAddMode mode = SKPathAddMode.Append)
+               {
+                       if (other == null)
+                               throw new ArgumentNullException (nameof (other));
+
+                       SkiaApi.sk_path_add_path (Handle, other.Handle, mode);
+               }
+
+               public void AddPathReverse (SKPath other)
+               {
+                       if (other == null)
+                               throw new ArgumentNullException (nameof (other));
+
+                       SkiaApi.sk_path_add_path_reverse (Handle, other.Handle);
+               }
+
+               public void AddRoundRect (SKRect rect, float rx, float ry, SKPathDirection dir = SKPathDirection.Clockwise) =>
+                       SkiaApi.sk_path_add_rounded_rect (Handle, &rect, rx, ry, dir);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use AddRoundRect instead.")]
+               public void AddRoundedRect (SKRect rect, float rx, float ry, SKPathDirection dir = SKPathDirection.Clockwise) =>
+                       AddRoundRect (rect, rx, ry, dir);
+
+               public void AddCircle (float x, float y, float radius, SKPathDirection dir = SKPathDirection.Clockwise) =>
+                       SkiaApi.sk_path_add_circle (Handle, x, y, radius, dir);
+
+               public void AddPoly (SKPoint[] points, bool close = true)
+               {
+                       if (points == null)
+                               throw new ArgumentNullException (nameof (points));
+                       fixed (SKPoint* p = points) {
+                               SkiaApi.sk_path_add_poly (Handle, p, points.Length, close);
+                       }
+               }
+
+               public Iterator CreateIterator (bool forceClose) =>
+                       new Iterator (this, forceClose);
+
+               public RawIterator CreateRawIterator () =>
+                       new RawIterator (this);
+
+               public bool Op (SKPath other, SKPathOp op, SKPath result)
+               {
+                       if (other == null)
+                               throw new ArgumentNullException (nameof (other));
+                       if (result == null)
+                               throw new ArgumentNullException (nameof (result));
+
+                       return SkiaApi.sk_pathop_op (Handle, other.Handle, op, result.Handle);
+               }
+
+               public SKPath Op (SKPath other, SKPathOp op)
+               {
+                       var result = new SKPath ();
+                       if (Op (other, op, result)) {
+                               return result;
+                       } else {
+                               result.Dispose ();
+                               return null;
+                       }
+               }
+
+               public bool Simplify (SKPath result)
+               {
+                       if (result == null)
+                               throw new ArgumentNullException (nameof (result));
+
+                       return SkiaApi.sk_pathop_simplify (Handle, result.Handle);
+               }
+
+               public SKPath Simplify ()
+               {
+                       var result = new SKPath ();
+                       if (Simplify (result)) {
+                               return result;
+                       } else {
+                               result.Dispose ();
+                               return null;
+                       }
+               }
+
+               public bool GetTightBounds (out SKRect result)
+               {
+                       fixed (SKRect* r = &result) {
+                               return SkiaApi.sk_pathop_tight_bounds (Handle, r);
+                       }
+               }
+
+               public string ToSvgPathData ()
+               {
+                       using (var str = new SKString ()) {
+                               SkiaApi.sk_path_to_svg_string (Handle, str.Handle);
+                               return (string)str;
+                       }
+               }
+
+               public static SKPath ParseSvgPathData (string svgPath)
+               {
+                       var path = new SKPath ();
+                       var success = SkiaApi.sk_path_parse_svg_string (path.Handle, svgPath);
+                       if (!success) {
+                               path.Dispose ();
+                               path = null;
+                       }
+                       return path;
+               }
+
+               public static SKPoint[] ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float w, int pow2)
+               {
+                       ConvertConicToQuads (p0, p1, p2, w, out var pts, pow2);
+                       return pts;
+               }
+
+               public static int ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float w, out SKPoint[] pts, int pow2)
+               {
+                       var quadCount = 1 << pow2;
+                       var ptCount = 2 * quadCount + 1;
+                       pts = new SKPoint[ptCount];
+                       return ConvertConicToQuads (p0, p1, p2, w, pts, pow2);
+               }
+
+               public static int ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float w, SKPoint[] pts, int pow2)
+               {
+                       if (pts == null)
+                               throw new ArgumentNullException (nameof (pts));
+                       fixed (SKPoint* ptsptr = pts) {
+                               return SkiaApi.sk_path_convert_conic_to_quads (&p0, &p1, &p2, w, ptsptr, pow2);
+                       }
+               }
+
+               public class Iterator : SKObject
+               {
+                       private readonly SKPath path;
+
+                       internal Iterator (SKPath path, bool forceClose)
+                               : base (SkiaApi.sk_path_create_iter (path.Handle, forceClose ? 1 : 0), true)
+                       {
+                               this.path = path;
+                       }
+
+                       protected override void Dispose (bool disposing) =>
+                               base.Dispose (disposing);
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_path_iter_destroy (Handle);
+
+                       public SKPathVerb Next (SKPoint[] points, bool doConsumeDegenerates = true, bool exact = false)
+                       {
+                               if (points == null)
+                                       throw new ArgumentNullException (nameof (points));
+                               if (points.Length != 4)
+                                       throw new ArgumentException ("Must be an array of four elements.", nameof (points));
+                               fixed (SKPoint* p = points) {
+                                       return SkiaApi.sk_path_iter_next (Handle, p, doConsumeDegenerates ? 1 : 0, exact ? 1 : 0);
+                               }
+                       }
+
+                       public float ConicWeight () =>
+                               SkiaApi.sk_path_iter_conic_weight (Handle);
+                       public bool IsCloseLine () =>
+                               SkiaApi.sk_path_iter_is_close_line (Handle) != 0;
+                       public bool IsCloseContour () =>
+                               SkiaApi.sk_path_iter_is_closed_contour (Handle) != 0;
+               }
+
+               public class RawIterator : SKObject
+               {
+                       private readonly SKPath path;
+
+                       internal RawIterator (SKPath path)
+                               : base (SkiaApi.sk_path_create_rawiter (path.Handle), true)
+                       {
+                               this.path = path;
+                       }
+
+                       protected override void Dispose (bool disposing) =>
+                               base.Dispose (disposing);
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_path_rawiter_destroy (Handle);
+
+                       public SKPathVerb Next (SKPoint[] points)
+                       {
+                               if (points == null)
+                                       throw new ArgumentNullException (nameof (points));
+                               if (points.Length != 4)
+                                       throw new ArgumentException ("Must be an array of four elements.", nameof (points));
+                               fixed (SKPoint* p = points) {
+                                       return SkiaApi.sk_path_rawiter_next (Handle, p);
+                               }
+                       }
+
+                       public float ConicWeight () =>
+                               SkiaApi.sk_path_rawiter_conic_weight (Handle);
+                       public SKPathVerb Peek () =>
+                               SkiaApi.sk_path_rawiter_peek (Handle);
+               }
+
+               public class OpBuilder : SKObject
+               {
+                       public OpBuilder ()
+                               : base (SkiaApi.sk_opbuilder_new (), true)
+                       {
+                       }
+
+                       public void Add (SKPath path, SKPathOp op) =>
+                               SkiaApi.sk_opbuilder_add (Handle, path.Handle, op);
+
+                       public bool Resolve (SKPath result)
+                       {
+                               if (result == null)
+                                       throw new ArgumentNullException (nameof (result));
+
+                               return SkiaApi.sk_opbuilder_resolve (Handle, result.Handle);
+                       }
+
+                       protected override void Dispose (bool disposing) =>
+                               base.Dispose (disposing);
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_opbuilder_destroy (Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPathEffect.cs b/src/XSF/SkiaSharp/SKPathEffect.cs
new file mode 100644 (file)
index 0000000..b087b66
--- /dev/null
@@ -0,0 +1,85 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPathEffect : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal SKPathEffect (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static SKPathEffect CreateCompose(SKPathEffect outer, SKPathEffect inner)
+               {
+                       if (outer == null)
+                               throw new ArgumentNullException(nameof(outer));
+                       if (inner == null)
+                               throw new ArgumentNullException(nameof(inner));
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_compose(outer.Handle, inner.Handle));
+               }
+
+               public static SKPathEffect CreateSum(SKPathEffect first, SKPathEffect second)
+               {
+                       if (first == null)
+                               throw new ArgumentNullException(nameof(first));
+                       if (second == null)
+                               throw new ArgumentNullException(nameof(second));
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_sum(first.Handle, second.Handle));
+               }
+
+               public static SKPathEffect CreateDiscrete(float segLength, float deviation, UInt32 seedAssist = 0)
+               {
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_discrete(segLength, deviation, seedAssist));
+               }
+
+               public static SKPathEffect CreateCorner(float radius)
+               {
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_corner(radius));
+               }
+
+               public static SKPathEffect Create1DPath(SKPath path, float advance, float phase, SKPath1DPathEffectStyle style)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException(nameof(path));
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_1d_path(path.Handle, advance, phase, style));
+               }
+
+               public static SKPathEffect Create2DLine(float width, SKMatrix matrix)
+               {
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_2d_line(width, &matrix));
+               }
+
+               public static SKPathEffect Create2DPath(SKMatrix matrix, SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException(nameof(path));
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_2d_path(&matrix, path.Handle));
+               }
+
+               public static SKPathEffect CreateDash(float[] intervals, float phase)
+               {
+                       if (intervals == null)
+                               throw new ArgumentNullException(nameof(intervals));
+                       if (intervals.Length % 2 != 0)
+                               throw new ArgumentException("The intervals must have an even number of entries.", nameof(intervals));
+                       fixed (float* i = intervals) {
+                               return GetObject<SKPathEffect> (SkiaApi.sk_path_effect_create_dash (i, intervals.Length, phase));
+                       }
+               }
+
+               public static SKPathEffect CreateTrim(float start, float stop)
+               {
+                       return CreateTrim(start, stop, SKTrimPathEffectMode.Normal);
+               }
+
+               public static SKPathEffect CreateTrim(float start, float stop, SKTrimPathEffectMode mode)
+               {
+                       return GetObject<SKPathEffect>(SkiaApi.sk_path_effect_create_trim(start, stop, mode));
+               }
+       }
+}
+
diff --git a/src/XSF/SkiaSharp/SKPathMeasure.cs b/src/XSF/SkiaSharp/SKPathMeasure.cs
new file mode 100644 (file)
index 0000000..710fb5a
--- /dev/null
@@ -0,0 +1,148 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPathMeasure : SKObject
+       {
+               [Preserve]
+               internal SKPathMeasure (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKPathMeasure ()
+                       : this (SkiaApi.sk_pathmeasure_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKPathMeasure instance.");
+                       }
+               }
+
+               public SKPathMeasure (SKPath path, bool forceClosed = false, float resScale = 1)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       Handle = SkiaApi.sk_pathmeasure_new_with_path (path.Handle, forceClosed, resScale);
+
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKPathMeasure instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_pathmeasure_destroy (Handle);
+
+               // properties
+
+               public float Length {
+                       get {
+                               return SkiaApi.sk_pathmeasure_get_length (Handle);
+                       }
+               }
+
+               public bool IsClosed {
+                       get {
+                               return SkiaApi.sk_pathmeasure_is_closed (Handle);
+                       }
+               }
+
+               // SetPath
+
+               public void SetPath (SKPath path) =>
+                       SetPath (path, false);
+
+               public void SetPath (SKPath path, bool forceClosed)
+               {
+                       SkiaApi.sk_pathmeasure_set_path (Handle, path == null ? IntPtr.Zero : path.Handle, forceClosed);
+               }
+
+               // GetPositionAndTangent
+
+               public bool GetPositionAndTangent (float distance, out SKPoint position, out SKPoint tangent)
+               {
+                       fixed (SKPoint* p = &position)
+                       fixed (SKPoint* t = &tangent) {
+                               return SkiaApi.sk_pathmeasure_get_pos_tan (Handle, distance, p, t);
+                       }
+               }
+
+               // GetPosition
+
+               public SKPoint GetPosition (float distance)
+               {
+                       if (!GetPosition (distance, out var position))
+                               position = SKPoint.Empty;
+                       return position;
+               }
+
+               public bool GetPosition (float distance, out SKPoint position)
+               {
+                       fixed (SKPoint* p = &position) {
+                               return SkiaApi.sk_pathmeasure_get_pos_tan (Handle, distance, p, null);
+                       }
+               }
+
+               // GetTangent
+
+               public SKPoint GetTangent (float distance)
+               {
+                       if (!GetTangent (distance, out var tangent))
+                               tangent = SKPoint.Empty;
+                       return tangent;
+               }
+
+               public bool GetTangent (float distance, out SKPoint tangent)
+               {
+                       fixed (SKPoint* t = &tangent) {
+                               return SkiaApi.sk_pathmeasure_get_pos_tan (Handle, distance, null, t);
+                       }
+               }
+
+               // GetMatrix
+
+               public SKMatrix GetMatrix (float distance, SKPathMeasureMatrixFlags flags)
+               {
+                       if (!GetMatrix (distance, out var matrix, flags))
+                               matrix = SKMatrix.Empty;
+                       return matrix;
+               }
+
+               public bool GetMatrix (float distance, out SKMatrix matrix, SKPathMeasureMatrixFlags flags)
+               {
+                       fixed (SKMatrix* m = &matrix) {
+                               return SkiaApi.sk_pathmeasure_get_matrix (Handle, distance, m, flags);
+                       }
+               }
+
+               // GetSegment
+
+               public bool GetSegment (float start, float stop, SKPath dst, bool startWithMoveTo)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       return SkiaApi.sk_pathmeasure_get_segment (Handle, start, stop, dst.Handle, startWithMoveTo);
+               }
+
+               public SKPath GetSegment (float start, float stop, bool startWithMoveTo)
+               {
+                       var dst = new SKPath ();
+                       if (!GetSegment (start, stop, dst, startWithMoveTo)) {
+                               dst.Dispose ();
+                               dst = null;
+                       }
+                       return dst;
+               }
+
+               // NextContour
+
+               public bool NextContour ()
+               {
+                       return SkiaApi.sk_pathmeasure_next_contour (Handle);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPicture.cs b/src/XSF/SkiaSharp/SKPicture.cs
new file mode 100644 (file)
index 0000000..17bd0c0
--- /dev/null
@@ -0,0 +1,41 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPicture : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal SKPicture (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public uint UniqueId =>
+                       SkiaApi.sk_picture_get_unique_id (Handle);
+
+               public SKRect CullRect {
+                       get {
+                               SKRect rect;
+                               SkiaApi.sk_picture_get_cull_rect (Handle, &rect);
+                               return rect;
+                       }
+               }
+
+               // ToShader
+
+               public SKShader ToShader () =>
+                       ToShader (SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy) =>
+                       SKShader.CreatePicture (this, tmx, tmy);
+
+               public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKRect tile) =>
+                       SKShader.CreatePicture (this, tmx, tmy, tile);
+
+               public SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix, SKRect tile) =>
+                       SKShader.CreatePicture (this, tmx, tmy, localMatrix, tile);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPictureRecorder.cs b/src/XSF/SkiaSharp/SKPictureRecorder.cs
new file mode 100644 (file)
index 0000000..7948969
--- /dev/null
@@ -0,0 +1,45 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPictureRecorder : SKObject
+       {
+               [Preserve]
+               internal SKPictureRecorder (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKPictureRecorder ()
+                       : this (SkiaApi.sk_picture_recorder_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKPictureRecorder instance.");
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_picture_recorder_delete (Handle);
+
+               public SKCanvas BeginRecording (SKRect cullRect)
+               {
+                       return OwnedBy (GetObject<SKCanvas> (SkiaApi.sk_picture_recorder_begin_recording (Handle, &cullRect), false), this);
+               }
+
+               public SKPicture EndRecording ()
+               {
+                       return GetObject<SKPicture> (SkiaApi.sk_picture_recorder_end_recording (Handle));
+               }
+
+               public SKDrawable EndRecordingAsDrawable ()
+               {
+                       return GetObject<SKDrawable> (SkiaApi.sk_picture_recorder_end_recording_as_drawable (Handle));
+               }
+
+               public SKCanvas RecordingCanvas =>
+                       OwnedBy (GetObject<SKCanvas> (SkiaApi.sk_picture_get_recording_canvas (Handle), false), this);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPixelSerializer.cs b/src/XSF/SkiaSharp/SKPixelSerializer.cs
new file mode 100644 (file)
index 0000000..f3e1a44
--- /dev/null
@@ -0,0 +1,78 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete]
+       public abstract class SKPixelSerializer : SKObject
+       {
+               protected SKPixelSerializer ()
+                       : base (IntPtr.Zero, false)
+               {
+               }
+
+               public bool UseEncodedData (IntPtr data, ulong length)
+               {
+                       if (SKObject.SizeOf<IntPtr> () == 4 && length > UInt32.MaxValue)
+                               throw new ArgumentOutOfRangeException (nameof (length), "The length exceeds the size of pointers.");
+
+                       return OnUseEncodedData (data, (IntPtr)length);
+               }
+
+               public SKData Encode (SKPixmap pixmap)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+
+                       return OnEncode (pixmap);
+               }
+
+               protected abstract bool OnUseEncodedData (IntPtr data, IntPtr length);
+
+               protected abstract SKData OnEncode (SKPixmap pixmap);
+
+               public static SKPixelSerializer Create (Func<SKPixmap, SKData> onEncode)
+               {
+                       return new SKSimplePixelSerializer (null, onEncode);
+               }
+
+               public static SKPixelSerializer Create (Func<IntPtr, IntPtr, bool> onUseEncodedData, Func<SKPixmap, SKData> onEncode)
+               {
+                       return new SKSimplePixelSerializer (onUseEncodedData, onEncode);
+               }
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete]
+       internal class SKSimplePixelSerializer : SKPixelSerializer
+       {
+               private readonly Func<IntPtr, IntPtr, bool> onUseEncodedData;
+               private readonly Func<SKPixmap, SKData> onEncode;
+
+               public SKSimplePixelSerializer (Func<IntPtr, IntPtr, bool> onUseEncodedData, Func<SKPixmap, SKData> onEncode)
+               {
+                       this.onUseEncodedData = onUseEncodedData;
+                       this.onEncode = onEncode;
+               }
+
+               protected override SKData OnEncode (SKPixmap pixmap)
+               {
+                       return onEncode?.Invoke (pixmap) ?? null;
+               }
+
+               protected override bool OnUseEncodedData (IntPtr data, IntPtr length)
+               {
+                       return onUseEncodedData?.Invoke (data, length) ?? false;
+               }
+       }
+
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Obsolete]
+       public abstract class SKManagedPixelSerializer : SKPixelSerializer
+       {
+               public SKManagedPixelSerializer ()
+               {
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKPixmap.cs b/src/XSF/SkiaSharp/SKPixmap.cs
new file mode 100644 (file)
index 0000000..8e4810b
--- /dev/null
@@ -0,0 +1,410 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public unsafe class SKPixmap : SKObject
+       {
+               private const string UnableToCreateInstanceMessage = "Unable to create a new SKPixmap instance.";
+
+               // this is not meant to be anything but a GC reference to keep the actual pixel data alive
+               internal SKObject pixelSource;
+
+               [Preserve]
+               internal SKPixmap (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKPixmap ()
+                       : this (SkiaApi.sk_pixmap_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException (UnableToCreateInstanceMessage);
+                       }
+               }
+
+               public SKPixmap (SKImageInfo info, IntPtr addr)
+                       : this (info, addr, info.RowBytes)
+               {
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use SKPixmap(SKImageInfo, IntPtr, int) instead.")]
+               public SKPixmap (SKImageInfo info, IntPtr addr, int rowBytes, SKColorTable ctable)
+                       : this (info, addr, info.RowBytes)
+               {
+               }
+
+               public SKPixmap (SKImageInfo info, IntPtr addr, int rowBytes)
+                       : this (IntPtr.Zero, true)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       Handle = SkiaApi.sk_pixmap_new_with_params (&cinfo, (void*)addr, (IntPtr)rowBytes);
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException (UnableToCreateInstanceMessage);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_pixmap_destructor (Handle);
+
+               protected override void DisposeManaged ()
+               {
+                       base.DisposeManaged ();
+
+                       pixelSource = null;
+               }
+
+               // Reset
+
+               public void Reset ()
+               {
+                       SkiaApi.sk_pixmap_reset (Handle);
+                       pixelSource = null;
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported. Use Reset(SKImageInfo, IntPtr, int) instead.")]
+               public void Reset (SKImageInfo info, IntPtr addr, int rowBytes, SKColorTable ctable)
+               {
+                       Reset (info, addr, rowBytes);
+               }
+
+               public void Reset (SKImageInfo info, IntPtr addr, int rowBytes)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       SkiaApi.sk_pixmap_reset_with_params (Handle, &cinfo, (void*)addr, (IntPtr)rowBytes);
+                       pixelSource = null;
+               }
+
+               // properties
+
+               public SKImageInfo Info {
+                       get {
+                               SKImageInfoNative cinfo;
+                               SkiaApi.sk_pixmap_get_info (Handle, &cinfo);
+                               return SKImageInfoNative.ToManaged (ref cinfo);
+                       }
+               }
+
+               public int Width => Info.Width;
+
+               public int Height => Info.Height;
+
+               public SKSizeI Size => new SKSizeI (Width, Height);
+
+               public SKRectI Rect => SKRectI.Create (Width, Height);
+
+               public SKColorType ColorType => Info.ColorType;
+
+               public SKAlphaType AlphaType => Info.AlphaType;
+
+               public SKColorSpace ColorSpace => Info.ColorSpace;
+
+               public int BytesPerPixel => Info.BytesPerPixel;
+
+               public int RowBytes => (int)SkiaApi.sk_pixmap_get_row_bytes (Handle);
+
+               public int BytesSize => Info.BytesSize;
+
+               // pixels
+
+               public IntPtr GetPixels () =>
+                       (IntPtr)SkiaApi.sk_pixmap_get_pixels (Handle);
+
+               public IntPtr GetPixels (int x, int y) =>
+                       (IntPtr)SkiaApi.sk_pixmap_get_pixels_with_xy (Handle, x, y);
+
+               public ReadOnlySpan<byte> GetPixelSpan ()
+               {
+                       return new ReadOnlySpan<byte> ((void*)GetPixels (), BytesSize);
+               }
+
+               public SKColor GetPixelColor (int x, int y)
+               {
+                       return SkiaApi.sk_pixmap_get_pixel_color (Handle, x, y);
+               }
+
+               // ColorTable
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("The Index8 color type and color table is no longer supported.")]
+               public SKColorTable ColorTable => null;
+
+               // Resize
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use ScalePixels(SKPixmap, SKFilterQuality) instead.")]
+               public static bool Resize (SKPixmap dst, SKPixmap src, SKBitmapResizeMethod method)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.ScalePixels (dst, method.ToFilterQuality ());
+               }
+
+               // ScalePixels
+
+               public bool ScalePixels (SKPixmap destination, SKFilterQuality quality)
+               {
+                       if (destination == null)
+                               throw new ArgumentNullException (nameof (destination));
+
+                       return SkiaApi.sk_pixmap_scale_pixels (Handle, destination.Handle, quality);
+               }
+
+               // ReadPixels
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY, SKTransferFunctionBehavior behavior)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref dstInfo);
+                       return SkiaApi.sk_pixmap_read_pixels (Handle, &cinfo, (void*)dstPixels, (IntPtr)dstRowBytes, srcX, srcY, behavior);
+               }
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY)
+               {
+                       return ReadPixels (dstInfo, dstPixels, dstRowBytes, 0, 0, SKTransferFunctionBehavior.Respect);
+               }
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes)
+               {
+                       return ReadPixels (dstInfo, dstPixels, dstRowBytes, 0, 0, SKTransferFunctionBehavior.Respect);
+               }
+
+               public bool ReadPixels (SKPixmap pixmap, int srcX, int srcY)
+               {
+                       return ReadPixels (pixmap.Info, pixmap.GetPixels (), pixmap.RowBytes, srcX, srcY, SKTransferFunctionBehavior.Respect);
+               }
+
+               public bool ReadPixels (SKPixmap pixmap)
+               {
+                       return ReadPixels (pixmap.Info, pixmap.GetPixels (), pixmap.RowBytes, 0, 0, SKTransferFunctionBehavior.Respect);
+               }
+
+               // Encode
+
+               public SKData Encode (SKEncodedImageFormat encoder, int quality)
+               {
+                       using var stream = new SKDynamicMemoryWStream ();
+                       var result = Encode (stream, encoder, quality);
+                       return result ? stream.DetachAsData () : null;
+               }
+
+               public bool Encode (Stream dst, SKEncodedImageFormat encoder, int quality)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var wrapped = new SKManagedWStream (dst);
+                       return Encode (wrapped, encoder, quality);
+               }
+
+               public bool Encode (SKWStream dst, SKEncodedImageFormat encoder, int quality)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_pixmap_encode_image (dst.Handle, Handle, encoder, quality);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Encode(SKWStream, SKEncodedImageFormat, int) instead.")]
+               public static bool Encode (SKWStream dst, SKBitmap src, SKEncodedImageFormat format, int quality)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.Encode (dst, format, quality);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Encode(SKWStream, SKEncodedImageFormat, int) instead.")]
+               public static bool Encode (SKWStream dst, SKPixmap src, SKEncodedImageFormat encoder, int quality)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.Encode (dst, encoder, quality);
+               }
+
+               // Encode (webp)
+
+               public SKData Encode (SKWebpEncoderOptions options)
+               {
+                       using var stream = new SKDynamicMemoryWStream ();
+                       var result = Encode (stream, options);
+                       return result ? stream.DetachAsData () : null;
+               }
+
+               public bool Encode (Stream dst, SKWebpEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var wrapped = new SKManagedWStream (dst);
+                       return Encode (wrapped, options);
+               }
+
+               public bool Encode (SKWStream dst, SKWebpEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_webpencoder_encode (dst.Handle, Handle, &options);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Encode(SKWStream, SKWebpEncoderOptions) instead.")]
+               public static bool Encode (SKWStream dst, SKPixmap src, SKWebpEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.Encode (dst, options);
+               }
+
+               // Encode (jpeg)
+
+               public SKData Encode (SKJpegEncoderOptions options)
+               {
+                       using var stream = new SKDynamicMemoryWStream ();
+                       var result = Encode (stream, options);
+                       return result ? stream.DetachAsData () : null;
+               }
+
+               public bool Encode (Stream dst, SKJpegEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var wrapped = new SKManagedWStream (dst);
+                       return Encode (wrapped, options);
+               }
+
+               public bool Encode (SKWStream dst, SKJpegEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_jpegencoder_encode (dst.Handle, Handle, &options);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Encode(SKWStream, SKJpegEncoderOptions) instead.")]
+               public static bool Encode (SKWStream dst, SKPixmap src, SKJpegEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.Encode (dst, options);
+               }
+
+               // Encode (png)
+
+               public SKData Encode (SKPngEncoderOptions options)
+               {
+                       using var stream = new SKDynamicMemoryWStream ();
+                       var result = Encode (stream, options);
+                       return result ? stream.DetachAsData () : null;
+               }
+
+               public bool Encode (Stream dst, SKPngEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var wrapped = new SKManagedWStream (dst);
+                       return Encode (wrapped, options);
+               }
+
+               public bool Encode (SKWStream dst, SKPngEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       return SkiaApi.sk_pngencoder_encode (dst.Handle, Handle, &options);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Encode(SKWStream, SKPngEncoderOptions) instead.")]
+               public static bool Encode (SKWStream dst, SKPixmap src, SKPngEncoderOptions options)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.Encode (dst, options);
+               }
+
+               // ExtractSubset
+
+               public SKPixmap ExtractSubset (SKRectI subset)
+               {
+                       var result = new SKPixmap ();
+                       if (!ExtractSubset (result, subset)) {
+                               result.Dispose ();
+                               result = null;
+                       }
+                       return result;
+               }
+
+               public bool ExtractSubset (SKPixmap result, SKRectI subset)
+               {
+                       if (result == null)
+                               throw new ArgumentNullException (nameof (result));
+
+                       return SkiaApi.sk_pixmap_extract_subset (Handle, result.Handle, &subset);
+               }
+
+               // Erase
+
+               public bool Erase (SKColor color)
+               {
+                       return Erase (color, Rect);
+               }
+
+               public bool Erase (SKColor color, SKRectI subset)
+               {
+                       return SkiaApi.sk_pixmap_erase_color (Handle, (uint)color, &subset);
+               }
+
+               public bool Erase (SKColorF color) =>
+                       Erase (color, Rect);
+
+               public bool Erase (SKColorF color, SKRectI subset) =>
+                       SkiaApi.sk_pixmap_erase_color4f (Handle, &color, &subset);
+
+               // With*
+
+               public SKPixmap WithColorType (SKColorType newColorType)
+               {
+                       return new SKPixmap (Info.WithColorType (newColorType), GetPixels (), RowBytes);
+               }
+
+               public SKPixmap WithColorSpace (SKColorSpace newColorSpace)
+               {
+                       return new SKPixmap (Info.WithColorSpace (newColorSpace), GetPixels (), RowBytes);
+               }
+
+               public SKPixmap WithAlphaType (SKAlphaType newAlphaType)
+               {
+                       return new SKPixmap (Info.WithAlphaType (newAlphaType), GetPixels (), RowBytes);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKRegion.cs b/src/XSF/SkiaSharp/SKRegion.cs
new file mode 100644 (file)
index 0000000..4cd41dd
--- /dev/null
@@ -0,0 +1,331 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKRegion : SKObject
+       {
+               [Preserve]
+               internal SKRegion (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKRegion ()
+                       : this (SkiaApi.sk_region_new (), true)
+               {
+               }
+
+               public SKRegion (SKRegion region)
+                       : this ()
+               {
+                       SetRegion (region);
+               }
+
+               public SKRegion (SKRectI rect)
+                       : this ()
+               {
+                       SetRect (rect);
+               }
+
+               public SKRegion (SKPath path)
+                       : this ()
+               {
+                       SetPath (path);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_region_delete (Handle);
+
+               // properties
+
+               public bool IsEmpty =>
+                       SkiaApi.sk_region_is_empty (Handle);
+
+               public bool IsRect =>
+                       SkiaApi.sk_region_is_rect (Handle);
+
+               public bool IsComplex =>
+                       SkiaApi.sk_region_is_complex (Handle);
+
+               public SKRectI Bounds {
+                       get {
+                               SKRectI rect;
+                               SkiaApi.sk_region_get_bounds (Handle, &rect);
+                               return rect;
+                       }
+               }
+
+               // GetBoundaryPath
+
+               public SKPath GetBoundaryPath ()
+               {
+                       var path = new SKPath ();
+                       if (!SkiaApi.sk_region_get_boundary_path (Handle, path.Handle)) {
+                               path.Dispose ();
+                               path = null;
+                       }
+                       return path;
+               }
+
+               // Contains
+
+               public bool Contains (SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       using var pathRegion = new SKRegion (path);
+                       return Contains (pathRegion);
+               }
+
+               public bool Contains (SKRegion src)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return SkiaApi.sk_region_contains (Handle, src.Handle);
+               }
+
+               public bool Contains (SKPointI xy) =>
+                       SkiaApi.sk_region_contains_point (Handle, xy.X, xy.Y);
+
+               public bool Contains (int x, int y) =>
+                       SkiaApi.sk_region_contains_point (Handle, x, y);
+
+               public bool Contains (SKRectI rect) =>
+                       SkiaApi.sk_region_contains_rect (Handle, &rect);
+
+               // QuickContains
+
+               public bool QuickContains (SKRectI rect) =>
+                       SkiaApi.sk_region_quick_contains (Handle, &rect);
+
+               // QuickReject
+
+               public bool QuickReject (SKRectI rect) =>
+                       SkiaApi.sk_region_quick_reject_rect (Handle, &rect);
+
+               public bool QuickReject (SKRegion region)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+
+                       return SkiaApi.sk_region_quick_reject (Handle, region.Handle);
+               }
+
+               public bool QuickReject (SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       using var pathRegion = new SKRegion (path);
+                       return QuickReject (pathRegion);
+               }
+
+               // Intersects
+
+               public bool Intersects (SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       using var pathRegion = new SKRegion (path);
+                       return Intersects (pathRegion);
+               }
+
+               public bool Intersects (SKRegion region)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+
+                       return SkiaApi.sk_region_intersects (Handle, region.Handle);
+               }
+
+               public bool Intersects (SKRectI rect) =>
+                       SkiaApi.sk_region_intersects_rect (Handle, &rect);
+
+               // Set*
+
+               public void SetEmpty () =>
+                       SkiaApi.sk_region_set_empty (Handle);
+
+               public bool SetRegion (SKRegion region)
+               {
+                       if (region == null)
+                               throw new ArgumentNullException (nameof (region));
+
+                       return SkiaApi.sk_region_set_region (Handle, region.Handle);
+               }
+
+               public bool SetRect (SKRectI rect) =>
+                       SkiaApi.sk_region_set_rect (Handle, &rect);
+
+               public bool SetRects (SKRectI[] rects)
+               {
+                       if (rects == null)
+                               throw new ArgumentNullException (nameof (rects));
+
+                       fixed (SKRectI* r = rects) {
+                               return SkiaApi.sk_region_set_rects (Handle, r, rects.Length);
+                       }
+               }
+
+               public bool SetPath (SKPath path, SKRegion clip)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+                       if (clip == null)
+                               throw new ArgumentNullException (nameof (clip));
+
+                       return SkiaApi.sk_region_set_path (Handle, path.Handle, clip.Handle);
+               }
+
+               public bool SetPath (SKPath path)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       using var clip = new SKRegion ();
+                       var rect = SKRectI.Ceiling (path.Bounds);
+                       if (!rect.IsEmpty)
+                               clip.SetRect (rect);
+
+                       return SkiaApi.sk_region_set_path (Handle, path.Handle, clip.Handle);
+               }
+
+               // Translate
+
+               public void Translate (int x, int y) =>
+                       SkiaApi.sk_region_translate (Handle, x, y);
+
+               // Op
+
+               public bool Op (SKRectI rect, SKRegionOperation op) =>
+                       SkiaApi.sk_region_op_rect (Handle, &rect, op);
+
+               public bool Op (int left, int top, int right, int bottom, SKRegionOperation op) =>
+                       Op (new SKRectI (left, top, right, bottom), op);
+
+               public bool Op (SKRegion region, SKRegionOperation op) =>
+                       SkiaApi.sk_region_op (Handle, region.Handle, op);
+
+               public bool Op (SKPath path, SKRegionOperation op)
+               {
+                       using var pathRegion = new SKRegion (path);
+                       return Op (pathRegion, op);
+               }
+
+               // Iterators
+
+               public RectIterator CreateRectIterator () =>
+                       new RectIterator (this);
+
+               public ClipIterator CreateClipIterator (SKRectI clip) =>
+                       new ClipIterator (this, clip);
+
+               public SpanIterator CreateSpanIterator (int y, int left, int right) =>
+                       new SpanIterator (this, y, left, right);
+
+               // classes
+
+               public class RectIterator : SKObject
+               {
+                       private readonly SKRegion region;
+
+                       internal RectIterator (SKRegion region)
+                               : base (SkiaApi.sk_region_iterator_new (region.Handle), true)
+                       {
+                               this.region = region;
+                       }
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_region_iterator_delete (Handle);
+
+                       public bool Next (out SKRectI rect)
+                       {
+                               if (SkiaApi.sk_region_iterator_done (Handle)) {
+                                       rect = SKRectI.Empty;
+                                       return false;
+                               }
+
+                               fixed (SKRectI* r = &rect) {
+                                       SkiaApi.sk_region_iterator_rect (Handle, r);
+                               }
+
+                               SkiaApi.sk_region_iterator_next (Handle);
+
+                               return true;
+                       }
+               }
+
+               public class ClipIterator : SKObject
+               {
+                       private readonly SKRegion region;
+                       private readonly SKRectI clip;
+
+                       internal ClipIterator (SKRegion region, SKRectI clip)
+                               : base (SkiaApi.sk_region_cliperator_new (region.Handle, &clip), true)
+                       {
+                               this.region = region;
+                               this.clip = clip;
+                       }
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_region_cliperator_delete (Handle);
+
+                       public bool Next (out SKRectI rect)
+                       {
+                               if (SkiaApi.sk_region_cliperator_done (Handle)) {
+                                       rect = SKRectI.Empty;
+                                       return false;
+                               }
+
+                               fixed (SKRectI* r = &rect) {
+                                       SkiaApi.sk_region_iterator_rect (Handle, r);
+                               }
+
+                               SkiaApi.sk_region_cliperator_next (Handle);
+
+                               return true;
+                       }
+               }
+
+               public class SpanIterator : SKObject
+               {
+                       private readonly SKRegion region;
+                       private readonly int y;
+                       private readonly int left;
+                       private readonly int right;
+
+                       internal SpanIterator (SKRegion region, int y, int left, int right)
+                               : base (SkiaApi.sk_region_spanerator_new (region.Handle, y, left, right), true)
+                       {
+                               this.region = region;
+                               this.y = y;
+                               this.left = left;
+                               this.right = right;
+                       }
+
+                       protected override void DisposeNative () =>
+                               SkiaApi.sk_region_spanerator_delete (Handle);
+
+                       public bool Next (out int left, out int right)
+                       {
+                               int l;
+                               int r;
+                               if (SkiaApi.sk_region_spanerator_next (Handle, &l, &r)) {
+                                       left = l;
+                                       right = r;
+                                       return true;
+                               }
+
+                               left = 0;
+                               right = 0;
+                               return false;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKRotationScaleMatrix.cs b/src/XSF/SkiaSharp/SKRotationScaleMatrix.cs
new file mode 100644 (file)
index 0000000..227b3bf
--- /dev/null
@@ -0,0 +1,50 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe partial struct SKRotationScaleMatrix
+       {
+               public static readonly SKRotationScaleMatrix Empty;
+
+               public static readonly SKRotationScaleMatrix Identity = new SKRotationScaleMatrix (1, 0, 0, 0);
+
+               public SKRotationScaleMatrix (float scos, float ssin, float tx, float ty)
+               {
+                       fSCos = scos;
+                       fSSin = ssin;
+                       fTX = tx;
+                       fTY = ty;
+               }
+
+               public readonly SKMatrix ToMatrix () =>
+                       new SKMatrix (fSCos, -fSSin, fTX, fSSin, fSCos, fTY, 0, 0, 1);
+
+               public static SKRotationScaleMatrix FromDegrees (float scale, float degrees, float tx, float ty, float anchorX, float anchorY) =>
+                       FromRadians (scale, degrees * SKMatrix.DegreesToRadians, tx, ty, anchorX, anchorY);
+
+               public static SKRotationScaleMatrix FromRadians (float scale, float radians, float tx, float ty, float anchorX, float anchorY)
+               {
+                       var s = (float)Math.Sin (radians) * scale;
+                       var c = (float)Math.Cos (radians) * scale;
+                       var x = tx + -c * anchorX + s * anchorY;
+                       var y = ty + -s * anchorX - c * anchorY;
+
+                       return new SKRotationScaleMatrix (c, s, x, y);
+               }
+
+               public static SKRotationScaleMatrix CreateIdentity () =>
+                       new SKRotationScaleMatrix (1, 0, 0, 0);
+
+               public static SKRotationScaleMatrix CreateTranslation (float x, float y) =>
+                       new SKRotationScaleMatrix (1, 0, x, y);
+
+               public static SKRotationScaleMatrix CreateScale (float s) =>
+                       new SKRotationScaleMatrix (s, 0, 0, 0);
+
+               public static SKRotationScaleMatrix CreateRotation (float radians, float anchorX, float anchorY) =>
+                       FromRadians (1, radians, 0, 0, anchorX, anchorY);
+
+               public static SKRotationScaleMatrix CreateRotationDegrees (float degrees, float anchorX, float anchorY) =>
+                       FromDegrees (1, degrees, 0, 0, anchorX, anchorY);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKRoundRect.cs b/src/XSF/SkiaSharp/SKRoundRect.cs
new file mode 100644 (file)
index 0000000..400fd51
--- /dev/null
@@ -0,0 +1,194 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKRoundRect : SKObject
+       {
+               [Preserve]
+               internal SKRoundRect (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKRoundRect ()
+                       : this (SkiaApi.sk_rrect_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKRoundRect instance.");
+                       }
+                       SetEmpty ();
+               }
+
+               public SKRoundRect (SKRect rect)
+                       : this (SkiaApi.sk_rrect_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKRoundRect instance.");
+                       }
+                       SetRect (rect);
+               }
+
+               public SKRoundRect (SKRect rect, float radius)
+                       : this (rect, radius, radius)
+               {
+               }
+
+               public SKRoundRect (SKRect rect, float xRadius, float yRadius)
+                       : this (SkiaApi.sk_rrect_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKRoundRect instance.");
+                       }
+                       SetRect (rect, xRadius, yRadius);
+               }
+
+               public SKRoundRect (SKRoundRect rrect)
+                       : this (SkiaApi.sk_rrect_new_copy (rrect.Handle), true)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_rrect_delete (Handle);
+
+               public SKRect Rect {
+                       get {
+                               SKRect rect;
+                               SkiaApi.sk_rrect_get_rect (Handle, &rect);
+                               return rect;
+                       }
+               }
+
+               public SKPoint[] Radii => new[] {
+                       GetRadii(SKRoundRectCorner.UpperLeft),
+                       GetRadii(SKRoundRectCorner.UpperRight),
+                       GetRadii(SKRoundRectCorner.LowerRight),
+                       GetRadii(SKRoundRectCorner.LowerLeft),
+               };
+
+               public SKRoundRectType Type => SkiaApi.sk_rrect_get_type (Handle);
+
+               public float Width => SkiaApi.sk_rrect_get_width (Handle);
+
+               public float Height => SkiaApi.sk_rrect_get_height (Handle);
+
+               public bool IsValid => SkiaApi.sk_rrect_is_valid (Handle);
+
+               public bool AllCornersCircular => CheckAllCornersCircular (Utils.NearlyZero);
+
+               public bool CheckAllCornersCircular (float tolerance)
+               {
+                       var ul = GetRadii (SKRoundRectCorner.UpperLeft);
+                       var ur = GetRadii (SKRoundRectCorner.UpperRight);
+                       var lr = GetRadii (SKRoundRectCorner.LowerRight);
+                       var ll = GetRadii (SKRoundRectCorner.LowerLeft);
+
+                       return
+                               Utils.NearlyEqual (ul.X, ul.Y, tolerance) &&
+                               Utils.NearlyEqual (ur.X, ur.Y, tolerance) &&
+                               Utils.NearlyEqual (lr.X, lr.Y, tolerance) &&
+                               Utils.NearlyEqual (ll.X, ll.Y, tolerance);
+               }
+
+               public void SetEmpty ()
+               {
+                       SkiaApi.sk_rrect_set_empty (Handle);
+               }
+
+               public void SetRect (SKRect rect)
+               {
+                       SkiaApi.sk_rrect_set_rect (Handle, &rect);
+               }
+
+               public void SetRect (SKRect rect, float xRadius, float yRadius)
+               {
+                       SkiaApi.sk_rrect_set_rect_xy (Handle, &rect, xRadius, yRadius);
+               }
+
+               public void SetOval (SKRect rect)
+               {
+                       SkiaApi.sk_rrect_set_oval (Handle, &rect);
+               }
+
+               public void SetNinePatch (SKRect rect, float leftRadius, float topRadius, float rightRadius, float bottomRadius)
+               {
+                       SkiaApi.sk_rrect_set_nine_patch (Handle, &rect, leftRadius, topRadius, rightRadius, bottomRadius);
+               }
+
+               public void SetRectRadii (SKRect rect, SKPoint[] radii)
+               {
+                       if (radii == null)
+                               throw new ArgumentNullException (nameof (radii));
+                       if (radii.Length != 4)
+                               throw new ArgumentException ("Radii must have a length of 4.", nameof (radii));
+
+                       fixed (SKPoint* r = radii) {
+                               SkiaApi.sk_rrect_set_rect_radii (Handle, &rect, r);
+                       }
+               }
+
+               public bool Contains (SKRect rect)
+               {
+                       return SkiaApi.sk_rrect_contains (Handle, &rect);
+               }
+
+               public SKPoint GetRadii (SKRoundRectCorner corner)
+               {
+                       SKPoint radii;
+                       SkiaApi.sk_rrect_get_radii (Handle, corner, &radii);
+                       return radii;
+               }
+
+               public void Deflate (SKSize size)
+               {
+                       Deflate (size.Width, size.Height);
+               }
+
+               public void Deflate (float dx, float dy)
+               {
+                       SkiaApi.sk_rrect_inset (Handle, dx, dy);
+               }
+
+               public void Inflate (SKSize size)
+               {
+                       Inflate (size.Width, size.Height);
+               }
+
+               public void Inflate (float dx, float dy)
+               {
+                       SkiaApi.sk_rrect_outset (Handle, dx, dy);
+               }
+
+               public void Offset (SKPoint pos)
+               {
+                       Offset (pos.X, pos.Y);
+               }
+
+               public void Offset (float dx, float dy)
+               {
+                       SkiaApi.sk_rrect_offset (Handle, dx, dy);
+               }
+
+               public bool TryTransform (SKMatrix matrix, out SKRoundRect transformed)
+               {
+                       var destHandle = SkiaApi.sk_rrect_new ();
+                       if (SkiaApi.sk_rrect_transform (Handle, &matrix, destHandle)) {
+                               transformed = new SKRoundRect (destHandle, true);
+                               return true;
+                       }
+                       SkiaApi.sk_rrect_delete (destHandle);
+                       transformed = null;
+                       return false;
+               }
+
+               public SKRoundRect Transform (SKMatrix matrix)
+               {
+                       if (TryTransform (matrix, out var transformed)) {
+                               return transformed;
+                       }
+                       return null;
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKRunBuffer.cs b/src/XSF/SkiaSharp/SKRunBuffer.cs
new file mode 100644 (file)
index 0000000..a5d8ef2
--- /dev/null
@@ -0,0 +1,67 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       public unsafe class SKRunBuffer
+       {
+               internal readonly SKRunBufferInternal internalBuffer;
+
+               internal SKRunBuffer (SKRunBufferInternal buffer, int size, int textSize)
+               {
+                       internalBuffer = buffer;
+                       Size = size;
+                       TextSize = textSize;
+               }
+
+               public int Size { get; }
+
+               public int TextSize { get; }
+
+               public Span<ushort> GetGlyphSpan () =>
+                       new Span<ushort> (internalBuffer.glyphs, internalBuffer.glyphs == null ? 0 : Size);
+
+               public Span<byte> GetTextSpan () =>
+                       new Span<byte> (internalBuffer.utf8text, internalBuffer.utf8text == null ? 0 : TextSize);
+
+               public Span<uint> GetClusterSpan () =>
+                       new Span<uint> (internalBuffer.clusters, internalBuffer.clusters == null ? 0 : Size);
+
+               public void SetGlyphs (ReadOnlySpan<ushort> glyphs) =>
+                       glyphs.CopyTo (GetGlyphSpan ());
+
+               public void SetText (ReadOnlySpan<byte> text) =>
+                       text.CopyTo (GetTextSpan ());
+
+               public void SetClusters (ReadOnlySpan<uint> clusters) =>
+                       clusters.CopyTo (GetClusterSpan ());
+       }
+
+       public sealed unsafe class SKHorizontalRunBuffer : SKRunBuffer
+       {
+               internal SKHorizontalRunBuffer (SKRunBufferInternal buffer, int count, int textSize)
+                       : base (buffer, count, textSize)
+               {
+               }
+
+               public Span<float> GetPositionSpan () =>
+                       new Span<float> (internalBuffer.pos, internalBuffer.pos == null ? 0 : Size);
+
+               public void SetPositions (ReadOnlySpan<float> positions) =>
+                       positions.CopyTo (GetPositionSpan ());
+       }
+
+       public sealed unsafe class SKPositionedRunBuffer : SKRunBuffer
+       {
+               internal SKPositionedRunBuffer (SKRunBufferInternal buffer, int count, int textSize)
+                       : base (buffer, count, textSize)
+               {
+               }
+
+               public Span<SKPoint> GetPositionSpan () =>
+                       new Span<SKPoint> (internalBuffer.pos, internalBuffer.pos == null ? 0 : Size);
+
+               public void SetPositions (ReadOnlySpan<SKPoint> positions) =>
+                       positions.CopyTo (GetPositionSpan ());
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKSVG.cs b/src/XSF/SkiaSharp/SKSVG.cs
new file mode 100644 (file)
index 0000000..23d9fa4
--- /dev/null
@@ -0,0 +1,46 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public unsafe class SKSvgCanvas
+       {
+               private SKSvgCanvas ()
+               {
+               }
+
+               // Create
+
+               public static SKCanvas Create (SKRect bounds, Stream stream)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       var managed = new SKManagedWStream (stream);
+                       return SKObject.Owned (Create (bounds, managed), managed);
+               }
+
+               public static SKCanvas Create (SKRect bounds, SKWStream stream)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       // TODO: there seems to be a memory issue with things getting destroyed in the incorrect order
+                       //return SKObject.Referenced (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_stream (&bounds, stream.Handle)), stream);
+
+                       var writer = new SKXmlStreamWriter (stream);
+                       return SKObject.Owned (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_writer (&bounds, writer.Handle)), writer);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKRect, Stream) instead.")]
+               public static SKCanvas Create (SKRect bounds, SKXmlWriter writer)
+               {
+                       if (writer == null)
+                               throw new ArgumentNullException (nameof (writer));
+
+                       return SKObject.Referenced (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_writer (&bounds, writer.Handle)), writer);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKShader.cs b/src/XSF/SkiaSharp/SKShader.cs
new file mode 100644 (file)
index 0000000..0df58c0
--- /dev/null
@@ -0,0 +1,446 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKShader : SKObject, ISKReferenceCounted
+       {
+               [Preserve]
+               internal SKShader (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // WithColorFilter
+
+               public SKShader WithColorFilter (SKColorFilter filter)
+               {
+                       if (filter == null)
+                               throw new ArgumentNullException (nameof (filter));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_with_color_filter (Handle, filter.Handle));
+               }
+
+               // WithLocalMatrix
+
+               public SKShader WithLocalMatrix (SKMatrix localMatrix) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_with_local_matrix (Handle, &localMatrix));
+
+               // CreateEmpty
+
+               public static SKShader CreateEmpty () =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_empty ());
+
+               // CreateColor
+
+               public static SKShader CreateColor (SKColor color) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_color ((uint)color));
+
+               public static SKShader CreateColor (SKColorF color, SKColorSpace colorspace)
+               {
+                       if (colorspace == null)
+                               throw new ArgumentNullException (nameof (colorspace));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_color4f (&color, colorspace.Handle));
+               }
+
+               // CreateBitmap
+
+               public static SKShader CreateBitmap (SKBitmap src) =>
+                       CreateBitmap (src, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_bitmap (src.Handle, tmx, tmy, null));
+               }
+
+               public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_bitmap (src.Handle, tmx, tmy, &localMatrix));
+               }
+
+               // CreateImage
+
+               public static SKShader CreateImage (SKImage src) =>
+                       CreateImage (src, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public static SKShader CreateImage (SKImage src, SKShaderTileMode tmx, SKShaderTileMode tmy)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.ToShader (tmx, tmy);
+               }
+
+               public static SKShader CreateImage (SKImage src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return src.ToShader (tmx, tmy, localMatrix);
+               }
+
+               // CreatePicture
+
+               public static SKShader CreatePicture (SKPicture src) =>
+                       CreatePicture (src, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
+
+               public static SKShader CreatePicture (SKPicture src, SKShaderTileMode tmx, SKShaderTileMode tmy)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_picture (src.Handle, tmx, tmy, null, null));
+               }
+
+               public static SKShader CreatePicture (SKPicture src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKRect tile)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_picture (src.Handle, tmx, tmy, null, &tile));
+               }
+
+               public static SKShader CreatePicture (SKPicture src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix, SKRect tile)
+               {
+                       if (src == null)
+                               throw new ArgumentNullException (nameof (src));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_picture (src.Handle, tmx, tmy, &localMatrix, &tile));
+               }
+
+               // CreateLinearGradient
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, SKShaderTileMode mode) =>
+                       CreateLinearGradient (start, end, colors, null, mode);
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       var points = stackalloc SKPoint[] { start, end };
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_linear_gradient (points, (uint*)c, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       var points = stackalloc SKPoint[] { start, end };
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_linear_gradient (points, (uint*)c, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) =>
+                       CreateLinearGradient (start, end, colors, colorspace, null, mode);
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       var points = stackalloc SKPoint[] { start, end };
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_linear_gradient_color4f (points, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       var points = stackalloc SKPoint[] { start, end };
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_linear_gradient_color4f (points, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               // CreateRadialGradient
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, SKShaderTileMode mode) =>
+                       CreateRadialGradient (center, radius, colors, null, mode);
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_radial_gradient (&center, radius, (uint*)c, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_radial_gradient (&center, radius, (uint*)c, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) =>
+                       CreateRadialGradient (center, radius, colors, colorspace, null, mode);
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_radial_gradient_color4f (&center, radius, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_radial_gradient_color4f (&center, radius, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               // CreateSweepGradient
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors) =>
+                       CreateSweepGradient (center, colors, null, SKShaderTileMode.Clamp, 0, 360);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos) =>
+                       CreateSweepGradient (center, colors, colorPos, SKShaderTileMode.Clamp, 0, 360);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKMatrix localMatrix) =>
+                       CreateSweepGradient (center, colors, colorPos, SKShaderTileMode.Clamp, 0, 360, localMatrix);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, SKShaderTileMode tileMode, float startAngle, float endAngle) =>
+                       CreateSweepGradient (center, colors, null, tileMode, startAngle, endAngle);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_sweep_gradient (&center, (uint*)c, cp, colors.Length, tileMode, startAngle, endAngle, null));
+                       }
+               }
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_sweep_gradient (&center, (uint*)c, cp, colors.Length, tileMode, startAngle, endAngle, &localMatrix));
+                       }
+               }
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace) =>
+                       CreateSweepGradient (center, colors, colorspace, null, SKShaderTileMode.Clamp, 0, 360);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos) =>
+                       CreateSweepGradient (center, colors, colorspace, colorPos, SKShaderTileMode.Clamp, 0, 360);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKMatrix localMatrix) =>
+                       CreateSweepGradient (center, colors, colorspace, colorPos, SKShaderTileMode.Clamp, 0, 360, localMatrix);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode tileMode, float startAngle, float endAngle) =>
+                       CreateSweepGradient (center, colors, colorspace, null, tileMode, startAngle, endAngle);
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_sweep_gradient_color4f (&center, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, tileMode, startAngle, endAngle, null));
+                       }
+               }
+
+               public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_sweep_gradient_color4f (&center, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, tileMode, startAngle, endAngle, &localMatrix));
+                       }
+               }
+
+               // CreateTwoPointConicalGradient
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, SKShaderTileMode mode) =>
+                       CreateTwoPointConicalGradient (start, startRadius, end, endRadius, colors, null, mode);
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_two_point_conical_gradient (&start, startRadius, &end, endRadius, (uint*)c, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColor* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_two_point_conical_gradient (&start, startRadius, &end, endRadius, (uint*)c, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) =>
+                       CreateTwoPointConicalGradient (start, startRadius, end, endRadius, colors, colorspace, null, mode);
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_two_point_conical_gradient_color4f (&start, startRadius, &end, endRadius, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, null));
+                       }
+               }
+
+               public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix)
+               {
+                       if (colors == null)
+                               throw new ArgumentNullException (nameof (colors));
+                       if (colorPos != null && colors.Length != colorPos.Length)
+                               throw new ArgumentException ("The number of colors must match the number of color positions.");
+
+                       fixed (SKColorF* c = colors)
+                       fixed (float* cp = colorPos) {
+                               return GetObject<SKShader> (SkiaApi.sk_shader_new_two_point_conical_gradient_color4f (&start, startRadius, &end, endRadius, c, colorspace?.Handle ?? IntPtr.Zero, cp, colors.Length, mode, &localMatrix));
+                       }
+               }
+
+               // CreatePerlinNoiseFractalNoise
+
+               public static SKShader CreatePerlinNoiseFractalNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_perlin_noise_fractal_noise (baseFrequencyX, baseFrequencyY, numOctaves, seed, null));
+
+               public static SKShader CreatePerlinNoiseFractalNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKPointI tileSize) =>
+                       CreatePerlinNoiseFractalNoise (baseFrequencyX, baseFrequencyY, numOctaves, seed, (SKSizeI)tileSize);
+
+               public static SKShader CreatePerlinNoiseFractalNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKSizeI tileSize) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_perlin_noise_fractal_noise (baseFrequencyX, baseFrequencyY, numOctaves, seed, &tileSize));
+
+               // CreatePerlinNoiseImprovedNoise
+
+               public static SKShader CreatePerlinNoiseImprovedNoise (float baseFrequencyX, float baseFrequencyY, int numOctaves, float z) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_perlin_noise_improved_noise (baseFrequencyX, baseFrequencyY, numOctaves, z));
+
+               // CreatePerlinNoiseTurbulence
+
+               public static SKShader CreatePerlinNoiseTurbulence (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_perlin_noise_turbulence (baseFrequencyX, baseFrequencyY, numOctaves, seed, null));
+
+               public static SKShader CreatePerlinNoiseTurbulence (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKPointI tileSize) =>
+                       CreatePerlinNoiseTurbulence (baseFrequencyX, baseFrequencyY, numOctaves, seed, (SKSizeI)tileSize);
+
+               public static SKShader CreatePerlinNoiseTurbulence (float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, SKSizeI tileSize) =>
+                       GetObject<SKShader> (SkiaApi.sk_shader_new_perlin_noise_turbulence (baseFrequencyX, baseFrequencyY, numOctaves, seed, &tileSize));
+
+               // CreateCompose
+
+               public static SKShader CreateCompose (SKShader shaderA, SKShader shaderB) =>
+                       CreateCompose (shaderA, shaderB, SKBlendMode.SrcOver);
+
+               public static SKShader CreateCompose (SKShader shaderA, SKShader shaderB, SKBlendMode mode)
+               {
+                       if (shaderA == null)
+                               throw new ArgumentNullException (nameof (shaderA));
+                       if (shaderB == null)
+                               throw new ArgumentNullException (nameof (shaderB));
+
+                       return GetObject<SKShader> (SkiaApi.sk_shader_new_compose (shaderA.Handle, shaderB.Handle, mode));
+               }
+
+               // CreateColorFilter
+
+               public static SKShader CreateColorFilter (SKShader shader, SKColorFilter filter)
+               {
+                       if (shader == null)
+                               throw new ArgumentNullException (nameof (shader));
+                       if (filter == null)
+                               throw new ArgumentNullException (nameof (filter));
+
+                       return shader.WithColorFilter (filter);
+               }
+
+               // CreateLocalMatrix
+
+               public static SKShader CreateLocalMatrix (SKShader shader, SKMatrix localMatrix)
+               {
+                       if (shader == null)
+                               throw new ArgumentNullException (nameof (shader));
+
+                       return shader.WithLocalMatrix (localMatrix);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKStream.cs b/src/XSF/SkiaSharp/SKStream.cs
new file mode 100644 (file)
index 0000000..b32555f
--- /dev/null
@@ -0,0 +1,589 @@
+using System;
+using System.IO;
+
+namespace SkiaSharp
+{
+       public unsafe abstract class SKStream : SKObject
+       {
+               internal SKStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+               
+               public bool IsAtEnd {
+                       get {
+                               return SkiaApi.sk_stream_is_at_end (Handle);
+                       }
+               }
+
+               public SByte ReadSByte ()
+               {
+                       if (ReadSByte (out var buffer))
+                               return buffer;
+                       return default (SByte);
+               }
+
+               public Int16 ReadInt16 ()
+               {
+                       if (ReadInt16 (out var buffer))
+                               return buffer;
+                       return default (Int16);
+               }
+
+               public Int32 ReadInt32 ()
+               {
+                       if (ReadInt32 (out var buffer))
+                               return buffer;
+                       return default (Int32);
+               }
+
+               public Byte ReadByte ()
+               {
+                       if (ReadByte (out var buffer))
+                               return buffer;
+                       return default (Byte);
+               }
+
+               public UInt16 ReadUInt16 ()
+               {
+                       if (ReadUInt16 (out var buffer))
+                               return buffer;
+                       return default (UInt16);
+               }
+
+               public UInt32 ReadUInt32 ()
+               {
+                       if (ReadUInt32 (out var buffer))
+                               return buffer;
+                       return default (UInt32);
+               }
+
+               public bool ReadBool ()
+               {
+                       if (ReadBool (out var buffer))
+                               return buffer;
+                       return default (bool);
+               }
+
+               public bool ReadSByte (out SByte buffer)
+               {
+                       fixed (SByte* b = &buffer) {
+                               return SkiaApi.sk_stream_read_s8 (Handle, b);
+                       }
+               }
+
+               public bool ReadInt16 (out Int16 buffer)
+               {
+                       fixed (Int16* b = &buffer) {
+                               return SkiaApi.sk_stream_read_s16 (Handle, b);
+                       }
+               }
+
+               public bool ReadInt32 (out Int32 buffer)
+               {
+                       fixed (Int32* b = &buffer) {
+                               return SkiaApi.sk_stream_read_s32 (Handle, b);
+                       }
+               }
+
+               public bool ReadByte (out Byte buffer)
+               {
+                       fixed (Byte* b = &buffer) {
+                               return SkiaApi.sk_stream_read_u8 (Handle, b);
+                       }
+               }
+
+               public bool ReadUInt16 (out UInt16 buffer)
+               {
+                       fixed (UInt16* b = &buffer) {
+                               return SkiaApi.sk_stream_read_u16 (Handle, b);
+                       }
+               }
+
+               public bool ReadUInt32 (out UInt32 buffer)
+               {
+                       fixed (UInt32* b = &buffer) {
+                               return SkiaApi.sk_stream_read_u32 (Handle, b);
+                       }
+               }
+
+               public bool ReadBool (out Boolean buffer)
+               {
+                       byte b;
+                       var result = SkiaApi.sk_stream_read_bool (Handle, &b);
+                       buffer = b > 0;
+                       return result;
+               }
+
+               public int Read (byte[] buffer, int size)
+               {
+                       fixed (byte* b = buffer) {
+                               return Read ((IntPtr)b, size);
+                       }
+               }
+
+               public int Read (IntPtr buffer, int size)
+               {
+                       return (int)SkiaApi.sk_stream_read (Handle, (void*)buffer, (IntPtr)size);
+               }
+
+               public int Peek (IntPtr buffer, int size)
+               {
+                       return (int)SkiaApi.sk_stream_peek (Handle, (void*)buffer, (IntPtr)size);
+               }
+
+               public int Skip (int size)
+               {
+                       return (int)SkiaApi.sk_stream_skip (Handle, (IntPtr)size);
+               }
+
+               public bool Rewind ()
+               {
+                       return SkiaApi.sk_stream_rewind (Handle);
+               }
+
+               public bool Seek (int position)
+               {
+                       return SkiaApi.sk_stream_seek (Handle, (IntPtr)position);
+               }
+
+               public bool Move (long offset) => Move ((int)offset);
+
+               public bool Move (int offset)
+               {
+                       return SkiaApi.sk_stream_move (Handle, offset);
+               }
+
+               public IntPtr GetMemoryBase ()
+               {
+                       return (IntPtr)SkiaApi.sk_stream_get_memory_base (Handle);
+               }
+
+               internal SKStream Fork () =>
+                       GetObject<SKStream, SKStreamImplementation> (SkiaApi.sk_stream_fork (Handle));
+
+               internal SKStream Duplicate () =>
+                       GetObject<SKStream, SKStreamImplementation> (SkiaApi.sk_stream_duplicate (Handle));
+
+               public bool HasPosition {
+                       get {
+                               return SkiaApi.sk_stream_has_position (Handle);
+                       }
+               }
+
+               public int Position {
+                       get {
+                               return (int)SkiaApi.sk_stream_get_position (Handle);
+                       }
+                       set { 
+                               Seek (value);
+                       }
+               }
+
+               public bool HasLength {
+                       get {
+                               return SkiaApi.sk_stream_has_length (Handle);
+                       }
+               }
+
+               public int Length {
+                       get {
+                               return (int)SkiaApi.sk_stream_get_length (Handle);
+                       }
+               }
+       }
+
+       internal class SKStreamImplementation : SKStream
+       {
+               [Preserve]
+               internal SKStreamImplementation (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_stream_destroy (Handle);
+       }
+
+       public abstract class SKStreamRewindable : SKStream
+       {
+               internal SKStreamRewindable (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+       }
+
+       public abstract class SKStreamSeekable : SKStreamRewindable
+       {
+               internal SKStreamSeekable (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+       }
+
+       public abstract class SKStreamAsset : SKStreamSeekable
+       {
+               internal SKStreamAsset (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+       }
+
+       internal class SKStreamAssetImplementation : SKStreamAsset
+       {
+               [Preserve]
+               internal SKStreamAssetImplementation (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_stream_asset_destroy (Handle);
+       }
+
+       public abstract class SKStreamMemory : SKStreamAsset
+       {
+               internal SKStreamMemory (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+       }
+
+       public unsafe class SKFileStream : SKStreamAsset
+       {
+               [Preserve]
+               internal SKFileStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKFileStream (string path)
+                       : base (CreateNew (path), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKFileStream instance.");
+                       }
+               }
+
+               private static IntPtr CreateNew (string path)
+               {
+                       var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
+                       fixed (byte* p = bytes) {
+                               return SkiaApi.sk_filestream_new (p);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_filestream_destroy (Handle);
+
+               public bool IsValid => SkiaApi.sk_filestream_is_valid (Handle);
+
+               public static bool IsPathSupported (string path) => true;
+
+               public static SKStreamAsset OpenStream (string path)
+               {
+                       var stream = new SKFileStream (path);
+                       if (!stream.IsValid) {
+                               stream.Dispose ();
+                               stream = null;
+                       }
+                       return stream;
+               }
+       }
+
+       public unsafe class SKMemoryStream : SKStreamMemory
+       {
+               [Preserve]
+               internal SKMemoryStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKMemoryStream ()
+                       : this (SkiaApi.sk_memorystream_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKMemoryStream instance.");
+                       }
+               }
+
+               public SKMemoryStream (ulong length)
+                       : this(SkiaApi.sk_memorystream_new_with_length ((IntPtr)length), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKMemoryStream instance.");
+                       }
+               }
+
+               internal SKMemoryStream (IntPtr data, IntPtr length, bool copyData = false)
+                       : this(SkiaApi.sk_memorystream_new_with_data ((void*)data, length, copyData), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKMemoryStream instance.");
+                       }
+               }
+
+               public SKMemoryStream (SKData data)
+                       : this(SkiaApi.sk_memorystream_new_with_skdata (data.Handle), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKMemoryStream instance.");
+                       }
+               }
+
+               public SKMemoryStream (byte[] data)
+                       : this ()
+               {
+                       SetMemory (data);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_memorystream_destroy (Handle);
+
+               internal void SetMemory (IntPtr data, IntPtr length, bool copyData = false)
+               {
+                       SkiaApi.sk_memorystream_set_memory (Handle, (void*)data, length, copyData);
+               }
+
+               internal void SetMemory (byte[] data, IntPtr length, bool copyData = false)
+               {
+                       fixed (byte* d = data) {
+                               SkiaApi.sk_memorystream_set_memory (Handle, d, length, copyData);
+                       }
+               }
+
+               public void SetMemory (byte[] data)
+               {
+                       SetMemory (data, (IntPtr)data.Length, true);
+               }
+       }
+
+       public unsafe abstract class SKWStream : SKObject
+       {
+               internal SKWStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+               
+               public virtual int BytesWritten {
+                       get {
+                               return (int)SkiaApi.sk_wstream_bytes_written (Handle);
+                       }
+               }
+
+               public virtual bool Write (byte[] buffer, int size)
+               {
+                       fixed (byte* b = buffer) {
+                               return SkiaApi.sk_wstream_write (Handle, (void*)b, (IntPtr)size);
+                       }
+               }
+
+               public bool NewLine ()
+               {
+                       return SkiaApi.sk_wstream_newline (Handle);
+               }
+
+               public virtual void Flush ()
+               {
+                       SkiaApi.sk_wstream_flush (Handle);
+               }
+
+               public bool Write8 (Byte value)
+               {
+                       return SkiaApi.sk_wstream_write_8 (Handle, value);
+               }
+
+               public bool Write16 (UInt16 value)
+               {
+                       return SkiaApi.sk_wstream_write_16 (Handle, value);
+               }
+
+               public bool Write32 (UInt32 value)
+               {
+                       return SkiaApi.sk_wstream_write_32 (Handle, value);
+               }
+
+               public bool WriteText (string value)
+               {
+                       return SkiaApi.sk_wstream_write_text (Handle, value);
+               }
+
+               public bool WriteDecimalAsTest (Int32 value)
+               {
+                       return SkiaApi.sk_wstream_write_dec_as_text (Handle, value);
+               }
+
+               public bool WriteBigDecimalAsText (Int64 value, int digits)
+               {
+                       return SkiaApi.sk_wstream_write_bigdec_as_text (Handle, value, digits);
+               }
+
+               public bool WriteHexAsText (UInt32 value, int digits)
+               {
+                       return SkiaApi.sk_wstream_write_hex_as_text (Handle, value, digits);
+               }
+
+               public bool WriteScalarAsText (float value)
+               {
+                       return SkiaApi.sk_wstream_write_scalar_as_text (Handle, value);
+               }
+
+               public bool WriteBool (bool value)
+               {
+                       return SkiaApi.sk_wstream_write_bool (Handle, value);
+               }
+
+               public bool WriteScalar (float value)
+               {
+                       return SkiaApi.sk_wstream_write_scalar (Handle, value);
+               }
+
+               public bool WritePackedUInt32 (UInt32 value)
+               {
+                       return SkiaApi.sk_wstream_write_packed_uint (Handle, (IntPtr)value);
+               }
+
+               public bool WriteStream (SKStream input, int length)
+               {
+                       if (input == null) {
+                               throw new ArgumentNullException (nameof(input));
+                       }
+
+                       return SkiaApi.sk_wstream_write_stream (Handle, input.Handle, (IntPtr)length);
+               }
+
+               public static int GetSizeOfPackedUInt32 (UInt32 value)
+               {
+                       return SkiaApi.sk_wstream_get_size_of_packed_uint ((IntPtr) value);
+               }
+       }
+
+       public unsafe class SKFileWStream : SKWStream
+       {
+               [Preserve]
+               internal SKFileWStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKFileWStream (string path)
+                       : base (CreateNew (path), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKFileWStream instance.");
+                       }
+               }
+
+               private static IntPtr CreateNew (string path)
+               {
+                       var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
+                       fixed (byte* p = bytes) {
+                               return SkiaApi.sk_filewstream_new (p);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_filewstream_destroy (Handle);
+
+               public bool IsValid => SkiaApi.sk_filewstream_is_valid (Handle);
+
+               public static bool IsPathSupported (string path) => true;
+
+               public static SKWStream OpenStream (string path)
+               {
+                       var stream = new SKFileWStream (path);
+                       if (!stream.IsValid) {
+                               stream.Dispose ();
+                               stream = null;
+                       }
+                       return stream;
+               }
+       }
+
+       public unsafe class SKDynamicMemoryWStream : SKWStream
+       {
+               [Preserve]
+               internal SKDynamicMemoryWStream (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKDynamicMemoryWStream ()
+                       : base (SkiaApi.sk_dynamicmemorywstream_new (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKDynamicMemoryWStream instance.");
+                       }
+               }
+
+               public SKData CopyToData ()
+               {
+                       var data = SKData.Create (BytesWritten);
+                       CopyTo (data.Data);
+                       return data;
+               }
+
+               public SKStreamAsset DetachAsStream ()
+               {
+                       return GetObject<SKStreamAsset, SKStreamAssetImplementation> (SkiaApi.sk_dynamicmemorywstream_detach_as_stream (Handle));
+               }
+
+               public SKData DetachAsData ()
+               {
+                       return GetObject<SKData> (SkiaApi.sk_dynamicmemorywstream_detach_as_data (Handle));
+               }
+
+               public void CopyTo (IntPtr data)
+               {
+                       SkiaApi.sk_dynamicmemorywstream_copy_to (Handle, (void*)data);
+               }
+
+               public void CopyTo (Span<byte> data)
+               {
+                       var size = BytesWritten;
+                       if (data.Length < size)
+                               throw new Exception ($"Not enough space to copy. Expected at least {size}, but received {data.Length}.");
+
+                       fixed (void* d = data) {
+                               SkiaApi.sk_dynamicmemorywstream_copy_to (Handle, d);
+                       }
+               }
+
+               public bool CopyTo (SKWStream dst)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+                       return SkiaApi.sk_dynamicmemorywstream_write_to_stream (Handle, dst.Handle);
+               }
+
+               public bool CopyTo (Stream dst)
+               {
+                       if (dst == null)
+                               throw new ArgumentNullException (nameof (dst));
+
+                       using var wrapped = new SKManagedWStream (dst);
+                       return CopyTo (wrapped);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_dynamicmemorywstream_destroy (Handle);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKString.cs b/src/XSF/SkiaSharp/SKString.cs
new file mode 100644 (file)
index 0000000..8b61369
--- /dev/null
@@ -0,0 +1,74 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       internal unsafe class SKString : SKObject
+       {
+               [Preserve]
+               internal SKString (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               public SKString ()
+                       : base (SkiaApi.sk_string_new_empty (), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to create a new SKString instance.");
+                       }
+               }
+               
+               public SKString (byte [] src, long length)
+                       : base (CreateCopy (src, length), true)
+               {
+                       if (Handle == IntPtr.Zero) {
+                               throw new InvalidOperationException ("Unable to copy the SKString instance.");
+                       }
+               }
+               
+               private static IntPtr CreateCopy (byte [] src, long length)
+               {
+                       fixed (byte* s = src) {
+                               return SkiaApi.sk_string_new_with_copy (s, (IntPtr)length);
+                       }
+               }
+
+               public SKString (byte [] src)
+                       : this (src, src.Length)
+               {
+               }
+               
+               public SKString (string str)
+                       : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf8))
+               {
+               }
+               
+               public override string ToString ()
+               {
+                       var cstr = SkiaApi.sk_string_get_c_str (Handle);
+                       var clen = SkiaApi.sk_string_get_size (Handle);
+                       return StringUtilities.GetString ((IntPtr)cstr, (int)clen, SKTextEncoding.Utf8);
+               }
+
+               public static explicit operator string (SKString skString)
+               {
+                       return skString.ToString ();
+               }
+               
+               internal static SKString Create (string str)
+               {
+                       if (str == null) {
+                               return null;
+                       }
+                       return new SKString (str);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_string_destructor (Handle);
+       }
+}
+
diff --git a/src/XSF/SkiaSharp/SKSurface.cs b/src/XSF/SkiaSharp/SKSurface.cs
new file mode 100644 (file)
index 0000000..ccc04d1
--- /dev/null
@@ -0,0 +1,353 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class SKSurface : SKObject, ISKReferenceCounted
+       {
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo) instead.")]
+               public static SKSurface Create (int width, int height, SKColorType colorType, SKAlphaType alphaType) => Create (new SKImageInfo (width, height, colorType, alphaType));
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (int width, int height, SKColorType colorType, SKAlphaType alphaType, SKSurfaceProps props) => Create (new SKImageInfo (width, height, colorType, alphaType), props);
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo, IntPtr, int) instead.")]
+               public static SKSurface Create (int width, int height, SKColorType colorType, SKAlphaType alphaType, IntPtr pixels, int rowBytes) => Create (new SKImageInfo (width, height, colorType, alphaType), pixels, rowBytes);
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo, IntPtr, int, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (int width, int height, SKColorType colorType, SKAlphaType alphaType, IntPtr pixels, int rowBytes, SKSurfaceProps props) => Create (new SKImageInfo (width, height, colorType, alphaType), pixels, rowBytes, props);
+
+               [Preserve]
+               internal SKSurface (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               // RASTER surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (SKImageInfo info, SKSurfaceProps props) =>
+                       Create (info, 0, new SKSurfaceProperties (props));
+
+               public static SKSurface Create (SKImageInfo info) =>
+                       Create (info, 0, null);
+
+               public static SKSurface Create (SKImageInfo info, int rowBytes) =>
+                       Create (info, rowBytes, null);
+
+               public static SKSurface Create (SKImageInfo info, SKSurfaceProperties props) =>
+                       Create (info, 0, props);
+
+               public static SKSurface Create (SKImageInfo info, int rowBytes, SKSurfaceProperties props)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_raster (&cinfo, (IntPtr)rowBytes, props?.Handle ?? IntPtr.Zero));
+               }
+
+               // convenience RASTER DIRECT to use a SKPixmap instead of SKImageInfo and IntPtr
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKPixmap, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (SKPixmap pixmap, SKSurfaceProps props) =>
+                       Create (pixmap, new SKSurfaceProperties (props));
+
+               public static SKSurface Create (SKPixmap pixmap) =>
+                       Create (pixmap, null);
+
+               public static SKSurface Create (SKPixmap pixmap, SKSurfaceProperties props)
+               {
+                       if (pixmap == null) {
+                               throw new ArgumentNullException (nameof (pixmap));
+                       }
+                       return Create (pixmap.Info, pixmap.GetPixels (), pixmap.RowBytes, null, null, props);
+               }
+
+               // RASTER DIRECT surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(SKImageInfo, IntPtr, rowBytes, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, int rowBytes, SKSurfaceProps props) =>
+                       Create (info, pixels, rowBytes, null, null, new SKSurfaceProperties (props));
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels) =>
+                       Create (info, pixels, info.RowBytes, null, null, null);
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, int rowBytes) =>
+                       Create (info, pixels, rowBytes, null, null, null);
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, int rowBytes, SKSurfaceReleaseDelegate releaseProc, object context) =>
+                       Create (info, pixels, rowBytes, releaseProc, context, null);
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, SKSurfaceProperties props) =>
+                       Create (info, pixels, info.RowBytes, null, null, props);
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, int rowBytes, SKSurfaceProperties props) =>
+                       Create (info, pixels, rowBytes, null, null, props);
+
+               public static SKSurface Create (SKImageInfo info, IntPtr pixels, int rowBytes, SKSurfaceReleaseDelegate releaseProc, object context, SKSurfaceProperties props)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       var del = releaseProc != null && context != null
+                               ? new SKSurfaceReleaseDelegate ((addr, _) => releaseProc (addr, context))
+                               : releaseProc;
+                       var proxy = DelegateProxies.Create (del, DelegateProxies.SKSurfaceReleaseDelegateProxy, out _, out var ctx);
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_raster_direct (&cinfo, (void*)pixels, (IntPtr)rowBytes, proxy, (void*)ctx, props?.Handle ?? IntPtr.Zero));
+               }
+
+               // GPU BACKEND RENDER TARGET surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendRenderTarget, GRSurfaceOrigin, SKColorType) instead.")]
+               public static SKSurface Create (GRContext context, GRBackendRenderTargetDesc desc)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+
+                       var renderTarget = new GRBackendRenderTarget (context.Backend, desc);
+                       return Create (context, renderTarget, desc.Origin, desc.Config.ToColorType (), null, null);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendRenderTarget, GRSurfaceOrigin, SKColorType, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (GRContext context, GRBackendRenderTargetDesc desc, SKSurfaceProps props)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+
+                       var renderTarget = new GRBackendRenderTarget (context.Backend, desc);
+                       return Create (context, renderTarget, desc.Origin, desc.Config.ToColorType (), null, new SKSurfaceProperties (props));
+               }
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, SKColorType colorType) =>
+                       Create (context, renderTarget, GRSurfaceOrigin.BottomLeft, colorType, null, null);
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType) =>
+                       Create (context, renderTarget, origin, colorType, null, null);
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType, SKColorSpace colorspace) =>
+                       Create (context, renderTarget, origin, colorType, colorspace, null);
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, SKColorType colorType, SKSurfaceProperties props) =>
+                       Create (context, renderTarget, GRSurfaceOrigin.BottomLeft, colorType, null, props);
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType, SKSurfaceProperties props) =>
+                       Create (context, renderTarget, origin, colorType, null, props);
+
+               public static SKSurface Create (GRContext context, GRBackendRenderTarget renderTarget, GRSurfaceOrigin origin, SKColorType colorType, SKColorSpace colorspace, SKSurfaceProperties props)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+                       if (renderTarget == null)
+                               throw new ArgumentNullException (nameof (renderTarget));
+
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_backend_render_target (context.Handle, renderTarget.Handle, origin, colorType, colorspace?.Handle ?? IntPtr.Zero, props?.Handle ?? IntPtr.Zero));
+               }
+
+               // GPU BACKEND TEXTURE surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType) instead.")]
+               public static SKSurface Create (GRContext context, GRGlBackendTextureDesc desc) =>
+                       Create (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, null);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType) instead.")]
+               public static SKSurface Create (GRContext context, GRBackendTextureDesc desc) =>
+                       Create (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, null);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (GRContext context, GRGlBackendTextureDesc desc, SKSurfaceProps props) =>
+                       Create (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, new SKSurfaceProperties (props));
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (GRContext context, GRBackendTextureDesc desc, SKSurfaceProps props) =>
+                       Create (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, new SKSurfaceProperties (props));
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, SKColorType colorType) =>
+                       Create (context, texture, GRSurfaceOrigin.BottomLeft, 0, colorType, null, null);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType) =>
+                       Create (context, texture, origin, 0, colorType, null, null);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType) =>
+                       Create (context, texture, origin, sampleCount, colorType, null, null);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKColorSpace colorspace) =>
+                       Create (context, texture, origin, sampleCount, colorType, colorspace, null);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, SKColorType colorType, SKSurfaceProperties props) =>
+                       Create (context, texture, GRSurfaceOrigin.BottomLeft, 0, colorType, null, props);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKSurfaceProperties props) =>
+                       Create (context, texture, origin, 0, colorType, null, props);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKSurfaceProperties props) =>
+                       Create (context, texture, origin, sampleCount, colorType, null, props);
+
+               public static SKSurface Create (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKColorSpace colorspace, SKSurfaceProperties props)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+                       if (texture == null)
+                               throw new ArgumentNullException (nameof (texture));
+
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_backend_texture (context.Handle, texture.Handle, origin, sampleCount, colorType, colorspace?.Handle ?? IntPtr.Zero, props?.Handle ?? IntPtr.Zero));
+               }
+
+               // GPU BACKEND TEXTURE AS RENDER TARGET surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateAsRenderTarget(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType) instead.")]
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRGlBackendTextureDesc desc) =>
+                       CreateAsRenderTarget (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, null);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateAsRenderTarget(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType) instead.")]
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTextureDesc desc) =>
+                       CreateAsRenderTarget (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, null);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateAsRenderTarget(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType, SKSurfaceProperties) instead.")]
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRGlBackendTextureDesc desc, SKSurfaceProps props) =>
+                       CreateAsRenderTarget (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, new SKSurfaceProperties (props));
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CreateAsRenderTarget(GRContext, GRBackendTexture, GRSurfaceOrigin, int, SKColorType, SKSurfaceProperties) instead.")]
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTextureDesc desc, SKSurfaceProps props) =>
+                       CreateAsRenderTarget (context, new GRBackendTexture (desc), desc.Origin, desc.SampleCount, desc.Config.ToColorType (), null, new SKSurfaceProperties (props));
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, SKColorType colorType) =>
+                       CreateAsRenderTarget (context, texture, GRSurfaceOrigin.BottomLeft, 0, colorType, null, null);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType) =>
+                       CreateAsRenderTarget (context, texture, origin, 0, colorType, null, null);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType) =>
+                       CreateAsRenderTarget (context, texture, origin, sampleCount, colorType, null, null);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKColorSpace colorspace) =>
+                       CreateAsRenderTarget (context, texture, origin, sampleCount, colorType, colorspace, null);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, SKColorType colorType, SKSurfaceProperties props) =>
+                       CreateAsRenderTarget (context, texture, GRSurfaceOrigin.BottomLeft, 0, colorType, null, props);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKSurfaceProperties props) =>
+                       CreateAsRenderTarget (context, texture, origin, 0, colorType, null, props);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKSurfaceProperties props) =>
+                       CreateAsRenderTarget (context, texture, origin, sampleCount, colorType, null, props);
+
+               public static SKSurface CreateAsRenderTarget (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, int sampleCount, SKColorType colorType, SKColorSpace colorspace, SKSurfaceProperties props)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+                       if (texture == null)
+                               throw new ArgumentNullException (nameof (texture));
+
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_backend_texture_as_render_target (context.Handle, texture.Handle, origin, sampleCount, colorType, colorspace?.Handle ?? IntPtr.Zero, props?.Handle ?? IntPtr.Zero));
+               }
+
+               // GPU NEW surface
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use Create(GRContext, bool, SKImageInfo, int, SKSurfaceProperties) instead.")]
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, int sampleCount, SKSurfaceProps props) =>
+                       Create (context, budgeted, info, sampleCount, GRSurfaceOrigin.BottomLeft, new SKSurfaceProperties (props), false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info) =>
+                       Create (context, budgeted, info, 0, GRSurfaceOrigin.BottomLeft, null, false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, int sampleCount) =>
+                       Create (context, budgeted, info, sampleCount, GRSurfaceOrigin.BottomLeft, null, false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, int sampleCount, GRSurfaceOrigin origin) =>
+                       Create (context, budgeted, info, sampleCount, GRSurfaceOrigin.BottomLeft, null, false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, SKSurfaceProperties props) =>
+                       Create (context, budgeted, info, 0, GRSurfaceOrigin.BottomLeft, props, false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, int sampleCount, SKSurfaceProperties props) =>
+                       Create (context, budgeted, info, sampleCount, GRSurfaceOrigin.BottomLeft, props, false);
+
+               public static SKSurface Create (GRContext context, bool budgeted, SKImageInfo info, int sampleCount, GRSurfaceOrigin origin, SKSurfaceProperties props, bool shouldCreateWithMips)
+               {
+                       if (context == null)
+                               throw new ArgumentNullException (nameof (context));
+
+                       var cinfo = SKImageInfoNative.FromManaged (ref info);
+                       return GetObject<SKSurface> (SkiaApi.sk_surface_new_render_target (context.Handle, budgeted, &cinfo, sampleCount, origin, props?.Handle ?? IntPtr.Zero, shouldCreateWithMips));
+               }
+
+               // NULL surface
+
+               public static SKSurface CreateNull (int width, int height) =>
+                       GetObject<SKSurface> (SkiaApi.sk_surface_new_null (width, height));
+
+               //
+
+               public SKCanvas Canvas =>
+                       OwnedBy (GetObject<SKCanvas> (SkiaApi.sk_surface_get_canvas (Handle), false, unrefExisting: false), this);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use SurfaceProperties instead.")]
+               public SKSurfaceProps SurfaceProps {
+                       get {
+                               var props = SurfaceProperties;
+                               return new SKSurfaceProps {
+                                       Flags = props.Flags,
+                                       PixelGeometry = props.PixelGeometry
+                               };
+                       }
+               }
+
+               public SKSurfaceProperties SurfaceProperties =>
+                       OwnedBy (GetObject<SKSurfaceProperties> (SkiaApi.sk_surface_get_props (Handle), false), this);
+
+               public SKImage Snapshot () =>
+                       GetObject<SKImage> (SkiaApi.sk_surface_new_image_snapshot (Handle));
+
+               public void Draw (SKCanvas canvas, float x, float y, SKPaint paint)
+               {
+                       if (canvas == null)
+                               throw new ArgumentNullException (nameof (canvas));
+
+                       SkiaApi.sk_surface_draw (Handle, canvas.Handle, x, y, paint == null ? IntPtr.Zero : paint.Handle);
+               }
+
+               public SKPixmap PeekPixels ()
+               {
+                       var pixmap = new SKPixmap ();
+                       var result = PeekPixels (pixmap);
+                       if (result) {
+                               return pixmap;
+                       } else {
+                               pixmap.Dispose ();
+                               return null;
+                       }
+               }
+
+               public bool PeekPixels (SKPixmap pixmap)
+               {
+                       if (pixmap == null)
+                               throw new ArgumentNullException (nameof (pixmap));
+
+                       var result = SkiaApi.sk_surface_peek_pixels (Handle, pixmap.Handle);
+                       if (result)
+                               pixmap.pixelSource = this;
+                       return result;
+               }
+
+               public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY)
+               {
+                       var cinfo = SKImageInfoNative.FromManaged (ref dstInfo);
+                       return SkiaApi.sk_surface_read_pixels (Handle, &cinfo, (void*)dstPixels, (IntPtr)dstRowBytes, srcX, srcY);
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKSurfaceProperties.cs b/src/XSF/SkiaSharp/SKSurfaceProperties.cs
new file mode 100644 (file)
index 0000000..6ec25cf
--- /dev/null
@@ -0,0 +1,51 @@
+using System;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public class SKSurfaceProperties : SKObject
+       {
+               [Preserve]
+               internal SKSurfaceProperties (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public SKSurfaceProperties (SKSurfaceProps props)
+                       : this (props.Flags, props.PixelGeometry)
+               {
+               }
+
+               public SKSurfaceProperties (SKPixelGeometry pixelGeometry)
+                       : this ((uint)0, pixelGeometry)
+               {
+               }
+
+               public SKSurfaceProperties (uint flags, SKPixelGeometry pixelGeometry)
+                       : this (SkiaApi.sk_surfaceprops_new (flags, pixelGeometry), true)
+               {
+               }
+
+               public SKSurfaceProperties (SKSurfacePropsFlags flags, SKPixelGeometry pixelGeometry)
+                       : this (SkiaApi.sk_surfaceprops_new ((uint)flags, pixelGeometry), true)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_surfaceprops_delete (Handle);
+
+               public SKSurfacePropsFlags Flags =>
+                       (SKSurfacePropsFlags)SkiaApi.sk_surfaceprops_get_flags (Handle);
+
+               public SKPixelGeometry PixelGeometry =>
+                       SkiaApi.sk_surfaceprops_get_pixel_geometry (Handle);
+
+               public bool IsUseDeviceIndependentFonts =>
+                       Flags.HasFlag (SKSurfacePropsFlags.UseDeviceIndependentFonts);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKSwizzle.cs b/src/XSF/SkiaSharp/SKSwizzle.cs
new file mode 100644 (file)
index 0000000..52b032f
--- /dev/null
@@ -0,0 +1,43 @@
+using System;
+
+namespace SkiaSharp
+{
+       public static unsafe class SKSwizzle
+       {
+               public static void SwapRedBlue (IntPtr pixels, int count) =>
+                       SwapRedBlue (pixels, pixels, count);
+
+               public static void SwapRedBlue (IntPtr dest, IntPtr src, int count)
+               {
+                       if (dest == IntPtr.Zero) {
+                               throw new ArgumentException (nameof (dest));
+                       }
+                       if (src == IntPtr.Zero) {
+                               throw new ArgumentException (nameof (src));
+                       }
+
+                       SkiaApi.sk_swizzle_swap_rb ((uint*)dest, (uint*)src, count);
+               }
+
+               public static void SwapRedBlue (Span<byte> pixels) =>
+                       SwapRedBlue (pixels, pixels, pixels.Length);
+
+               public static void SwapRedBlue (ReadOnlySpan<byte> pixels, int count) =>
+                       SwapRedBlue (pixels, pixels, count);
+
+               public static void SwapRedBlue (ReadOnlySpan<byte> dest, ReadOnlySpan<byte> src, int count)
+               {
+                       if (dest == null) {
+                               throw new ArgumentNullException (nameof (dest));
+                       }
+                       if (src == null) {
+                               throw new ArgumentNullException (nameof (src));
+                       }
+
+                       fixed (byte* d = dest)
+                       fixed (byte* s = src) {
+                               SkiaApi.sk_swizzle_swap_rb ((uint*)d, (uint*)s, count);
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKTextBlob.cs b/src/XSF/SkiaSharp/SKTextBlob.cs
new file mode 100644 (file)
index 0000000..1d95d56
--- /dev/null
@@ -0,0 +1,345 @@
+using System;
+
+namespace SkiaSharp
+{
+       public unsafe class SKTextBlob : SKObject, ISKNonVirtualReferenceCounted
+       {
+               [Preserve]
+               internal SKTextBlob (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               void ISKNonVirtualReferenceCounted.ReferenceNative () => SkiaApi.sk_textblob_ref (Handle);
+
+               void ISKNonVirtualReferenceCounted.UnreferenceNative () => SkiaApi.sk_textblob_unref (Handle);
+
+               public SKRect Bounds {
+                       get {
+                               SKRect bounds;
+                               SkiaApi.sk_textblob_get_bounds (Handle, &bounds);
+                               return bounds;
+                       }
+               }
+
+               public uint UniqueId => SkiaApi.sk_textblob_get_unique_id (Handle);
+       }
+
+       public unsafe class SKTextBlobBuilder : SKObject
+       {
+               [Preserve]
+               internal SKTextBlobBuilder (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               public SKTextBlobBuilder ()
+                       : this (SkiaApi.sk_textblob_builder_new (), true)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_textblob_builder_delete (Handle);
+
+               // Build
+
+               public SKTextBlob Build () =>
+                       GetObject<SKTextBlob> (SkiaApi.sk_textblob_builder_make (Handle));
+
+               // AddRun
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, string text, uint[] clusters)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddRun (font, x, y, glyphs, utf8Text, clusters, null);
+               }
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, string text, uint[] clusters, SKRect bounds)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddRun (font, x, y, glyphs, utf8Text, clusters, (SKRect?)bounds);
+               }
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs) =>
+                       AddRun (font, x, y, glyphs, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, SKRect bounds) =>
+                       AddRun (font, x, y, glyphs, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, byte[] text, uint[] clusters) =>
+                       AddRun (font, x, y, glyphs, text, clusters, null);
+
+               public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, byte[] text, uint[] clusters, SKRect bounds) =>
+                       AddRun (font, x, y, glyphs, text, clusters, (SKRect?)bounds);
+
+               // AddRun (spans)
+
+               public void AddRun (SKPaint font, float x, float y, ReadOnlySpan<ushort> glyphs) =>
+                       AddRun (font, x, y, glyphs, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddRun (SKPaint font, float x, float y, ReadOnlySpan<ushort> glyphs, SKRect? bounds) =>
+                       AddRun (font, x, y, glyphs, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddRun (SKPaint font, float x, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters) =>
+                       AddRun (font, x, y, glyphs, text, clusters, null);
+
+               public void AddRun (SKPaint font, float x, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+                       if (glyphs.IsEmpty)
+                               throw new ArgumentNullException (nameof (glyphs));
+
+                       if (!text.IsEmpty) {
+                               if (clusters.IsEmpty)
+                                       throw new ArgumentNullException (nameof (clusters));
+                               if (glyphs.Length != clusters.Length)
+                                       throw new ArgumentException ("The number of glyphs and clusters must be the same.");
+                       }
+
+                       var run = AllocateRun (font, glyphs.Length, x, y, text.IsEmpty ? 0 : text.Length, bounds);
+                       run.SetGlyphs (glyphs);
+
+                       if (!text.IsEmpty) {
+                               run.SetText (text);
+                               run.SetClusters (clusters);
+                       }
+               }
+
+               // AddHorizontalRun
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, string text, uint[] clusters)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddHorizontalRun (font, y, glyphs, positions, utf8Text, clusters, null);
+               }
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, string text, uint[] clusters, SKRect bounds)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddHorizontalRun (font, y, glyphs, positions, utf8Text, clusters, (SKRect?)bounds);
+               }
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions) =>
+                       AddHorizontalRun (font, y, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, SKRect bounds) =>
+                       AddHorizontalRun (font, y, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, byte[] text, uint[] clusters) =>
+                       AddHorizontalRun (font, y, glyphs, positions, text, clusters, null);
+
+               public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, byte[] text, uint[] clusters, SKRect bounds) =>
+                       AddHorizontalRun (font, y, glyphs, positions, text, clusters, (SKRect?)bounds);
+
+               // AddHorizontalRun (spans)
+
+               public void AddHorizontalRun (SKPaint font, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<float> positions) =>
+                       AddHorizontalRun (font, y, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddHorizontalRun (SKPaint font, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<float> positions, SKRect? bounds) =>
+                       AddHorizontalRun (font, y, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddHorizontalRun (SKPaint font, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<float> positions, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters) =>
+                       AddHorizontalRun (font, y, glyphs, positions, text, clusters, null);
+
+               public void AddHorizontalRun (SKPaint font, float y, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<float> positions, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+                       if (glyphs.IsEmpty)
+                               throw new ArgumentNullException (nameof (glyphs));
+                       if (positions.IsEmpty)
+                               throw new ArgumentNullException (nameof (positions));
+                       if (glyphs.Length != positions.Length)
+                               throw new ArgumentException ("The number of glyphs and positions must be the same.");
+
+                       if (!text.IsEmpty) {
+                               if (clusters.IsEmpty)
+                                       throw new ArgumentNullException (nameof (clusters));
+                               if (glyphs.Length != clusters.Length)
+                                       throw new ArgumentException ("The number of glyphs and clusters must be the same.");
+                       }
+
+                       var run = AllocateHorizontalRun (font, glyphs.Length, y, text.IsEmpty ? 0 : text.Length, bounds);
+                       run.SetGlyphs (glyphs);
+                       run.SetPositions (positions);
+
+                       if (!text.IsEmpty) {
+                               run.SetText (text);
+                               run.SetClusters (clusters);
+                       }
+               }
+
+               // AddPositionedRun
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, string text, uint[] clusters)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddPositionedRun (font, glyphs, positions, utf8Text, clusters, null);
+               }
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, string text, uint[] clusters, SKRect bounds)
+               {
+                       var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
+                       AddPositionedRun (font, glyphs, positions, utf8Text, clusters, (SKRect?)bounds);
+               }
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions) =>
+                       AddPositionedRun (font, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, SKRect bounds) =>
+                       AddPositionedRun (font, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, byte[] text, uint[] clusters) =>
+                       AddPositionedRun (font, glyphs, positions, text, clusters, null);
+
+               public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, byte[] text, uint[] clusters, SKRect bounds) =>
+                       AddPositionedRun (font, glyphs, positions, text, clusters, (SKRect?)bounds);
+
+               // AddPositionedRun (spans)
+
+               public void AddPositionedRun (SKPaint font, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions) =>
+                       AddPositionedRun (font, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, null);
+
+               public void AddPositionedRun (SKPaint font, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions, SKRect? bounds) =>
+                       AddPositionedRun (font, glyphs, positions, ReadOnlySpan<byte>.Empty, ReadOnlySpan<uint>.Empty, bounds);
+
+               public void AddPositionedRun (SKPaint font, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters) =>
+                       AddPositionedRun (font, glyphs, positions, text, clusters, null);
+
+               public void AddPositionedRun (SKPaint font, ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions, ReadOnlySpan<byte> text, ReadOnlySpan<uint> clusters, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+                       if (glyphs.IsEmpty)
+                               throw new ArgumentNullException (nameof (glyphs));
+                       if (positions.IsEmpty)
+                               throw new ArgumentNullException (nameof (positions));
+                       if (glyphs.Length != positions.Length)
+                               throw new ArgumentException ("The number of glyphs and positions must be the same.");
+
+                       if (!text.IsEmpty) {
+                               if (clusters.IsEmpty)
+                                       throw new ArgumentNullException (nameof (clusters));
+                               if (glyphs.Length != clusters.Length)
+                                       throw new ArgumentException ("The number of glyphs and clusters must be the same.");
+                       }
+
+                       var run = AllocatePositionedRun (font, glyphs.Length, text.IsEmpty ? 0 : text.Length, bounds);
+                       run.SetGlyphs (glyphs);
+                       run.SetPositions (positions);
+
+                       if (!text.IsEmpty) {
+                               run.SetText (text);
+                               run.SetClusters (clusters);
+                       }
+               }
+
+               // AllocateRun
+
+               public SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y) =>
+                       AllocateRun (font, count, x, y, 0, null);
+
+               public SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y, SKRect? bounds) =>
+                       AllocateRun (font, count, x, y, 0, bounds);
+
+               public SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y, int textByteCount) =>
+                       AllocateRun (font, count, x, y, textByteCount, null);
+
+               public SKRunBuffer AllocateRun (SKPaint font, int count, float x, float y, int textByteCount, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+
+                       var originalEncoding = font.TextEncoding;
+                       try {
+                               font.TextEncoding = SKTextEncoding.GlyphId;
+
+                               SKRunBufferInternal runbuffer;
+                               if (bounds is SKRect b) {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text (Handle, font.Handle, count, x, y, textByteCount, &b, &runbuffer);
+                               } else {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text (Handle, font.Handle, count, x, y, textByteCount, null, &runbuffer);
+                               }
+
+                               return new SKRunBuffer (runbuffer, count, textByteCount);
+
+                       } finally {
+                               font.TextEncoding = originalEncoding;
+                       }
+               }
+
+               // AllocateHorizontalRun
+
+               public SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y) =>
+                       AllocateHorizontalRun (font, count, y, 0, null);
+
+               public SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y, SKRect? bounds) =>
+                       AllocateHorizontalRun (font, count, y, 0, bounds);
+
+               public SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y, int textByteCount) =>
+                       AllocateHorizontalRun (font, count, y, textByteCount, null);
+
+               public SKHorizontalRunBuffer AllocateHorizontalRun (SKPaint font, int count, float y, int textByteCount, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+
+                       var originalEncoding = font.TextEncoding;
+                       try {
+                               font.TextEncoding = SKTextEncoding.GlyphId;
+
+                               SKRunBufferInternal runbuffer;
+                               if (bounds is SKRect b) {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text_pos_h (Handle, font.Handle, count, y, textByteCount, &b, &runbuffer);
+                               } else {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text_pos_h (Handle, font.Handle, count, y, textByteCount, null, &runbuffer);
+                               }
+
+                               return new SKHorizontalRunBuffer (runbuffer, count, textByteCount);
+                       } finally {
+                               font.TextEncoding = originalEncoding;
+                       }
+               }
+
+               // AllocatePositionedRun
+
+               public SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count) =>
+                       AllocatePositionedRun (font, count, 0, null);
+
+               public SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count, SKRect? bounds) =>
+                       AllocatePositionedRun (font, count, 0, bounds);
+
+               public SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count, int textByteCount) =>
+                       AllocatePositionedRun (font, count, textByteCount, null);
+
+               public SKPositionedRunBuffer AllocatePositionedRun (SKPaint font, int count, int textByteCount, SKRect? bounds)
+               {
+                       if (font == null)
+                               throw new ArgumentNullException (nameof (font));
+
+                       var originalEncoding = font.TextEncoding;
+                       try {
+                               font.TextEncoding = SKTextEncoding.GlyphId;
+
+                               SKRunBufferInternal runbuffer;
+                               if (bounds is SKRect b) {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text_pos (Handle, font.Handle, count, textByteCount, &b, &runbuffer);
+                               } else {
+                                       SkiaApi.sk_textblob_builder_alloc_run_text_pos (Handle, font.Handle, count, textByteCount, null, &runbuffer);
+                               }
+
+                               return new SKPositionedRunBuffer (runbuffer, count, textByteCount);
+                       } finally {
+                               font.TextEncoding = originalEncoding;
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKTypeface.cs b/src/XSF/SkiaSharp/SKTypeface.cs
new file mode 100644 (file)
index 0000000..62217ec
--- /dev/null
@@ -0,0 +1,458 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+
+namespace SkiaSharp
+{
+       [EditorBrowsable (EditorBrowsableState.Never)]
+       [Flags]
+       [Obsolete ("Use SKFontStyleWeight and SKFontStyleSlant instead.")]
+       public enum SKTypefaceStyle
+       {
+               Normal = 0,
+               Bold = 0x01,
+               Italic = 0x02,
+               BoldItalic = 0x03
+       }
+
+       public unsafe class SKTypeface : SKObject, ISKReferenceCounted
+       {
+               private static readonly Lazy<SKTypeface> defaultTypeface;
+
+               static SKTypeface ()
+               {
+                       defaultTypeface = new Lazy<SKTypeface> (() => new SKTypefaceStatic (SkiaApi.sk_typeface_ref_default ()));
+               }
+
+               internal static void EnsureStaticInstanceAreInitialized ()
+               {
+                       // IMPORTANT: do not remove to ensure that the static instances
+                       //            are initialized before any access is made to them
+               }
+
+               [Preserve]
+               internal SKTypeface (IntPtr handle, bool owns)
+                       : base (handle, owns)
+               {
+               }
+
+               // Default
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               public static SKTypeface Default => defaultTypeface.Value;
+
+               public static SKTypeface CreateDefault ()
+               {
+                       return GetObject<SKTypeface> (SkiaApi.sk_typeface_create_default ());
+               }
+
+               // FromFamilyName
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FromFamilyName(string, SKFontStyleWeight, SKFontStyleWidth, SKFontStyleSlant) instead.")]
+               public static SKTypeface FromFamilyName (string familyName, SKTypefaceStyle style)
+               {
+                       var weight = style.HasFlag (SKTypefaceStyle.Bold) ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal;
+                       var slant = style.HasFlag (SKTypefaceStyle.Italic) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
+
+                       return FromFamilyName (familyName, weight, SKFontStyleWidth.Normal, slant);
+               }
+
+               public static SKTypeface FromFamilyName (string familyName, int weight, int width, SKFontStyleSlant slant)
+               {
+                       return FromFamilyName (familyName, new SKFontStyle (weight, width, slant));
+               }
+
+               public static SKTypeface FromFamilyName (string familyName)
+               {
+                       return FromFamilyName (familyName, SKFontStyle.Normal);
+               }
+
+               public static SKTypeface FromFamilyName (string familyName, SKFontStyle style)
+               {
+                       if (style == null)
+                               throw new ArgumentNullException (nameof (style));
+
+                       return GetObject<SKTypeface> (SkiaApi.sk_typeface_create_from_name_with_font_style (familyName, style.Handle));
+               }
+
+               public static SKTypeface FromFamilyName (string familyName, SKFontStyleWeight weight, SKFontStyleWidth width, SKFontStyleSlant slant)
+               {
+                       return FromFamilyName (familyName, (int)weight, (int)width, slant);
+               }
+
+               // From*
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete]
+               public static SKTypeface FromTypeface (SKTypeface typeface, SKTypefaceStyle style)
+               {
+                       if (typeface == null)
+                               throw new ArgumentNullException (nameof (typeface));
+
+                       var weight = style.HasFlag (SKTypefaceStyle.Bold) ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal;
+                       var width = SKFontStyleWidth.Normal;
+                       var slant = style.HasFlag (SKTypefaceStyle.Italic) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
+
+                       return SKFontManager.Default.MatchTypeface (typeface, new SKFontStyle (weight, width, slant));
+               }
+
+               public static SKTypeface FromFile (string path, int index = 0)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException (nameof (path));
+
+                       var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
+                       fixed (byte* u = utf8path) {
+                               return GetObject<SKTypeface> (SkiaApi.sk_typeface_create_from_file (u, index));
+                       }
+               }
+
+               public static SKTypeface FromStream (Stream stream, int index = 0)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       return FromStream (new SKManagedStream (stream, true), index);
+               }
+
+               public static SKTypeface FromStream (SKStreamAsset stream, int index = 0)
+               {
+                       if (stream == null)
+                               throw new ArgumentNullException (nameof (stream));
+
+                       if (stream is SKManagedStream managed) {
+                               stream = managed.ToMemoryStream ();
+                               managed.Dispose ();
+                       }
+
+                       var typeface = GetObject<SKTypeface> (SkiaApi.sk_typeface_create_from_stream (stream.Handle, index));
+                       stream.RevokeOwnership (typeface);
+                       return typeface;
+               }
+
+               public static SKTypeface FromData (SKData data, int index = 0)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       return FromStream (new SKMemoryStream (data), index);
+               }
+
+               // CharsToGlyphs
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(string, out ushort[]) instead.")]
+               public int CharsToGlyphs (string chars, out ushort[] glyphs) =>
+                       GetGlyphs (chars, out glyphs);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding, out ushort[]) instead.")]
+               public int CharsToGlyphs (IntPtr str, int strlen, SKEncoding encoding, out ushort[] glyphs) =>
+                       GetGlyphs (str, strlen, encoding, out glyphs);
+
+               // Properties
+
+               public string FamilyName => (string)GetObject<SKString> (SkiaApi.sk_typeface_get_family_name (Handle));
+
+               public SKFontStyle FontStyle => GetObject<SKFontStyle> (SkiaApi.sk_typeface_get_fontstyle (Handle));
+
+               public int FontWeight => SkiaApi.sk_typeface_get_font_weight (Handle);
+
+               public int FontWidth => SkiaApi.sk_typeface_get_font_width (Handle);
+
+               public SKFontStyleSlant FontSlant => SkiaApi.sk_typeface_get_font_slant (Handle);
+
+               public bool IsBold => FontStyle.Weight >= (int)SKFontStyleWeight.SemiBold;
+
+               public bool IsItalic => FontStyle.Slant != SKFontStyleSlant.Upright;
+
+               public bool IsFixedPitch => SkiaApi.sk_typeface_is_fixed_pitch (Handle);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use FontWeight and FontSlant instead.")]
+               public SKTypefaceStyle Style {
+                       get {
+                               var style = SKTypefaceStyle.Normal;
+                               if (FontWeight >= (int)SKFontStyleWeight.SemiBold)
+                                       style |= SKTypefaceStyle.Bold;
+                               if (FontSlant != (int)SKFontStyleSlant.Upright)
+                                       style |= SKTypefaceStyle.Italic;
+                               return style;
+                       }
+               }
+
+               public int UnitsPerEm => SkiaApi.sk_typeface_get_units_per_em (Handle);
+
+               public int GlyphCount => SkiaApi.sk_typeface_count_glyphs (Handle);
+
+               // GetTableTags
+
+               public int TableCount => SkiaApi.sk_typeface_count_tables (Handle);
+
+               public UInt32[] GetTableTags ()
+               {
+                       if (!TryGetTableTags (out var result)) {
+                               throw new Exception ("Unable to read the tables for the file.");
+                       }
+                       return result;
+               }
+
+               public bool TryGetTableTags (out UInt32[] tags)
+               {
+                       var buffer = new UInt32[TableCount];
+                       fixed (UInt32* b = buffer) {
+                               if (SkiaApi.sk_typeface_get_table_tags (Handle, b) == 0) {
+                                       tags = null;
+                                       return false;
+                               }
+                       }
+                       tags = buffer;
+                       return true;
+               }
+
+               // GetTableSize
+
+               public int GetTableSize (UInt32 tag) =>
+                       (int)SkiaApi.sk_typeface_get_table_size (Handle, tag);
+
+               // GetTableData
+
+               public byte[] GetTableData (UInt32 tag)
+               {
+                       if (!TryGetTableData (tag, out var result)) {
+                               throw new Exception ("Unable to read the data table.");
+                       }
+                       return result;
+               }
+
+               public bool TryGetTableData (UInt32 tag, out byte[] tableData)
+               {
+                       var length = GetTableSize (tag);
+                       var buffer = new byte[length];
+                       fixed (byte* b = buffer) {
+                               if (!TryGetTableData (tag, 0, length, (IntPtr)b)) {
+                                       tableData = null;
+                                       return false;
+                               }
+                       }
+                       tableData = buffer;
+                       return true;
+               }
+
+               public bool TryGetTableData (UInt32 tag, int offset, int length, IntPtr tableData)
+               {
+                       var actual = SkiaApi.sk_typeface_get_table_data (Handle, tag, (IntPtr)offset, (IntPtr)length, (byte*)tableData);
+                       return actual != IntPtr.Zero;
+               }
+
+               // CountGlyphs (string/char)
+
+               public int CountGlyphs (string str) =>
+                       CountGlyphs (str.AsSpan ());
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CountGlyphs(string) instead.")]
+               public int CountGlyphs (string str, SKEncoding encoding) =>
+                       CountGlyphs (str.AsSpan ());
+
+               public int CountGlyphs (ReadOnlySpan<char> str)
+               {
+                       var bytes = StringUtilities.GetEncodedText (str, SKTextEncoding.Utf16);
+                       return CountGlyphs (bytes, SKTextEncoding.Utf16);
+               }
+
+               // CountGlyphs (byte[])
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CountGlyphs(byte[], SKTextEncoding) instead.")]
+               public int CountGlyphs (byte[] str, SKEncoding encoding) =>
+                       CountGlyphs (str.AsSpan (), encoding.ToTextEncoding ());
+
+               public int CountGlyphs (byte[] str, SKTextEncoding encoding) =>
+                       CountGlyphs (str.AsSpan (), encoding);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CountGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
+               public int CountGlyphs (ReadOnlySpan<byte> str, SKEncoding encoding) =>
+                       CountGlyphs (str, encoding.ToTextEncoding ());
+
+               public int CountGlyphs (ReadOnlySpan<byte> str, SKTextEncoding encoding)
+               {
+                       fixed (byte* p = str) {
+                               return CountGlyphs ((IntPtr)p, str.Length, encoding);
+                       }
+               }
+
+               // CountGlyphs (IntPtr)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use CountGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
+               public int CountGlyphs (IntPtr str, int strLen, SKEncoding encoding) =>
+                       CountGlyphs (str, strLen, encoding.ToTextEncoding ());
+
+               public int CountGlyphs (IntPtr str, int strLen, SKTextEncoding encoding)
+               {
+                       if (str == IntPtr.Zero && strLen != 0)
+                               throw new ArgumentNullException (nameof (str));
+
+                       return SkiaApi.sk_typeface_chars_to_glyphs (Handle, (byte*)str, encoding.ToEncoding (), null, strLen);
+               }
+
+               // GetGlyphs (string/char, out)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(string) instead.")]
+               public int GetGlyphs (string text, out ushort[] glyphs)
+               {
+                       glyphs = GetGlyphs (text);
+                       return glyphs.Length;
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(string) instead.")]
+               public int GetGlyphs (string text, SKEncoding encoding, out ushort[] glyphs) =>
+                       GetGlyphs (text, out glyphs);
+
+               // GetGlyphs (byte[], out)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(byte[], SKTextEncoding) instead.")]
+               public int GetGlyphs (byte[] text, SKEncoding encoding, out ushort[] glyphs) =>
+                       GetGlyphs (text.AsSpan (), encoding, out glyphs);
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
+               public int GetGlyphs (ReadOnlySpan<byte> text, SKEncoding encoding, out ushort[] glyphs)
+               {
+                       glyphs = GetGlyphs (text, encoding);
+                       return glyphs.Length;
+               }
+
+               // GetGlyphs (IntPtr, out)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding) instead.")]
+               public int GetGlyphs (IntPtr text, int length, SKEncoding encoding, out ushort[] glyphs)
+               {
+                       glyphs = GetGlyphs (text, length, encoding);
+                       return glyphs.Length;
+               }
+
+               // GetGlyphs (string/char, out)
+
+               public ushort[] GetGlyphs (string text) =>
+                       GetGlyphs (text.AsSpan ());
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(string) instead.")]
+               public ushort[] GetGlyphs (string text, SKEncoding encoding) =>
+                       GetGlyphs (text.AsSpan ());
+
+               public ushort[] GetGlyphs (ReadOnlySpan<char> text)
+               {
+                       fixed (void* t = text) {
+                               return GetGlyphs ((IntPtr)t, text.Length, SKTextEncoding.Utf16);
+                       }
+               }
+
+               // GetGlyphs (byte[], out)
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
+               public ushort[] GetGlyphs (byte[] text, SKEncoding encoding) =>
+                       GetGlyphs (text.AsSpan (), encoding.ToTextEncoding ());
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
+               public ushort[] GetGlyphs (ReadOnlySpan<byte> text, SKEncoding encoding) =>
+                       GetGlyphs (text, encoding.ToTextEncoding ());
+
+               public ushort[] GetGlyphs (ReadOnlySpan<byte> text, SKTextEncoding encoding)
+               {
+                       fixed (void* t = text) {
+                               return GetGlyphs ((IntPtr)t, text.Length, encoding);
+                       }
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding) instead.")]
+               public ushort[] GetGlyphs (IntPtr text, int length, SKEncoding encoding) =>
+                       GetGlyphs (text, length, encoding.ToTextEncoding ());
+
+               public ushort[] GetGlyphs (IntPtr text, int length, SKTextEncoding encoding)
+               {
+                       if (text == IntPtr.Zero && length != 0)
+                               throw new ArgumentNullException (nameof (text));
+
+                       var n = SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding.ToEncoding (), null, length);
+                       if (n <= 0)
+                               return new ushort[0];
+
+                       var glyphs = new ushort[n];
+                       fixed (ushort* gp = glyphs) {
+                               SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding.ToEncoding (), gp, n);
+                       }
+                       return glyphs;
+               }
+
+               // ContainsGlyphs
+
+               public bool ContainsGlyphs (string text) =>
+                       ContainsGlyphs (GetGlyphs (text));
+
+               public bool ContainsGlyphs (ReadOnlySpan<char> text) =>
+                       ContainsGlyphs (GetGlyphs (text));
+
+               public bool ContainsGlyphs (ReadOnlySpan<byte> text, SKTextEncoding encoding) =>
+                       ContainsGlyphs (GetGlyphs (text, encoding));
+
+               public bool ContainsGlyphs (IntPtr text, int length, SKTextEncoding encoding) =>
+                       ContainsGlyphs (GetGlyphs (text, length, encoding));
+
+               private bool ContainsGlyphs (ushort[] glyphs) =>
+                       Array.IndexOf (glyphs, 0) != -1;
+
+               // OpenStream
+
+               public SKStreamAsset OpenStream () =>
+                       OpenStream (out _);
+
+               public SKStreamAsset OpenStream (out int ttcIndex)
+               {
+                       fixed (int* ttc = &ttcIndex) {
+                               return GetObject<SKStreamAssetImplementation> (SkiaApi.sk_typeface_open_stream (Handle, ttc));
+                       }
+               }
+
+               // GetKerningPairAdjustments
+
+               public int[] GetKerningPairAdjustments (ReadOnlySpan<ushort> glyphs)
+               {
+                       var adjustments = new int[glyphs.Length];
+                       fixed (ushort* gp = glyphs)
+                       fixed (int* ap = adjustments) {
+                               SkiaApi.sk_typeface_get_kerning_pair_adjustments (Handle, gp, glyphs.Length, ap);
+                       }
+                       return adjustments;
+               }
+
+               //
+
+               private sealed class SKTypefaceStatic : SKTypeface
+               {
+                       internal SKTypefaceStatic (IntPtr x)
+                               : base (x, false)
+                       {
+                               IgnorePublicDispose = true;
+                       }
+
+                       protected override void Dispose (bool disposing)
+                       {
+                               // do not dispose
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKVertices.cs b/src/XSF/SkiaSharp/SKVertices.cs
new file mode 100644 (file)
index 0000000..7d73aec
--- /dev/null
@@ -0,0 +1,55 @@
+using System;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Text;
+using System.ComponentModel;
+
+namespace SkiaSharp
+{
+       public unsafe class SKVertices : SKObject, ISKNonVirtualReferenceCounted
+       {
+               [Preserve]
+               internal SKVertices (IntPtr x, bool owns)
+                       : base (x, owns)
+               {
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               void ISKNonVirtualReferenceCounted.ReferenceNative () => SkiaApi.sk_vertices_ref (Handle);
+
+               void ISKNonVirtualReferenceCounted.UnreferenceNative () => SkiaApi.sk_vertices_unref (Handle);
+
+               public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKColor[] colors)
+               {
+                       return CreateCopy (vmode, positions, null, colors, null);
+               }
+
+               public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKPoint[] texs, SKColor[] colors)
+               {
+                       return CreateCopy (vmode, positions, texs, colors, null);
+               }
+
+               public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKPoint[] texs, SKColor[] colors, UInt16[] indices)
+               {
+                       if (positions == null)
+                               throw new ArgumentNullException (nameof (positions));
+
+                       if (texs != null && positions.Length != texs.Length)
+                               throw new ArgumentException ("The number of texture coordinates must match the number of vertices.", nameof (texs));
+                       if (colors != null && positions.Length != colors.Length)
+                               throw new ArgumentException ("The number of colors must match the number of vertices.", nameof (colors));
+
+                       var vertexCount = positions.Length;
+                       var indexCount = indices?.Length ?? 0;
+
+                       fixed (SKPoint* p = positions)
+                       fixed (SKPoint* t = texs)
+                       fixed (SKColor* c = colors)
+                       fixed (UInt16* i = indices) {
+                               return GetObject<SKVertices> (SkiaApi.sk_vertices_make_copy (vmode, vertexCount, p, t, (uint*)c, indexCount, i));
+                       }
+               }
+       }
+}
diff --git a/src/XSF/SkiaSharp/SKXml.cs b/src/XSF/SkiaSharp/SKXml.cs
new file mode 100644 (file)
index 0000000..7aaaa0f
--- /dev/null
@@ -0,0 +1,37 @@
+using System;
+
+namespace SkiaSharp
+{
+       public abstract class SKXmlWriter : SKObject
+       {
+               internal SKXmlWriter (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+       }
+
+       public class SKXmlStreamWriter : SKXmlWriter
+       {
+               [Preserve]
+               internal SKXmlStreamWriter (IntPtr h, bool owns)
+                       : base (h, owns)
+               {
+               }
+               
+               public SKXmlStreamWriter (SKWStream stream)
+                       : this (IntPtr.Zero, true)
+               {
+                       if (stream == null) {
+                               throw new ArgumentNullException (nameof (stream));
+                       }
+
+                       Handle = SkiaApi.sk_xmlstreamwriter_new (stream.Handle);
+               }
+
+               protected override void Dispose (bool disposing) =>
+                       base.Dispose (disposing);
+
+               protected override void DisposeNative () =>
+                       SkiaApi.sk_xmlstreamwriter_delete (Handle);
+       }
+}
diff --git a/src/XSF/SkiaSharp/SkiaApi.cs b/src/XSF/SkiaSharp/SkiaApi.cs
new file mode 100644 (file)
index 0000000..529ff54
--- /dev/null
@@ -0,0 +1,7 @@
+namespace SkiaSharp
+{
+       internal partial class SkiaApi
+       {
+               private const string SKIA = "libSkiaSharp.1.68.2.so";
+       }
+}
diff --git a/src/XSF/SkiaSharp/SkiaApi.generated.cs b/src/XSF/SkiaSharp/SkiaApi.generated.cs
new file mode 100644 (file)
index 0000000..6b6710c
--- /dev/null
@@ -0,0 +1,6137 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SkiaSharp
+{
+       #region Class declarations
+
+       using gr_backendrendertarget_t = IntPtr;
+       using gr_backendtexture_t = IntPtr;
+       using gr_context_t = IntPtr;
+       using gr_glinterface_t = IntPtr;
+       using sk_3dview_t = IntPtr;
+       using sk_bitmap_t = IntPtr;
+       using sk_canvas_t = IntPtr;
+       using sk_codec_t = IntPtr;
+       using sk_colorfilter_t = IntPtr;
+       using sk_colorspace_t = IntPtr;
+       using sk_colortable_t = IntPtr;
+       using sk_data_t = IntPtr;
+       using sk_document_t = IntPtr;
+       using sk_drawable_t = IntPtr;
+       using sk_fontmgr_t = IntPtr;
+       using sk_fontstyle_t = IntPtr;
+       using sk_fontstyleset_t = IntPtr;
+       using sk_image_t = IntPtr;
+       using sk_imagefilter_croprect_t = IntPtr;
+       using sk_imagefilter_t = IntPtr;
+       using sk_manageddrawable_t = IntPtr;
+       using sk_maskfilter_t = IntPtr;
+       using sk_matrix44_t = IntPtr;
+       using sk_nodraw_canvas_t = IntPtr;
+       using sk_nvrefcnt_t = IntPtr;
+       using sk_nway_canvas_t = IntPtr;
+       using sk_opbuilder_t = IntPtr;
+       using sk_overdraw_canvas_t = IntPtr;
+       using sk_paint_t = IntPtr;
+       using sk_path_effect_t = IntPtr;
+       using sk_path_iterator_t = IntPtr;
+       using sk_path_rawiterator_t = IntPtr;
+       using sk_path_t = IntPtr;
+       using sk_pathmeasure_t = IntPtr;
+       using sk_picture_recorder_t = IntPtr;
+       using sk_picture_t = IntPtr;
+       using sk_pixelref_factory_t = IntPtr;
+       using sk_pixmap_t = IntPtr;
+       using sk_refcnt_t = IntPtr;
+       using sk_region_cliperator_t = IntPtr;
+       using sk_region_iterator_t = IntPtr;
+       using sk_region_spanerator_t = IntPtr;
+       using sk_region_t = IntPtr;
+       using sk_rrect_t = IntPtr;
+       using sk_shader_t = IntPtr;
+       using sk_stream_asset_t = IntPtr;
+       using sk_stream_filestream_t = IntPtr;
+       using sk_stream_managedstream_t = IntPtr;
+       using sk_stream_memorystream_t = IntPtr;
+       using sk_stream_streamrewindable_t = IntPtr;
+       using sk_stream_t = IntPtr;
+       using sk_string_t = IntPtr;
+       using sk_surface_t = IntPtr;
+       using sk_surfaceprops_t = IntPtr;
+       using sk_svgcanvas_t = IntPtr;
+       using sk_textblob_builder_t = IntPtr;
+       using sk_textblob_t = IntPtr;
+       using sk_typeface_t = IntPtr;
+       using sk_vertices_t = IntPtr;
+       using sk_wstream_dynamicmemorystream_t = IntPtr;
+       using sk_wstream_filestream_t = IntPtr;
+       using sk_wstream_managedstream_t = IntPtr;
+       using sk_wstream_t = IntPtr;
+       using sk_xmlstreamwriter_t = IntPtr;
+       using sk_xmlwriter_t = IntPtr;
+
+       #endregion
+
+       internal unsafe partial class SkiaApi
+       {
+               #region gr_context.h
+
+               // void gr_backendrendertarget_delete(gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_backendrendertarget_delete (gr_backendrendertarget_t rendertarget);
+
+               // gr_backend_t gr_backendrendertarget_get_backend(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern GRBackend gr_backendrendertarget_get_backend (gr_backendrendertarget_t rendertarget);
+
+               // bool gr_backendrendertarget_get_gl_framebufferinfo(const gr_backendrendertarget_t* rendertarget, gr_gl_framebufferinfo_t* glInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_backendrendertarget_get_gl_framebufferinfo (gr_backendrendertarget_t rendertarget, GRGlFramebufferInfo* glInfo);
+
+               // int gr_backendrendertarget_get_height(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendrendertarget_get_height (gr_backendrendertarget_t rendertarget);
+
+               // int gr_backendrendertarget_get_samples(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendrendertarget_get_samples (gr_backendrendertarget_t rendertarget);
+
+               // int gr_backendrendertarget_get_stencils(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendrendertarget_get_stencils (gr_backendrendertarget_t rendertarget);
+
+               // int gr_backendrendertarget_get_width(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendrendertarget_get_width (gr_backendrendertarget_t rendertarget);
+
+               // bool gr_backendrendertarget_is_valid(const gr_backendrendertarget_t* rendertarget)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_backendrendertarget_is_valid (gr_backendrendertarget_t rendertarget);
+
+               // gr_backendrendertarget_t* gr_backendrendertarget_new_gl(int width, int height, int samples, int stencils, const gr_gl_framebufferinfo_t* glInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_backendrendertarget_t gr_backendrendertarget_new_gl (Int32 width, Int32 height, Int32 samples, Int32 stencils, GRGlFramebufferInfo* glInfo);
+
+               // void gr_backendtexture_delete(gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_backendtexture_delete (gr_backendtexture_t texture);
+
+               // gr_backend_t gr_backendtexture_get_backend(const gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern GRBackend gr_backendtexture_get_backend (gr_backendtexture_t texture);
+
+               // bool gr_backendtexture_get_gl_textureinfo(const gr_backendtexture_t* texture, gr_gl_textureinfo_t* glInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_backendtexture_get_gl_textureinfo (gr_backendtexture_t texture, GRGlTextureInfo* glInfo);
+
+               // int gr_backendtexture_get_height(const gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendtexture_get_height (gr_backendtexture_t texture);
+
+               // int gr_backendtexture_get_width(const gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_backendtexture_get_width (gr_backendtexture_t texture);
+
+               // bool gr_backendtexture_has_mipmaps(const gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_backendtexture_has_mipmaps (gr_backendtexture_t texture);
+
+               // bool gr_backendtexture_is_valid(const gr_backendtexture_t* texture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_backendtexture_is_valid (gr_backendtexture_t texture);
+
+               // gr_backendtexture_t* gr_backendtexture_new_gl(int width, int height, bool mipmapped, const gr_gl_textureinfo_t* glInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_backendtexture_t gr_backendtexture_new_gl (Int32 width, Int32 height, [MarshalAs (UnmanagedType.I1)] bool mipmapped, GRGlTextureInfo* glInfo);
+
+               // void gr_context_abandon_context(gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_abandon_context (gr_context_t context);
+
+               // void gr_context_flush(gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_flush (gr_context_t context);
+
+               // gr_backend_t gr_context_get_backend(gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern GRBackend gr_context_get_backend (gr_context_t context);
+
+               // int gr_context_get_max_surface_sample_count_for_color_type(gr_context_t* context, sk_colortype_t colorType)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 gr_context_get_max_surface_sample_count_for_color_type (gr_context_t context, SKColorType colorType);
+
+               // void gr_context_get_resource_cache_limits(gr_context_t* context, int* maxResources, size_t* maxResourceBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_get_resource_cache_limits (gr_context_t context, Int32* maxResources, /* size_t */ IntPtr* maxResourceBytes);
+
+               // void gr_context_get_resource_cache_usage(gr_context_t* context, int* maxResources, size_t* maxResourceBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_get_resource_cache_usage (gr_context_t context, Int32* maxResources, /* size_t */ IntPtr* maxResourceBytes);
+
+               // gr_context_t* gr_context_make_gl(const gr_glinterface_t* glInterface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_context_t gr_context_make_gl (gr_glinterface_t glInterface);
+
+               // void gr_context_release_resources_and_abandon_context(gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_release_resources_and_abandon_context (gr_context_t context);
+
+               // void gr_context_reset_context(gr_context_t* context, uint32_t state)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_reset_context (gr_context_t context, UInt32 state);
+
+               // void gr_context_set_resource_cache_limits(gr_context_t* context, int maxResources, size_t maxResourceBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_set_resource_cache_limits (gr_context_t context, Int32 maxResources, /* size_t */ IntPtr maxResourceBytes);
+
+               // void gr_context_unref(gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_context_unref (gr_context_t context);
+
+               // const gr_glinterface_t* gr_glinterface_assemble_gl_interface(void* ctx, gr_gl_get_proc get)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_glinterface_t gr_glinterface_assemble_gl_interface (void* ctx, GRGlGetProcProxyDelegate get);
+
+               // const gr_glinterface_t* gr_glinterface_assemble_gles_interface(void* ctx, gr_gl_get_proc get)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_glinterface_t gr_glinterface_assemble_gles_interface (void* ctx, GRGlGetProcProxyDelegate get);
+
+               // const gr_glinterface_t* gr_glinterface_assemble_interface(void* ctx, gr_gl_get_proc get)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_glinterface_t gr_glinterface_assemble_interface (void* ctx, GRGlGetProcProxyDelegate get);
+
+               // const gr_glinterface_t* gr_glinterface_create_native_interface()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern gr_glinterface_t gr_glinterface_create_native_interface ();
+
+               // bool gr_glinterface_has_extension(const gr_glinterface_t* glInterface, const char* extension)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_glinterface_has_extension (gr_glinterface_t glInterface, [MarshalAs (UnmanagedType.LPStr)] String extension);
+
+               // void gr_glinterface_unref(const gr_glinterface_t* glInterface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void gr_glinterface_unref (gr_glinterface_t glInterface);
+
+               // bool gr_glinterface_validate(const gr_glinterface_t* glInterface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool gr_glinterface_validate (gr_glinterface_t glInterface);
+
+               #endregion
+
+               #region sk_bitmap.h
+
+               // void sk_bitmap_destructor(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_destructor (sk_bitmap_t cbitmap);
+
+               // void sk_bitmap_erase(sk_bitmap_t* cbitmap, sk_color_t color)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_erase (sk_bitmap_t cbitmap, UInt32 color);
+
+               // void sk_bitmap_erase_rect(sk_bitmap_t* cbitmap, sk_color_t color, sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_erase_rect (sk_bitmap_t cbitmap, UInt32 color, SKRectI* rect);
+
+               // bool sk_bitmap_extract_alpha(sk_bitmap_t* cbitmap, sk_bitmap_t* dst, const sk_paint_t* paint, sk_ipoint_t* offset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_extract_alpha (sk_bitmap_t cbitmap, sk_bitmap_t dst, sk_paint_t paint, SKPointI* offset);
+
+               // bool sk_bitmap_extract_subset(sk_bitmap_t* cbitmap, sk_bitmap_t* dst, sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_extract_subset (sk_bitmap_t cbitmap, sk_bitmap_t dst, SKRectI* subset);
+
+               // void* sk_bitmap_get_addr(sk_bitmap_t* cbitmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_bitmap_get_addr (sk_bitmap_t cbitmap, Int32 x, Int32 y);
+
+               // uint16_t sk_bitmap_get_addr_16(sk_bitmap_t* cbitmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt16 sk_bitmap_get_addr_16 (sk_bitmap_t cbitmap, Int32 x, Int32 y);
+
+               // uint32_t sk_bitmap_get_addr_32(sk_bitmap_t* cbitmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_bitmap_get_addr_32 (sk_bitmap_t cbitmap, Int32 x, Int32 y);
+
+               // uint8_t sk_bitmap_get_addr_8(sk_bitmap_t* cbitmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Byte sk_bitmap_get_addr_8 (sk_bitmap_t cbitmap, Int32 x, Int32 y);
+
+               // size_t sk_bitmap_get_byte_count(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_bitmap_get_byte_count (sk_bitmap_t cbitmap);
+
+               // void sk_bitmap_get_info(sk_bitmap_t* cbitmap, sk_imageinfo_t* info)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_get_info (sk_bitmap_t cbitmap, SKImageInfoNative* info);
+
+               // sk_color_t sk_bitmap_get_pixel_color(sk_bitmap_t* cbitmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_bitmap_get_pixel_color (sk_bitmap_t cbitmap, Int32 x, Int32 y);
+
+               // void sk_bitmap_get_pixel_colors(sk_bitmap_t* cbitmap, sk_color_t* colors)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_get_pixel_colors (sk_bitmap_t cbitmap, UInt32* colors);
+
+               // void* sk_bitmap_get_pixels(sk_bitmap_t* cbitmap, size_t* length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_bitmap_get_pixels (sk_bitmap_t cbitmap, /* size_t */ IntPtr* length);
+
+               // size_t sk_bitmap_get_row_bytes(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_bitmap_get_row_bytes (sk_bitmap_t cbitmap);
+
+               // bool sk_bitmap_install_mask_pixels(sk_bitmap_t* cbitmap, const sk_mask_t* cmask)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_install_mask_pixels (sk_bitmap_t cbitmap, SKMask* cmask);
+
+               // bool sk_bitmap_install_pixels(sk_bitmap_t* cbitmap, const sk_imageinfo_t* cinfo, void* pixels, size_t rowBytes, const sk_bitmap_release_proc releaseProc, void* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_install_pixels (sk_bitmap_t cbitmap, SKImageInfoNative* cinfo, void* pixels, /* size_t */ IntPtr rowBytes, SKBitmapReleaseProxyDelegate releaseProc, void* context);
+
+               // bool sk_bitmap_install_pixels_with_pixmap(sk_bitmap_t* cbitmap, const sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_install_pixels_with_pixmap (sk_bitmap_t cbitmap, sk_pixmap_t cpixmap);
+
+               // bool sk_bitmap_is_immutable(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_is_immutable (sk_bitmap_t cbitmap);
+
+               // bool sk_bitmap_is_null(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_is_null (sk_bitmap_t cbitmap);
+
+               // bool sk_bitmap_is_volatile(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_is_volatile (sk_bitmap_t cbitmap);
+
+               // sk_bitmap_t* sk_bitmap_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_bitmap_t sk_bitmap_new ();
+
+               // void sk_bitmap_notify_pixels_changed(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_notify_pixels_changed (sk_bitmap_t cbitmap);
+
+               // bool sk_bitmap_peek_pixels(sk_bitmap_t* cbitmap, sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_peek_pixels (sk_bitmap_t cbitmap, sk_pixmap_t cpixmap);
+
+               // bool sk_bitmap_ready_to_draw(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_ready_to_draw (sk_bitmap_t cbitmap);
+
+               // void sk_bitmap_reset(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_reset (sk_bitmap_t cbitmap);
+
+               // void sk_bitmap_set_immutable(sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_set_immutable (sk_bitmap_t cbitmap);
+
+               // void sk_bitmap_set_pixel_color(sk_bitmap_t* cbitmap, int x, int y, sk_color_t color)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_set_pixel_color (sk_bitmap_t cbitmap, Int32 x, Int32 y, UInt32 color);
+
+               // void sk_bitmap_set_pixel_colors(sk_bitmap_t* cbitmap, const sk_color_t* colors)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_set_pixel_colors (sk_bitmap_t cbitmap, UInt32* colors);
+
+               // void sk_bitmap_set_pixels(sk_bitmap_t* cbitmap, void* pixels)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_set_pixels (sk_bitmap_t cbitmap, void* pixels);
+
+               // void sk_bitmap_set_volatile(sk_bitmap_t* cbitmap, bool value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_set_volatile (sk_bitmap_t cbitmap, [MarshalAs (UnmanagedType.I1)] bool value);
+
+               // void sk_bitmap_swap(sk_bitmap_t* cbitmap, sk_bitmap_t* cother)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_bitmap_swap (sk_bitmap_t cbitmap, sk_bitmap_t cother);
+
+               // bool sk_bitmap_try_alloc_pixels(sk_bitmap_t* cbitmap, const sk_imageinfo_t* requestedInfo, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_try_alloc_pixels (sk_bitmap_t cbitmap, SKImageInfoNative* requestedInfo, /* size_t */ IntPtr rowBytes);
+
+               // bool sk_bitmap_try_alloc_pixels_with_flags(sk_bitmap_t* cbitmap, const sk_imageinfo_t* requestedInfo, uint32_t flags)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_bitmap_try_alloc_pixels_with_flags (sk_bitmap_t cbitmap, SKImageInfoNative* requestedInfo, UInt32 flags);
+
+               #endregion
+
+               #region sk_canvas.h
+
+               // void sk_canvas_clear(sk_canvas_t*, sk_color_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_clear (sk_canvas_t param0, UInt32 param1);
+
+               // void sk_canvas_clip_path_with_operation(sk_canvas_t* t, const sk_path_t* crect, sk_clipop_t op, bool doAA)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_clip_path_with_operation (sk_canvas_t t, sk_path_t crect, SKClipOperation op, [MarshalAs (UnmanagedType.I1)] bool doAA);
+
+               // void sk_canvas_clip_rect_with_operation(sk_canvas_t* t, const sk_rect_t* crect, sk_clipop_t op, bool doAA)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_clip_rect_with_operation (sk_canvas_t t, SKRect* crect, SKClipOperation op, [MarshalAs (UnmanagedType.I1)] bool doAA);
+
+               // void sk_canvas_clip_region(sk_canvas_t* canvas, const sk_region_t* region, sk_clipop_t op)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_clip_region (sk_canvas_t canvas, sk_region_t region, SKClipOperation op);
+
+               // void sk_canvas_clip_rrect_with_operation(sk_canvas_t* t, const sk_rrect_t* crect, sk_clipop_t op, bool doAA)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_clip_rrect_with_operation (sk_canvas_t t, sk_rrect_t crect, SKClipOperation op, [MarshalAs (UnmanagedType.I1)] bool doAA);
+
+               // void sk_canvas_concat(sk_canvas_t*, const sk_matrix_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_concat (sk_canvas_t param0, SKMatrix* param1);
+
+               // void sk_canvas_destroy(sk_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_destroy (sk_canvas_t param0);
+
+               // void sk_canvas_discard(sk_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_discard (sk_canvas_t param0);
+
+               // void sk_canvas_draw_annotation(sk_canvas_t* t, const sk_rect_t* rect, const char* key, sk_data_t* value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_annotation (sk_canvas_t t, SKRect* rect, /* char */ void* key, sk_data_t value);
+
+               // void sk_canvas_draw_arc(sk_canvas_t* ccanvas, const sk_rect_t* oval, float startAngle, float sweepAngle, bool useCenter, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_arc (sk_canvas_t ccanvas, SKRect* oval, Single startAngle, Single sweepAngle, [MarshalAs (UnmanagedType.I1)] bool useCenter, sk_paint_t paint);
+
+               // void sk_canvas_draw_atlas(sk_canvas_t* ccanvas, const sk_image_t* atlas, const sk_rsxform_t* xform, const sk_rect_t* tex, const sk_color_t* colors, int count, sk_blendmode_t mode, const sk_rect_t* cullRect, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_atlas (sk_canvas_t ccanvas, sk_image_t atlas, SKRotationScaleMatrix* xform, SKRect* tex, UInt32* colors, Int32 count, SKBlendMode mode, SKRect* cullRect, sk_paint_t paint);
+
+               // void sk_canvas_draw_bitmap(sk_canvas_t* ccanvas, const sk_bitmap_t* bitmap, float left, float top, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_bitmap (sk_canvas_t ccanvas, sk_bitmap_t bitmap, Single left, Single top, sk_paint_t paint);
+
+               // void sk_canvas_draw_bitmap_lattice(sk_canvas_t* t, const sk_bitmap_t* bitmap, const sk_lattice_t* lattice, const sk_rect_t* dst, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_bitmap_lattice (sk_canvas_t t, sk_bitmap_t bitmap, SKLatticeInternal* lattice, SKRect* dst, sk_paint_t paint);
+
+               // void sk_canvas_draw_bitmap_nine(sk_canvas_t* t, const sk_bitmap_t* bitmap, const sk_irect_t* center, const sk_rect_t* dst, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_bitmap_nine (sk_canvas_t t, sk_bitmap_t bitmap, SKRectI* center, SKRect* dst, sk_paint_t paint);
+
+               // void sk_canvas_draw_bitmap_rect(sk_canvas_t* ccanvas, const sk_bitmap_t* bitmap, const sk_rect_t* src, const sk_rect_t* dst, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_bitmap_rect (sk_canvas_t ccanvas, sk_bitmap_t bitmap, SKRect* src, SKRect* dst, sk_paint_t paint);
+
+               // void sk_canvas_draw_circle(sk_canvas_t*, float cx, float cy, float rad, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_circle (sk_canvas_t param0, Single cx, Single cy, Single rad, sk_paint_t param4);
+
+               // void sk_canvas_draw_color(sk_canvas_t* ccanvas, sk_color_t color, sk_blendmode_t mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_color (sk_canvas_t ccanvas, UInt32 color, SKBlendMode mode);
+
+               // void sk_canvas_draw_drawable(sk_canvas_t*, sk_drawable_t*, const sk_matrix_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_drawable (sk_canvas_t param0, sk_drawable_t param1, SKMatrix* param2);
+
+               // void sk_canvas_draw_drrect(sk_canvas_t* ccanvas, const sk_rrect_t* outer, const sk_rrect_t* inner, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_drrect (sk_canvas_t ccanvas, sk_rrect_t outer, sk_rrect_t inner, sk_paint_t paint);
+
+               // void sk_canvas_draw_image(sk_canvas_t*, const sk_image_t*, float x, float y, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_image (sk_canvas_t param0, sk_image_t param1, Single x, Single y, sk_paint_t param4);
+
+               // void sk_canvas_draw_image_lattice(sk_canvas_t* t, const sk_image_t* image, const sk_lattice_t* lattice, const sk_rect_t* dst, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_image_lattice (sk_canvas_t t, sk_image_t image, SKLatticeInternal* lattice, SKRect* dst, sk_paint_t paint);
+
+               // void sk_canvas_draw_image_nine(sk_canvas_t* t, const sk_image_t* image, const sk_irect_t* center, const sk_rect_t* dst, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_image_nine (sk_canvas_t t, sk_image_t image, SKRectI* center, SKRect* dst, sk_paint_t paint);
+
+               // void sk_canvas_draw_image_rect(sk_canvas_t*, const sk_image_t*, const sk_rect_t* src, const sk_rect_t* dst, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_image_rect (sk_canvas_t param0, sk_image_t param1, SKRect* src, SKRect* dst, sk_paint_t param4);
+
+               // void sk_canvas_draw_line(sk_canvas_t* ccanvas, float x0, float y0, float x1, float y1, sk_paint_t* cpaint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_line (sk_canvas_t ccanvas, Single x0, Single y0, Single x1, Single y1, sk_paint_t cpaint);
+
+               // void sk_canvas_draw_link_destination_annotation(sk_canvas_t* t, const sk_rect_t* rect, sk_data_t* value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_link_destination_annotation (sk_canvas_t t, SKRect* rect, sk_data_t value);
+
+               // void sk_canvas_draw_named_destination_annotation(sk_canvas_t* t, const sk_point_t* point, sk_data_t* value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_named_destination_annotation (sk_canvas_t t, SKPoint* point, sk_data_t value);
+
+               // void sk_canvas_draw_oval(sk_canvas_t*, const sk_rect_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_oval (sk_canvas_t param0, SKRect* param1, sk_paint_t param2);
+
+               // void sk_canvas_draw_paint(sk_canvas_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_paint (sk_canvas_t param0, sk_paint_t param1);
+
+               // void sk_canvas_draw_patch(sk_canvas_t* ccanvas, const sk_point_t* cubics, const sk_color_t* colors, const sk_point_t* texCoords, sk_blendmode_t mode, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_patch (sk_canvas_t ccanvas, SKPoint* cubics, UInt32* colors, SKPoint* texCoords, SKBlendMode mode, sk_paint_t paint);
+
+               // void sk_canvas_draw_path(sk_canvas_t*, const sk_path_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_path (sk_canvas_t param0, sk_path_t param1, sk_paint_t param2);
+
+               // void sk_canvas_draw_picture(sk_canvas_t*, const sk_picture_t*, const sk_matrix_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_picture (sk_canvas_t param0, sk_picture_t param1, SKMatrix* param2, sk_paint_t param3);
+
+               // void sk_canvas_draw_point(sk_canvas_t*, float, float, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_point (sk_canvas_t param0, Single param1, Single param2, sk_paint_t param3);
+
+               // void sk_canvas_draw_points(sk_canvas_t*, sk_point_mode_t, size_t, const sk_point_t[-1], const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_points (sk_canvas_t param0, SKPointMode param1, /* size_t */ IntPtr param2, SKPoint* param3, sk_paint_t param4);
+
+               // void sk_canvas_draw_pos_text(sk_canvas_t*, const char* text, size_t byteLength, const sk_point_t[-1], const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_pos_text (sk_canvas_t param0, /* char */ void* text, /* size_t */ IntPtr byteLength, SKPoint* param3, sk_paint_t paint);
+
+               // void sk_canvas_draw_rect(sk_canvas_t*, const sk_rect_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_rect (sk_canvas_t param0, SKRect* param1, sk_paint_t param2);
+
+               // void sk_canvas_draw_region(sk_canvas_t*, const sk_region_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_region (sk_canvas_t param0, sk_region_t param1, sk_paint_t param2);
+
+               // void sk_canvas_draw_round_rect(sk_canvas_t*, const sk_rect_t*, float rx, float ry, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_round_rect (sk_canvas_t param0, SKRect* param1, Single rx, Single ry, sk_paint_t param4);
+
+               // void sk_canvas_draw_rrect(sk_canvas_t*, const sk_rrect_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_rrect (sk_canvas_t param0, sk_rrect_t param1, sk_paint_t param2);
+
+               // void sk_canvas_draw_text(sk_canvas_t*, const char* text, size_t byteLength, float x, float y, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_text (sk_canvas_t param0, /* char */ void* text, /* size_t */ IntPtr byteLength, Single x, Single y, sk_paint_t paint);
+
+               // void sk_canvas_draw_text_blob(sk_canvas_t*, sk_textblob_t* text, float x, float y, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_text_blob (sk_canvas_t param0, sk_textblob_t text, Single x, Single y, sk_paint_t paint);
+
+               // void sk_canvas_draw_text_on_path(sk_canvas_t*, const char* text, size_t byteLength, const sk_path_t* path, float hOffset, float vOffset, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_text_on_path (sk_canvas_t param0, /* char */ void* text, /* size_t */ IntPtr byteLength, sk_path_t path, Single hOffset, Single vOffset, sk_paint_t paint);
+
+               // void sk_canvas_draw_url_annotation(sk_canvas_t* t, const sk_rect_t* rect, sk_data_t* value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_url_annotation (sk_canvas_t t, SKRect* rect, sk_data_t value);
+
+               // void sk_canvas_draw_vertices(sk_canvas_t* ccanvas, const sk_vertices_t* vertices, sk_blendmode_t mode, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_draw_vertices (sk_canvas_t ccanvas, sk_vertices_t vertices, SKBlendMode mode, sk_paint_t paint);
+
+               // void sk_canvas_flush(sk_canvas_t* ccanvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_flush (sk_canvas_t ccanvas);
+
+               // bool sk_canvas_get_device_clip_bounds(sk_canvas_t* t, sk_irect_t* cbounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_canvas_get_device_clip_bounds (sk_canvas_t t, SKRectI* cbounds);
+
+               // bool sk_canvas_get_local_clip_bounds(sk_canvas_t* t, sk_rect_t* cbounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_canvas_get_local_clip_bounds (sk_canvas_t t, SKRect* cbounds);
+
+               // int sk_canvas_get_save_count(sk_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_canvas_get_save_count (sk_canvas_t param0);
+
+               // void sk_canvas_get_total_matrix(sk_canvas_t* ccanvas, sk_matrix_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_get_total_matrix (sk_canvas_t ccanvas, SKMatrix* matrix);
+
+               // bool sk_canvas_is_clip_empty(sk_canvas_t* ccanvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_canvas_is_clip_empty (sk_canvas_t ccanvas);
+
+               // bool sk_canvas_is_clip_rect(sk_canvas_t* ccanvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_canvas_is_clip_rect (sk_canvas_t ccanvas);
+
+               // sk_canvas_t* sk_canvas_new_from_bitmap(const sk_bitmap_t* bitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_canvas_new_from_bitmap (sk_bitmap_t bitmap);
+
+               // bool sk_canvas_quick_reject(sk_canvas_t*, const sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_canvas_quick_reject (sk_canvas_t param0, SKRect* param1);
+
+               // void sk_canvas_reset_matrix(sk_canvas_t* ccanvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_reset_matrix (sk_canvas_t ccanvas);
+
+               // void sk_canvas_restore(sk_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_restore (sk_canvas_t param0);
+
+               // void sk_canvas_restore_to_count(sk_canvas_t*, int saveCount)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_restore_to_count (sk_canvas_t param0, Int32 saveCount);
+
+               // void sk_canvas_rotate_degrees(sk_canvas_t*, float degrees)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_rotate_degrees (sk_canvas_t param0, Single degrees);
+
+               // void sk_canvas_rotate_radians(sk_canvas_t*, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_rotate_radians (sk_canvas_t param0, Single radians);
+
+               // int sk_canvas_save(sk_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_canvas_save (sk_canvas_t param0);
+
+               // int sk_canvas_save_layer(sk_canvas_t*, const sk_rect_t*, const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_canvas_save_layer (sk_canvas_t param0, SKRect* param1, sk_paint_t param2);
+
+               // void sk_canvas_scale(sk_canvas_t*, float sx, float sy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_scale (sk_canvas_t param0, Single sx, Single sy);
+
+               // void sk_canvas_set_matrix(sk_canvas_t* ccanvas, const sk_matrix_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_set_matrix (sk_canvas_t ccanvas, SKMatrix* matrix);
+
+               // void sk_canvas_skew(sk_canvas_t*, float sx, float sy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_skew (sk_canvas_t param0, Single sx, Single sy);
+
+               // void sk_canvas_translate(sk_canvas_t*, float dx, float dy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_canvas_translate (sk_canvas_t param0, Single dx, Single dy);
+
+               // void sk_nodraw_canvas_destroy(sk_nodraw_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nodraw_canvas_destroy (sk_nodraw_canvas_t param0);
+
+               // sk_nodraw_canvas_t* sk_nodraw_canvas_new(int width, int height)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_nodraw_canvas_t sk_nodraw_canvas_new (Int32 width, Int32 height);
+
+               // void sk_nway_canvas_add_canvas(sk_nway_canvas_t*, sk_canvas_t* canvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nway_canvas_add_canvas (sk_nway_canvas_t param0, sk_canvas_t canvas);
+
+               // void sk_nway_canvas_destroy(sk_nway_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nway_canvas_destroy (sk_nway_canvas_t param0);
+
+               // sk_nway_canvas_t* sk_nway_canvas_new(int width, int height)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_nway_canvas_t sk_nway_canvas_new (Int32 width, Int32 height);
+
+               // void sk_nway_canvas_remove_all(sk_nway_canvas_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nway_canvas_remove_all (sk_nway_canvas_t param0);
+
+               // void sk_nway_canvas_remove_canvas(sk_nway_canvas_t*, sk_canvas_t* canvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nway_canvas_remove_canvas (sk_nway_canvas_t param0, sk_canvas_t canvas);
+
+               // void sk_overdraw_canvas_destroy(sk_overdraw_canvas_t* canvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_overdraw_canvas_destroy (sk_overdraw_canvas_t canvas);
+
+               // sk_overdraw_canvas_t* sk_overdraw_canvas_new(sk_canvas_t* canvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_overdraw_canvas_t sk_overdraw_canvas_new (sk_canvas_t canvas);
+
+               #endregion
+
+               #region sk_codec.h
+
+               // void sk_codec_destroy(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_codec_destroy (sk_codec_t codec);
+
+               // sk_encoded_image_format_t sk_codec_get_encoded_format(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKEncodedImageFormat sk_codec_get_encoded_format (sk_codec_t codec);
+
+               // int sk_codec_get_frame_count(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_codec_get_frame_count (sk_codec_t codec);
+
+               // void sk_codec_get_frame_info(sk_codec_t* codec, sk_codec_frameinfo_t* frameInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_codec_get_frame_info (sk_codec_t codec, SKCodecFrameInfo* frameInfo);
+
+               // bool sk_codec_get_frame_info_for_index(sk_codec_t* codec, int index, sk_codec_frameinfo_t* frameInfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_codec_get_frame_info_for_index (sk_codec_t codec, Int32 index, SKCodecFrameInfo* frameInfo);
+
+               // void sk_codec_get_info(sk_codec_t* codec, sk_imageinfo_t* info)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_codec_get_info (sk_codec_t codec, SKImageInfoNative* info);
+
+               // sk_encodedorigin_t sk_codec_get_origin(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKEncodedOrigin sk_codec_get_origin (sk_codec_t codec);
+
+               // sk_codec_result_t sk_codec_get_pixels(sk_codec_t* codec, const sk_imageinfo_t* info, void* pixels, size_t rowBytes, const sk_codec_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKCodecResult sk_codec_get_pixels (sk_codec_t codec, SKImageInfoNative* info, void* pixels, /* size_t */ IntPtr rowBytes, SKCodecOptionsInternal* options);
+
+               // int sk_codec_get_repetition_count(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_codec_get_repetition_count (sk_codec_t codec);
+
+               // void sk_codec_get_scaled_dimensions(sk_codec_t* codec, float desiredScale, sk_isize_t* dimensions)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_codec_get_scaled_dimensions (sk_codec_t codec, Single desiredScale, SKSizeI* dimensions);
+
+               // sk_codec_scanline_order_t sk_codec_get_scanline_order(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKCodecScanlineOrder sk_codec_get_scanline_order (sk_codec_t codec);
+
+               // int sk_codec_get_scanlines(sk_codec_t* codec, void* dst, int countLines, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_codec_get_scanlines (sk_codec_t codec, void* dst, Int32 countLines, /* size_t */ IntPtr rowBytes);
+
+               // bool sk_codec_get_valid_subset(sk_codec_t* codec, sk_irect_t* desiredSubset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_codec_get_valid_subset (sk_codec_t codec, SKRectI* desiredSubset);
+
+               // sk_codec_result_t sk_codec_incremental_decode(sk_codec_t* codec, int* rowsDecoded)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKCodecResult sk_codec_incremental_decode (sk_codec_t codec, Int32* rowsDecoded);
+
+               // size_t sk_codec_min_buffered_bytes_needed()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_codec_min_buffered_bytes_needed ();
+
+               // sk_codec_t* sk_codec_new_from_data(sk_data_t* data)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_codec_t sk_codec_new_from_data (sk_data_t data);
+
+               // sk_codec_t* sk_codec_new_from_stream(sk_stream_t* stream, sk_codec_result_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_codec_t sk_codec_new_from_stream (sk_stream_t stream, SKCodecResult* result);
+
+               // int sk_codec_next_scanline(sk_codec_t* codec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_codec_next_scanline (sk_codec_t codec);
+
+               // int sk_codec_output_scanline(sk_codec_t* codec, int inputScanline)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_codec_output_scanline (sk_codec_t codec, Int32 inputScanline);
+
+               // bool sk_codec_skip_scanlines(sk_codec_t* codec, int countLines)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_codec_skip_scanlines (sk_codec_t codec, Int32 countLines);
+
+               // sk_codec_result_t sk_codec_start_incremental_decode(sk_codec_t* codec, const sk_imageinfo_t* info, void* pixels, size_t rowBytes, const sk_codec_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKCodecResult sk_codec_start_incremental_decode (sk_codec_t codec, SKImageInfoNative* info, void* pixels, /* size_t */ IntPtr rowBytes, SKCodecOptionsInternal* options);
+
+               // sk_codec_result_t sk_codec_start_scanline_decode(sk_codec_t* codec, const sk_imageinfo_t* info, const sk_codec_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKCodecResult sk_codec_start_scanline_decode (sk_codec_t codec, SKImageInfoNative* info, SKCodecOptionsInternal* options);
+
+               #endregion
+
+               #region sk_colorfilter.h
+
+               // sk_colorfilter_t* sk_colorfilter_new_color_matrix(const float[20] array = 20)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_color_matrix (Single* array);
+
+               // sk_colorfilter_t* sk_colorfilter_new_compose(sk_colorfilter_t* outer, sk_colorfilter_t* inner)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_compose (sk_colorfilter_t outer, sk_colorfilter_t inner);
+
+               // sk_colorfilter_t* sk_colorfilter_new_high_contrast(const sk_highcontrastconfig_t* config)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_high_contrast (SKHighContrastConfig* config);
+
+               // sk_colorfilter_t* sk_colorfilter_new_lighting(sk_color_t mul, sk_color_t add)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_lighting (UInt32 mul, UInt32 add);
+
+               // sk_colorfilter_t* sk_colorfilter_new_luma_color()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_luma_color ();
+
+               // sk_colorfilter_t* sk_colorfilter_new_mode(sk_color_t c, sk_blendmode_t mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_mode (UInt32 c, SKBlendMode mode);
+
+               // sk_colorfilter_t* sk_colorfilter_new_table(const uint8_t[256] table = 256)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_table (Byte* table);
+
+               // sk_colorfilter_t* sk_colorfilter_new_table_argb(const uint8_t[256] tableA = 256, const uint8_t[256] tableR = 256, const uint8_t[256] tableG = 256, const uint8_t[256] tableB = 256)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_colorfilter_new_table_argb (Byte* tableA, Byte* tableR, Byte* tableG, Byte* tableB);
+
+               // void sk_colorfilter_unref(sk_colorfilter_t* filter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_colorfilter_unref (sk_colorfilter_t filter);
+
+               #endregion
+
+               #region sk_colorspace.h
+
+               // void sk_color4f_from_color(sk_color_t color, sk_color4f_t* color4f)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_color4f_from_color (UInt32 color, SKColorF* color4f);
+
+               // void sk_color4f_pin(const sk_color4f_t* color4f, sk_color4f_t* pinned)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_color4f_pin (SKColorF* color4f, SKColorF* pinned);
+
+               // sk_color_t sk_color4f_to_color(const sk_color4f_t* color4f)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_color4f_to_color (SKColorF* color4f);
+
+               // const sk_matrix44_t* sk_colorspace_as_from_xyzd50(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_colorspace_as_from_xyzd50 (sk_colorspace_t cColorSpace);
+
+               // const sk_matrix44_t* sk_colorspace_as_to_xyzd50(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_colorspace_as_to_xyzd50 (sk_colorspace_t cColorSpace);
+
+               // bool sk_colorspace_equals(const sk_colorspace_t* src, const sk_colorspace_t* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_equals (sk_colorspace_t src, sk_colorspace_t dst);
+
+               // bool sk_colorspace_gamma_close_to_srgb(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_gamma_close_to_srgb (sk_colorspace_t cColorSpace);
+
+               // sk_gamma_named_t sk_colorspace_gamma_get_gamma_named(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKNamedGamma sk_colorspace_gamma_get_gamma_named (sk_colorspace_t cColorSpace);
+
+               // sk_colorspace_type_t sk_colorspace_gamma_get_type(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKColorSpaceType sk_colorspace_gamma_get_type (sk_colorspace_t cColorSpace);
+
+               // bool sk_colorspace_gamma_is_linear(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_gamma_is_linear (sk_colorspace_t cColorSpace);
+
+               // bool sk_colorspace_is_numerical_transfer_fn(const sk_colorspace_t* cColorSpace, sk_colorspace_transfer_fn_t* fn)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_is_numerical_transfer_fn (sk_colorspace_t cColorSpace, SKColorSpaceTransferFn* fn);
+
+               // bool sk_colorspace_is_srgb(const sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_is_srgb (sk_colorspace_t cColorSpace);
+
+               // sk_colorspace_t* sk_colorspace_new_icc(const void* input, size_t len)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_icc (void* input, /* size_t */ IntPtr len);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_coeffs(const sk_colorspace_transfer_fn_t* coeffs, const sk_matrix44_t* toXYZD50)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_coeffs (SKColorSpaceTransferFn* coeffs, sk_matrix44_t toXYZD50);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_coeffs_and_gamut(const sk_colorspace_transfer_fn_t* coeffs, sk_colorspace_gamut_t gamut)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_coeffs_and_gamut (SKColorSpaceTransferFn* coeffs, SKColorSpaceGamut gamut);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_gamma(sk_colorspace_render_target_gamma_t gamma, const sk_matrix44_t* toXYZD50)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_gamma (SKColorSpaceRenderTargetGamma gamma, sk_matrix44_t toXYZD50);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_gamma_and_gamut(sk_colorspace_render_target_gamma_t gamma, sk_colorspace_gamut_t gamut)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_gamma_and_gamut (SKColorSpaceRenderTargetGamma gamma, SKColorSpaceGamut gamut);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_gamma_named(sk_gamma_named_t gamma, const sk_matrix44_t* toXYZD50)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_gamma_named (SKNamedGamma gamma, sk_matrix44_t toXYZD50);
+
+               // sk_colorspace_t* sk_colorspace_new_rgb_with_gamma_named_and_gamut(sk_gamma_named_t gamma, sk_colorspace_gamut_t gamut)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_rgb_with_gamma_named_and_gamut (SKNamedGamma gamma, SKColorSpaceGamut gamut);
+
+               // sk_colorspace_t* sk_colorspace_new_srgb()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_srgb ();
+
+               // sk_colorspace_t* sk_colorspace_new_srgb_linear()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_colorspace_new_srgb_linear ();
+
+               // bool sk_colorspace_to_xyzd50(const sk_colorspace_t* cColorSpace, sk_matrix44_t* toXYZD50)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspace_to_xyzd50 (sk_colorspace_t cColorSpace, sk_matrix44_t toXYZD50);
+
+               // void sk_colorspace_transfer_fn_invert(const sk_colorspace_transfer_fn_t* transfer, sk_colorspace_transfer_fn_t* inverted)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_colorspace_transfer_fn_invert (SKColorSpaceTransferFn* transfer, SKColorSpaceTransferFn* inverted);
+
+               // float sk_colorspace_transfer_fn_transform(const sk_colorspace_transfer_fn_t* transfer, float x)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_colorspace_transfer_fn_transform (SKColorSpaceTransferFn* transfer, Single x);
+
+               // void sk_colorspace_unref(sk_colorspace_t* cColorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_colorspace_unref (sk_colorspace_t cColorSpace);
+
+               // bool sk_colorspaceprimaries_to_xyzd50(const sk_colorspaceprimaries_t* primaries, sk_matrix44_t* toXYZD50)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_colorspaceprimaries_to_xyzd50 (SKColorSpacePrimaries* primaries, sk_matrix44_t toXYZD50);
+
+               #endregion
+
+               #region sk_colortable.h
+
+               // int sk_colortable_count(const sk_colortable_t* ctable)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_colortable_count (sk_colortable_t ctable);
+
+               // sk_colortable_t* sk_colortable_new(const sk_pmcolor_t* colors, int count)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colortable_t sk_colortable_new (UInt32* colors, Int32 count);
+
+               // void sk_colortable_read_colors(const sk_colortable_t* ctable, sk_pmcolor_t** colors)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_colortable_read_colors (sk_colortable_t ctable, UInt32** colors);
+
+               // void sk_colortable_unref(sk_colortable_t* ctable)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_colortable_unref (sk_colortable_t ctable);
+
+               #endregion
+
+               #region sk_data.h
+
+               // const uint8_t* sk_data_get_bytes(const sk_data_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Byte* sk_data_get_bytes (sk_data_t param0);
+
+               // const void* sk_data_get_data(const sk_data_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_data_get_data (sk_data_t param0);
+
+               // size_t sk_data_get_size(const sk_data_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_data_get_size (sk_data_t param0);
+
+               // sk_data_t* sk_data_new_empty()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_empty ();
+
+               // sk_data_t* sk_data_new_from_file(const char* path)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_from_file (/* char */ void* path);
+
+               // sk_data_t* sk_data_new_from_stream(sk_stream_t* stream, size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_from_stream (sk_stream_t stream, /* size_t */ IntPtr length);
+
+               // sk_data_t* sk_data_new_subset(const sk_data_t* src, size_t offset, size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_subset (sk_data_t src, /* size_t */ IntPtr offset, /* size_t */ IntPtr length);
+
+               // sk_data_t* sk_data_new_uninitialized(size_t size)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_uninitialized (/* size_t */ IntPtr size);
+
+               // sk_data_t* sk_data_new_with_copy(const void* src, size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_with_copy (void* src, /* size_t */ IntPtr length);
+
+               // sk_data_t* sk_data_new_with_proc(const void* ptr, size_t length, sk_data_release_proc proc, void* ctx)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_data_new_with_proc (void* ptr, /* size_t */ IntPtr length, SKDataReleaseProxyDelegate proc, void* ctx);
+
+               // void sk_data_ref(const sk_data_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_data_ref (sk_data_t param0);
+
+               // void sk_data_unref(const sk_data_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_data_unref (sk_data_t param0);
+
+               #endregion
+
+               #region sk_document.h
+
+               // void sk_document_abort(sk_document_t* document)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_document_abort (sk_document_t document);
+
+               // sk_canvas_t* sk_document_begin_page(sk_document_t* document, float width, float height, const sk_rect_t* content)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_document_begin_page (sk_document_t document, Single width, Single height, SKRect* content);
+
+               // void sk_document_close(sk_document_t* document)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_document_close (sk_document_t document);
+
+               // sk_document_t* sk_document_create_pdf_from_stream(sk_wstream_t* stream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_document_t sk_document_create_pdf_from_stream (sk_wstream_t stream);
+
+               // sk_document_t* sk_document_create_pdf_from_stream_with_metadata(sk_wstream_t* stream, const sk_document_pdf_metadata_t* metadata)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_document_t sk_document_create_pdf_from_stream_with_metadata (sk_wstream_t stream, SKDocumentPdfMetadataInternal* metadata);
+
+               // sk_document_t* sk_document_create_xps_from_stream(sk_wstream_t* stream, float dpi)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_document_t sk_document_create_xps_from_stream (sk_wstream_t stream, Single dpi);
+
+               // void sk_document_end_page(sk_document_t* document)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_document_end_page (sk_document_t document);
+
+               // void sk_document_unref(sk_document_t* document)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_document_unref (sk_document_t document);
+
+               #endregion
+
+               #region sk_drawable.h
+
+               // void sk_drawable_draw(sk_drawable_t*, sk_canvas_t*, const sk_matrix_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_drawable_draw (sk_drawable_t param0, sk_canvas_t param1, SKMatrix* param2);
+
+               // void sk_drawable_get_bounds(sk_drawable_t*, sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_drawable_get_bounds (sk_drawable_t param0, SKRect* param1);
+
+               // uint32_t sk_drawable_get_generation_id(sk_drawable_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_drawable_get_generation_id (sk_drawable_t param0);
+
+               // sk_picture_t* sk_drawable_new_picture_snapshot(sk_drawable_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_picture_t sk_drawable_new_picture_snapshot (sk_drawable_t param0);
+
+               // void sk_drawable_notify_drawing_changed(sk_drawable_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_drawable_notify_drawing_changed (sk_drawable_t param0);
+
+               // void sk_drawable_unref(sk_drawable_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_drawable_unref (sk_drawable_t param0);
+
+               #endregion
+
+               #region sk_general.h
+
+               // sk_colortype_t sk_colortype_get_default_8888()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKColorType sk_colortype_get_default_8888 ();
+
+               // int sk_nvrefcnt_get_ref_count(const sk_nvrefcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_nvrefcnt_get_ref_count (sk_nvrefcnt_t refcnt);
+
+               // void sk_nvrefcnt_safe_ref(sk_nvrefcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nvrefcnt_safe_ref (sk_nvrefcnt_t refcnt);
+
+               // void sk_nvrefcnt_safe_unref(sk_nvrefcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_nvrefcnt_safe_unref (sk_nvrefcnt_t refcnt);
+
+               // bool sk_nvrefcnt_unique(const sk_nvrefcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_nvrefcnt_unique (sk_nvrefcnt_t refcnt);
+
+               // int sk_refcnt_get_ref_count(const sk_refcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_refcnt_get_ref_count (sk_refcnt_t refcnt);
+
+               // void sk_refcnt_safe_ref(sk_refcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_refcnt_safe_ref (sk_refcnt_t refcnt);
+
+               // void sk_refcnt_safe_unref(sk_refcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_refcnt_safe_unref (sk_refcnt_t refcnt);
+
+               // bool sk_refcnt_unique(const sk_refcnt_t* refcnt)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_refcnt_unique (sk_refcnt_t refcnt);
+
+               #endregion
+
+               #region sk_image.h
+
+               // sk_data_t* sk_image_encode(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_image_encode (sk_image_t param0);
+
+               // sk_data_t* sk_image_encode_specific(const sk_image_t* cimage, sk_encoded_image_format_t encoder, int quality)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_image_encode_specific (sk_image_t cimage, SKEncodedImageFormat encoder, Int32 quality);
+
+               // sk_alphatype_t sk_image_get_alpha_type(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKAlphaType sk_image_get_alpha_type (sk_image_t param0);
+
+               // sk_colortype_t sk_image_get_color_type(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKColorType sk_image_get_color_type (sk_image_t param0);
+
+               // sk_colorspace_t* sk_image_get_colorspace(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorspace_t sk_image_get_colorspace (sk_image_t param0);
+
+               // int sk_image_get_height(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_image_get_height (sk_image_t param0);
+
+               // uint32_t sk_image_get_unique_id(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_image_get_unique_id (sk_image_t param0);
+
+               // int sk_image_get_width(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_image_get_width (sk_image_t param0);
+
+               // bool sk_image_is_alpha_only(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_is_alpha_only (sk_image_t param0);
+
+               // bool sk_image_is_lazy_generated(const sk_image_t* image)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_is_lazy_generated (sk_image_t image);
+
+               // bool sk_image_is_texture_backed(const sk_image_t* image)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_is_texture_backed (sk_image_t image);
+
+               // bool sk_image_is_valid(const sk_image_t* image, gr_context_t* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_is_valid (sk_image_t image, gr_context_t context);
+
+               // sk_image_t* sk_image_make_non_texture_image(const sk_image_t* cimage)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_make_non_texture_image (sk_image_t cimage);
+
+               // sk_image_t* sk_image_make_raster_image(const sk_image_t* cimage)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_make_raster_image (sk_image_t cimage);
+
+               // sk_shader_t* sk_image_make_shader(const sk_image_t*, sk_shader_tilemode_t tileX, sk_shader_tilemode_t tileY, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_image_make_shader (sk_image_t param0, SKShaderTileMode tileX, SKShaderTileMode tileY, SKMatrix* localMatrix);
+
+               // sk_image_t* sk_image_make_subset(const sk_image_t* cimage, const sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_make_subset (sk_image_t cimage, SKRectI* subset);
+
+               // sk_image_t* sk_image_make_texture_image(const sk_image_t* cimage, gr_context_t* context, sk_colorspace_t* colorspace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_make_texture_image (sk_image_t cimage, gr_context_t context, sk_colorspace_t colorspace);
+
+               // sk_image_t* sk_image_make_with_filter(const sk_image_t* cimage, const sk_imagefilter_t* filter, const sk_irect_t* subset, const sk_irect_t* clipBounds, sk_irect_t* outSubset, sk_ipoint_t* outOffset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_make_with_filter (sk_image_t cimage, sk_imagefilter_t filter, SKRectI* subset, SKRectI* clipBounds, SKRectI* outSubset, SKPointI* outOffset);
+
+               // sk_image_t* sk_image_new_from_adopted_texture(gr_context_t* context, const gr_backendtexture_t* texture, gr_surfaceorigin_t origin, sk_colortype_t colorType, sk_alphatype_t alpha, sk_colorspace_t* colorSpace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_from_adopted_texture (gr_context_t context, gr_backendtexture_t texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, sk_colorspace_t colorSpace);
+
+               // sk_image_t* sk_image_new_from_bitmap(const sk_bitmap_t* cbitmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_from_bitmap (sk_bitmap_t cbitmap);
+
+               // sk_image_t* sk_image_new_from_encoded(sk_data_t* encoded, const sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_from_encoded (sk_data_t encoded, SKRectI* subset);
+
+               // sk_image_t* sk_image_new_from_picture(sk_picture_t* picture, const sk_isize_t* dimensions, const sk_matrix_t* matrix, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_from_picture (sk_picture_t picture, SKSizeI* dimensions, SKMatrix* matrix, sk_paint_t paint);
+
+               // sk_image_t* sk_image_new_from_texture(gr_context_t* context, const gr_backendtexture_t* texture, gr_surfaceorigin_t origin, sk_colortype_t colorType, sk_alphatype_t alpha, sk_colorspace_t* colorSpace, sk_image_texture_release_proc releaseProc, void* releaseContext)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_from_texture (gr_context_t context, gr_backendtexture_t texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, sk_colorspace_t colorSpace, SKImageTextureReleaseProxyDelegate releaseProc, void* releaseContext);
+
+               // sk_image_t* sk_image_new_raster(const sk_pixmap_t* pixmap, sk_image_raster_release_proc releaseProc, void* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_raster (sk_pixmap_t pixmap, SKImageRasterReleaseProxyDelegate releaseProc, void* context);
+
+               // sk_image_t* sk_image_new_raster_copy(const sk_imageinfo_t*, const void* pixels, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_raster_copy (SKImageInfoNative* param0, void* pixels, /* size_t */ IntPtr rowBytes);
+
+               // sk_image_t* sk_image_new_raster_copy_with_pixmap(const sk_pixmap_t* pixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_raster_copy_with_pixmap (sk_pixmap_t pixmap);
+
+               // sk_image_t* sk_image_new_raster_data(const sk_imageinfo_t* cinfo, sk_data_t* pixels, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_image_new_raster_data (SKImageInfoNative* cinfo, sk_data_t pixels, /* size_t */ IntPtr rowBytes);
+
+               // bool sk_image_peek_pixels(const sk_image_t* image, sk_pixmap_t* pixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_peek_pixels (sk_image_t image, sk_pixmap_t pixmap);
+
+               // bool sk_image_read_pixels(const sk_image_t* image, const sk_imageinfo_t* dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, int srcY, sk_image_caching_hint_t cachingHint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_read_pixels (sk_image_t image, SKImageInfoNative* dstInfo, void* dstPixels, /* size_t */ IntPtr dstRowBytes, Int32 srcX, Int32 srcY, SKImageCachingHint cachingHint);
+
+               // bool sk_image_read_pixels_into_pixmap(const sk_image_t* image, const sk_pixmap_t* dst, int srcX, int srcY, sk_image_caching_hint_t cachingHint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_read_pixels_into_pixmap (sk_image_t image, sk_pixmap_t dst, Int32 srcX, Int32 srcY, SKImageCachingHint cachingHint);
+
+               // void sk_image_ref(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_image_ref (sk_image_t param0);
+
+               // sk_data_t* sk_image_ref_encoded(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_image_ref_encoded (sk_image_t param0);
+
+               // bool sk_image_scale_pixels(const sk_image_t* image, const sk_pixmap_t* dst, sk_filter_quality_t quality, sk_image_caching_hint_t cachingHint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_image_scale_pixels (sk_image_t image, sk_pixmap_t dst, SKFilterQuality quality, SKImageCachingHint cachingHint);
+
+               // void sk_image_unref(const sk_image_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_image_unref (sk_image_t param0);
+
+               #endregion
+
+               #region sk_imagefilter.h
+
+               // void sk_imagefilter_croprect_destructor(sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_imagefilter_croprect_destructor (sk_imagefilter_croprect_t cropRect);
+
+               // uint32_t sk_imagefilter_croprect_get_flags(sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_imagefilter_croprect_get_flags (sk_imagefilter_croprect_t cropRect);
+
+               // void sk_imagefilter_croprect_get_rect(sk_imagefilter_croprect_t* cropRect, sk_rect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_imagefilter_croprect_get_rect (sk_imagefilter_croprect_t cropRect, SKRect* rect);
+
+               // sk_imagefilter_croprect_t* sk_imagefilter_croprect_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_croprect_t sk_imagefilter_croprect_new ();
+
+               // sk_imagefilter_croprect_t* sk_imagefilter_croprect_new_with_rect(const sk_rect_t* rect, uint32_t flags)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_croprect_t sk_imagefilter_croprect_new_with_rect (SKRect* rect, UInt32 flags);
+
+               // sk_imagefilter_t* sk_imagefilter_new_alpha_threshold(const sk_region_t* region, float innerThreshold, float outerThreshold, sk_imagefilter_t* input)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_alpha_threshold (sk_region_t region, Single innerThreshold, Single outerThreshold, sk_imagefilter_t input);
+
+               // sk_imagefilter_t* sk_imagefilter_new_arithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, sk_imagefilter_t* background, sk_imagefilter_t* foreground, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_arithmetic (Single k1, Single k2, Single k3, Single k4, [MarshalAs (UnmanagedType.I1)] bool enforcePMColor, sk_imagefilter_t background, sk_imagefilter_t foreground, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_blur(float sigmaX, float sigmaY, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_blur (Single sigmaX, Single sigmaY, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_color_filter(sk_colorfilter_t* cf, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_color_filter (sk_colorfilter_t cf, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_compose(sk_imagefilter_t* outer, sk_imagefilter_t* inner)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_compose (sk_imagefilter_t outer, sk_imagefilter_t inner);
+
+               // sk_imagefilter_t* sk_imagefilter_new_dilate(int radiusX, int radiusY, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_dilate (Int32 radiusX, Int32 radiusY, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_displacement_map_effect(sk_displacement_map_effect_channel_selector_type_t xChannelSelector, sk_displacement_map_effect_channel_selector_type_t yChannelSelector, float scale, sk_imagefilter_t* displacement, sk_imagefilter_t* color, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_displacement_map_effect (SKDisplacementMapEffectChannelSelectorType xChannelSelector, SKDisplacementMapEffectChannelSelectorType yChannelSelector, Single scale, sk_imagefilter_t displacement, sk_imagefilter_t color, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_distant_lit_diffuse(const sk_point3_t* direction, sk_color_t lightColor, float surfaceScale, float kd, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_distant_lit_diffuse (SKPoint3* direction, UInt32 lightColor, Single surfaceScale, Single kd, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_distant_lit_specular(const sk_point3_t* direction, sk_color_t lightColor, float surfaceScale, float ks, float shininess, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_distant_lit_specular (SKPoint3* direction, UInt32 lightColor, Single surfaceScale, Single ks, Single shininess, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_drop_shadow(float dx, float dy, float sigmaX, float sigmaY, sk_color_t color, sk_drop_shadow_image_filter_shadow_mode_t shadowMode, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_drop_shadow (Single dx, Single dy, Single sigmaX, Single sigmaY, UInt32 color, SKDropShadowImageFilterShadowMode shadowMode, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_erode(int radiusX, int radiusY, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_erode (Int32 radiusX, Int32 radiusY, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_image_source(sk_image_t* image, const sk_rect_t* srcRect, const sk_rect_t* dstRect, sk_filter_quality_t filterQuality)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_image_source (sk_image_t image, SKRect* srcRect, SKRect* dstRect, SKFilterQuality filterQuality);
+
+               // sk_imagefilter_t* sk_imagefilter_new_image_source_default(sk_image_t* image)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_image_source_default (sk_image_t image);
+
+               // sk_imagefilter_t* sk_imagefilter_new_magnifier(const sk_rect_t* src, float inset, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_magnifier (SKRect* src, Single inset, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_matrix(const sk_matrix_t* matrix, sk_filter_quality_t quality, sk_imagefilter_t* input)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_matrix (SKMatrix* matrix, SKFilterQuality quality, sk_imagefilter_t input);
+
+               // sk_imagefilter_t* sk_imagefilter_new_matrix_convolution(const sk_isize_t* kernelSize, const float[-1] kernel, float gain, float bias, const sk_ipoint_t* kernelOffset, sk_matrix_convolution_tilemode_t tileMode, bool convolveAlpha, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_matrix_convolution (SKSizeI* kernelSize, Single* kernel, Single gain, Single bias, SKPointI* kernelOffset, SKMatrixConvolutionTileMode tileMode, [MarshalAs (UnmanagedType.I1)] bool convolveAlpha, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_merge(sk_imagefilter_t*[-1] filters, int count, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_merge (sk_imagefilter_t* filters, Int32 count, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_offset(float dx, float dy, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_offset (Single dx, Single dy, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_paint(const sk_paint_t* paint, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_paint (sk_paint_t paint, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_picture(sk_picture_t* picture)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_picture (sk_picture_t picture);
+
+               // sk_imagefilter_t* sk_imagefilter_new_picture_with_croprect(sk_picture_t* picture, const sk_rect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_picture_with_croprect (sk_picture_t picture, SKRect* cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_point_lit_diffuse(const sk_point3_t* location, sk_color_t lightColor, float surfaceScale, float kd, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_point_lit_diffuse (SKPoint3* location, UInt32 lightColor, Single surfaceScale, Single kd, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_point_lit_specular(const sk_point3_t* location, sk_color_t lightColor, float surfaceScale, float ks, float shininess, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_point_lit_specular (SKPoint3* location, UInt32 lightColor, Single surfaceScale, Single ks, Single shininess, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_spot_lit_diffuse(const sk_point3_t* location, const sk_point3_t* target, float specularExponent, float cutoffAngle, sk_color_t lightColor, float surfaceScale, float kd, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_spot_lit_diffuse (SKPoint3* location, SKPoint3* target, Single specularExponent, Single cutoffAngle, UInt32 lightColor, Single surfaceScale, Single kd, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_spot_lit_specular(const sk_point3_t* location, const sk_point3_t* target, float specularExponent, float cutoffAngle, sk_color_t lightColor, float surfaceScale, float ks, float shininess, sk_imagefilter_t* input, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_spot_lit_specular (SKPoint3* location, SKPoint3* target, Single specularExponent, Single cutoffAngle, UInt32 lightColor, Single surfaceScale, Single ks, Single shininess, sk_imagefilter_t input, sk_imagefilter_croprect_t cropRect);
+
+               // sk_imagefilter_t* sk_imagefilter_new_tile(const sk_rect_t* src, const sk_rect_t* dst, sk_imagefilter_t* input)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_tile (SKRect* src, SKRect* dst, sk_imagefilter_t input);
+
+               // sk_imagefilter_t* sk_imagefilter_new_xfermode(sk_blendmode_t mode, sk_imagefilter_t* background, sk_imagefilter_t* foreground, const sk_imagefilter_croprect_t* cropRect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_imagefilter_new_xfermode (SKBlendMode mode, sk_imagefilter_t background, sk_imagefilter_t foreground, sk_imagefilter_croprect_t cropRect);
+
+               // void sk_imagefilter_unref(sk_imagefilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_imagefilter_unref (sk_imagefilter_t param0);
+
+               #endregion
+
+               #region sk_mask.h
+
+               // uint8_t* sk_mask_alloc_image(size_t bytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Byte* sk_mask_alloc_image (/* size_t */ IntPtr bytes);
+
+               // size_t sk_mask_compute_image_size(sk_mask_t* cmask)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_mask_compute_image_size (SKMask* cmask);
+
+               // size_t sk_mask_compute_total_image_size(sk_mask_t* cmask)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_mask_compute_total_image_size (SKMask* cmask);
+
+               // void sk_mask_free_image(void* image)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_mask_free_image (void* image);
+
+               // void* sk_mask_get_addr(sk_mask_t* cmask, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_mask_get_addr (SKMask* cmask, Int32 x, Int32 y);
+
+               // uint8_t sk_mask_get_addr_1(sk_mask_t* cmask, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Byte sk_mask_get_addr_1 (SKMask* cmask, Int32 x, Int32 y);
+
+               // uint32_t sk_mask_get_addr_32(sk_mask_t* cmask, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_mask_get_addr_32 (SKMask* cmask, Int32 x, Int32 y);
+
+               // uint8_t sk_mask_get_addr_8(sk_mask_t* cmask, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Byte sk_mask_get_addr_8 (SKMask* cmask, Int32 x, Int32 y);
+
+               // uint16_t sk_mask_get_addr_lcd_16(sk_mask_t* cmask, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt16 sk_mask_get_addr_lcd_16 (SKMask* cmask, Int32 x, Int32 y);
+
+               // bool sk_mask_is_empty(sk_mask_t* cmask)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_mask_is_empty (SKMask* cmask);
+
+               #endregion
+
+               #region sk_maskfilter.h
+
+               // sk_maskfilter_t* sk_maskfilter_new_blur(sk_blurstyle_t, float sigma)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_maskfilter_new_blur (SKBlurStyle param0, Single sigma);
+
+               // sk_maskfilter_t* sk_maskfilter_new_blur_with_flags(sk_blurstyle_t, float sigma, const sk_rect_t* occluder, bool respectCTM)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_maskfilter_new_blur_with_flags (SKBlurStyle param0, Single sigma, SKRect* occluder, [MarshalAs (UnmanagedType.I1)] bool respectCTM);
+
+               // sk_maskfilter_t* sk_maskfilter_new_clip(uint8_t min, uint8_t max)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_maskfilter_new_clip (Byte min, Byte max);
+
+               // sk_maskfilter_t* sk_maskfilter_new_gamma(float gamma)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_maskfilter_new_gamma (Single gamma);
+
+               // sk_maskfilter_t* sk_maskfilter_new_table(const uint8_t[256] table = 256)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_maskfilter_new_table (Byte* table);
+
+               // void sk_maskfilter_ref(sk_maskfilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_maskfilter_ref (sk_maskfilter_t param0);
+
+               // void sk_maskfilter_unref(sk_maskfilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_maskfilter_unref (sk_maskfilter_t param0);
+
+               #endregion
+
+               #region sk_matrix.h
+
+               // void sk_3dview_apply_to_canvas(sk_3dview_t* cview, sk_canvas_t* ccanvas)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_apply_to_canvas (sk_3dview_t cview, sk_canvas_t ccanvas);
+
+               // void sk_3dview_destroy(sk_3dview_t* cview)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_destroy (sk_3dview_t cview);
+
+               // float sk_3dview_dot_with_normal(sk_3dview_t* cview, float dx, float dy, float dz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_3dview_dot_with_normal (sk_3dview_t cview, Single dx, Single dy, Single dz);
+
+               // void sk_3dview_get_matrix(sk_3dview_t* cview, sk_matrix_t* cmatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_get_matrix (sk_3dview_t cview, SKMatrix* cmatrix);
+
+               // sk_3dview_t* sk_3dview_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_3dview_t sk_3dview_new ();
+
+               // void sk_3dview_restore(sk_3dview_t* cview)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_restore (sk_3dview_t cview);
+
+               // void sk_3dview_rotate_x_degrees(sk_3dview_t* cview, float degrees)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_x_degrees (sk_3dview_t cview, Single degrees);
+
+               // void sk_3dview_rotate_x_radians(sk_3dview_t* cview, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_x_radians (sk_3dview_t cview, Single radians);
+
+               // void sk_3dview_rotate_y_degrees(sk_3dview_t* cview, float degrees)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_y_degrees (sk_3dview_t cview, Single degrees);
+
+               // void sk_3dview_rotate_y_radians(sk_3dview_t* cview, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_y_radians (sk_3dview_t cview, Single radians);
+
+               // void sk_3dview_rotate_z_degrees(sk_3dview_t* cview, float degrees)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_z_degrees (sk_3dview_t cview, Single degrees);
+
+               // void sk_3dview_rotate_z_radians(sk_3dview_t* cview, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_rotate_z_radians (sk_3dview_t cview, Single radians);
+
+               // void sk_3dview_save(sk_3dview_t* cview)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_save (sk_3dview_t cview);
+
+               // void sk_3dview_translate(sk_3dview_t* cview, float x, float y, float z)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_3dview_translate (sk_3dview_t cview, Single x, Single y, Single z);
+
+               // void sk_matrix_concat(sk_matrix_t* result, sk_matrix_t* first, sk_matrix_t* second)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_concat (SKMatrix* result, SKMatrix* first, SKMatrix* second);
+
+               // void sk_matrix_map_points(sk_matrix_t* matrix, sk_point_t* dst, sk_point_t* src, int count)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_map_points (SKMatrix* matrix, SKPoint* dst, SKPoint* src, Int32 count);
+
+               // float sk_matrix_map_radius(sk_matrix_t* matrix, float radius)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_matrix_map_radius (SKMatrix* matrix, Single radius);
+
+               // void sk_matrix_map_rect(sk_matrix_t* matrix, sk_rect_t* dest, sk_rect_t* source)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_map_rect (SKMatrix* matrix, SKRect* dest, SKRect* source);
+
+               // void sk_matrix_map_vector(sk_matrix_t* matrix, float x, float y, sk_point_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_map_vector (SKMatrix* matrix, Single x, Single y, SKPoint* result);
+
+               // void sk_matrix_map_vectors(sk_matrix_t* matrix, sk_point_t* dst, sk_point_t* src, int count)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_map_vectors (SKMatrix* matrix, SKPoint* dst, SKPoint* src, Int32 count);
+
+               // void sk_matrix_map_xy(sk_matrix_t* matrix, float x, float y, sk_point_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_map_xy (SKMatrix* matrix, Single x, Single y, SKPoint* result);
+
+               // void sk_matrix_post_concat(sk_matrix_t* result, sk_matrix_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_post_concat (SKMatrix* result, SKMatrix* matrix);
+
+               // void sk_matrix_pre_concat(sk_matrix_t* result, sk_matrix_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix_pre_concat (SKMatrix* result, SKMatrix* matrix);
+
+               // bool sk_matrix_try_invert(sk_matrix_t* matrix, sk_matrix_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_matrix_try_invert (SKMatrix* matrix, SKMatrix* result);
+
+               // void sk_matrix44_as_col_major(sk_matrix44_t* matrix, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_as_col_major (sk_matrix44_t matrix, Single* dst);
+
+               // void sk_matrix44_as_row_major(sk_matrix44_t* matrix, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_as_row_major (sk_matrix44_t matrix, Single* dst);
+
+               // void sk_matrix44_destroy(sk_matrix44_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_destroy (sk_matrix44_t matrix);
+
+               // double sk_matrix44_determinant(sk_matrix44_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Double sk_matrix44_determinant (sk_matrix44_t matrix);
+
+               // bool sk_matrix44_equals(sk_matrix44_t* matrix, const sk_matrix44_t* other)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_matrix44_equals (sk_matrix44_t matrix, sk_matrix44_t other);
+
+               // float sk_matrix44_get(sk_matrix44_t* matrix, int row, int col)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_matrix44_get (sk_matrix44_t matrix, Int32 row, Int32 col);
+
+               // sk_matrix44_type_mask_t sk_matrix44_get_type(sk_matrix44_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKMatrix44TypeMask sk_matrix44_get_type (sk_matrix44_t matrix);
+
+               // bool sk_matrix44_invert(sk_matrix44_t* matrix, sk_matrix44_t* inverse)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_matrix44_invert (sk_matrix44_t matrix, sk_matrix44_t inverse);
+
+               // void sk_matrix44_map_scalars(sk_matrix44_t* matrix, const float* src, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_map_scalars (sk_matrix44_t matrix, Single* src, Single* dst);
+
+               // void sk_matrix44_map2(sk_matrix44_t* matrix, const float* src2, int count, float* dst4)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_map2 (sk_matrix44_t matrix, Single* src2, Int32 count, Single* dst4);
+
+               // sk_matrix44_t* sk_matrix44_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_matrix44_new ();
+
+               // sk_matrix44_t* sk_matrix44_new_concat(const sk_matrix44_t* a, const sk_matrix44_t* b)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_matrix44_new_concat (sk_matrix44_t a, sk_matrix44_t b);
+
+               // sk_matrix44_t* sk_matrix44_new_copy(const sk_matrix44_t* src)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_matrix44_new_copy (sk_matrix44_t src);
+
+               // sk_matrix44_t* sk_matrix44_new_identity()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_matrix44_new_identity ();
+
+               // sk_matrix44_t* sk_matrix44_new_matrix(const sk_matrix_t* src)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_matrix44_t sk_matrix44_new_matrix (SKMatrix* src);
+
+               // void sk_matrix44_post_concat(sk_matrix44_t* matrix, const sk_matrix44_t* m)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_post_concat (sk_matrix44_t matrix, sk_matrix44_t m);
+
+               // void sk_matrix44_post_scale(sk_matrix44_t* matrix, float sx, float sy, float sz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_post_scale (sk_matrix44_t matrix, Single sx, Single sy, Single sz);
+
+               // void sk_matrix44_post_translate(sk_matrix44_t* matrix, float dx, float dy, float dz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_post_translate (sk_matrix44_t matrix, Single dx, Single dy, Single dz);
+
+               // void sk_matrix44_pre_concat(sk_matrix44_t* matrix, const sk_matrix44_t* m)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_pre_concat (sk_matrix44_t matrix, sk_matrix44_t m);
+
+               // void sk_matrix44_pre_scale(sk_matrix44_t* matrix, float sx, float sy, float sz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_pre_scale (sk_matrix44_t matrix, Single sx, Single sy, Single sz);
+
+               // void sk_matrix44_pre_translate(sk_matrix44_t* matrix, float dx, float dy, float dz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_pre_translate (sk_matrix44_t matrix, Single dx, Single dy, Single dz);
+
+               // bool sk_matrix44_preserves_2d_axis_alignment(sk_matrix44_t* matrix, float epsilon)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_matrix44_preserves_2d_axis_alignment (sk_matrix44_t matrix, Single epsilon);
+
+               // void sk_matrix44_set(sk_matrix44_t* matrix, int row, int col, float value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set (sk_matrix44_t matrix, Int32 row, Int32 col, Single value);
+
+               // void sk_matrix44_set_3x3_row_major(sk_matrix44_t* matrix, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_3x3_row_major (sk_matrix44_t matrix, Single* dst);
+
+               // void sk_matrix44_set_col_major(sk_matrix44_t* matrix, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_col_major (sk_matrix44_t matrix, Single* dst);
+
+               // void sk_matrix44_set_concat(sk_matrix44_t* matrix, const sk_matrix44_t* a, const sk_matrix44_t* b)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_concat (sk_matrix44_t matrix, sk_matrix44_t a, sk_matrix44_t b);
+
+               // void sk_matrix44_set_identity(sk_matrix44_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_identity (sk_matrix44_t matrix);
+
+               // void sk_matrix44_set_rotate_about_degrees(sk_matrix44_t* matrix, float x, float y, float z, float degrees)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_rotate_about_degrees (sk_matrix44_t matrix, Single x, Single y, Single z, Single degrees);
+
+               // void sk_matrix44_set_rotate_about_radians(sk_matrix44_t* matrix, float x, float y, float z, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_rotate_about_radians (sk_matrix44_t matrix, Single x, Single y, Single z, Single radians);
+
+               // void sk_matrix44_set_rotate_about_radians_unit(sk_matrix44_t* matrix, float x, float y, float z, float radians)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_rotate_about_radians_unit (sk_matrix44_t matrix, Single x, Single y, Single z, Single radians);
+
+               // void sk_matrix44_set_row_major(sk_matrix44_t* matrix, float* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_row_major (sk_matrix44_t matrix, Single* dst);
+
+               // void sk_matrix44_set_scale(sk_matrix44_t* matrix, float sx, float sy, float sz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_scale (sk_matrix44_t matrix, Single sx, Single sy, Single sz);
+
+               // void sk_matrix44_set_translate(sk_matrix44_t* matrix, float dx, float dy, float dz)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_set_translate (sk_matrix44_t matrix, Single dx, Single dy, Single dz);
+
+               // void sk_matrix44_to_matrix(sk_matrix44_t* matrix, sk_matrix_t* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_to_matrix (sk_matrix44_t matrix, SKMatrix* dst);
+
+               // void sk_matrix44_transpose(sk_matrix44_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_matrix44_transpose (sk_matrix44_t matrix);
+
+               #endregion
+
+               #region sk_paint.h
+
+               // size_t sk_paint_break_text(const sk_paint_t* cpaint, const void* text, size_t length, float maxWidth, float* measuredWidth)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_paint_break_text (sk_paint_t cpaint, void* text, /* size_t */ IntPtr length, Single maxWidth, Single* measuredWidth);
+
+               // sk_paint_t* sk_paint_clone(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_paint_t sk_paint_clone (sk_paint_t param0);
+
+               // bool sk_paint_contains_text(const sk_paint_t* cpaint, const void* text, size_t byteLength)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_contains_text (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength);
+
+               // int sk_paint_count_text(const sk_paint_t* cpaint, const void* text, size_t byteLength)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_count_text (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength);
+
+               // void sk_paint_delete(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_delete (sk_paint_t param0);
+
+               // sk_blendmode_t sk_paint_get_blendmode(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKBlendMode sk_paint_get_blendmode (sk_paint_t param0);
+
+               // sk_color_t sk_paint_get_color(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_paint_get_color (sk_paint_t param0);
+
+               // sk_colorfilter_t* sk_paint_get_colorfilter(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_colorfilter_t sk_paint_get_colorfilter (sk_paint_t param0);
+
+               // bool sk_paint_get_fill_path(const sk_paint_t*, const sk_path_t* src, sk_path_t* dst, const sk_rect_t* cullRect, float resScale)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_get_fill_path (sk_paint_t param0, sk_path_t src, sk_path_t dst, SKRect* cullRect, Single resScale);
+
+               // sk_filter_quality_t sk_paint_get_filter_quality(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKFilterQuality sk_paint_get_filter_quality (sk_paint_t param0);
+
+               // float sk_paint_get_fontmetrics(sk_paint_t* cpaint, sk_fontmetrics_t* cfontmetrics, float scale)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_fontmetrics (sk_paint_t cpaint, SKFontMetrics* cfontmetrics, Single scale);
+
+               // sk_paint_hinting_t sk_paint_get_hinting(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPaintHinting sk_paint_get_hinting (sk_paint_t param0);
+
+               // sk_imagefilter_t* sk_paint_get_imagefilter(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_imagefilter_t sk_paint_get_imagefilter (sk_paint_t param0);
+
+               // sk_maskfilter_t* sk_paint_get_maskfilter(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_maskfilter_t sk_paint_get_maskfilter (sk_paint_t param0);
+
+               // sk_path_effect_t* sk_paint_get_path_effect(sk_paint_t* cpaint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_paint_get_path_effect (sk_paint_t cpaint);
+
+               // int sk_paint_get_pos_text_blob_intercepts(const sk_paint_t* cpaint, sk_textblob_t* blob, const float[2] bounds = 2, float* intervals)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_get_pos_text_blob_intercepts (sk_paint_t cpaint, sk_textblob_t blob, Single* bounds, Single* intervals);
+
+               // int sk_paint_get_pos_text_h_intercepts(const sk_paint_t* cpaint, const void* text, size_t byteLength, float* xpos, float y, const float[2] bounds = 2, float* intervals)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_get_pos_text_h_intercepts (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength, Single* xpos, Single y, Single* bounds, Single* intervals);
+
+               // int sk_paint_get_pos_text_intercepts(const sk_paint_t* cpaint, const void* text, size_t byteLength, sk_point_t* pos, const float[2] bounds = 2, float* intervals)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_get_pos_text_intercepts (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength, SKPoint* pos, Single* bounds, Single* intervals);
+
+               // sk_path_t* sk_paint_get_pos_text_path(sk_paint_t* cpaint, const void* text, size_t length, const sk_point_t[-1] pos)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_t sk_paint_get_pos_text_path (sk_paint_t cpaint, void* text, /* size_t */ IntPtr length, SKPoint* pos);
+
+               // sk_shader_t* sk_paint_get_shader(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_paint_get_shader (sk_paint_t param0);
+
+               // sk_stroke_cap_t sk_paint_get_stroke_cap(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKStrokeCap sk_paint_get_stroke_cap (sk_paint_t param0);
+
+               // sk_stroke_join_t sk_paint_get_stroke_join(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKStrokeJoin sk_paint_get_stroke_join (sk_paint_t param0);
+
+               // float sk_paint_get_stroke_miter(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_stroke_miter (sk_paint_t param0);
+
+               // float sk_paint_get_stroke_width(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_stroke_width (sk_paint_t param0);
+
+               // sk_paint_style_t sk_paint_get_style(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPaintStyle sk_paint_get_style (sk_paint_t param0);
+
+               // sk_text_align_t sk_paint_get_text_align(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKTextAlign sk_paint_get_text_align (sk_paint_t param0);
+
+               // sk_text_encoding_t sk_paint_get_text_encoding(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKTextEncoding sk_paint_get_text_encoding (sk_paint_t param0);
+
+               // int sk_paint_get_text_intercepts(const sk_paint_t* cpaint, const void* text, size_t byteLength, float x, float y, const float[2] bounds = 2, float* intervals)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_get_text_intercepts (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength, Single x, Single y, Single* bounds, Single* intervals);
+
+               // sk_path_t* sk_paint_get_text_path(sk_paint_t* cpaint, const void* text, size_t length, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_t sk_paint_get_text_path (sk_paint_t cpaint, void* text, /* size_t */ IntPtr length, Single x, Single y);
+
+               // float sk_paint_get_text_scale_x(const sk_paint_t* cpaint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_text_scale_x (sk_paint_t cpaint);
+
+               // float sk_paint_get_text_skew_x(const sk_paint_t* cpaint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_text_skew_x (sk_paint_t cpaint);
+
+               // int sk_paint_get_text_widths(const sk_paint_t* cpaint, const void* text, size_t byteLength, float* widths, sk_rect_t* bounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_get_text_widths (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength, Single* widths, SKRect* bounds);
+
+               // float sk_paint_get_textsize(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_get_textsize (sk_paint_t param0);
+
+               // sk_typeface_t* sk_paint_get_typeface(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_paint_get_typeface (sk_paint_t param0);
+
+               // bool sk_paint_is_antialias(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_antialias (sk_paint_t param0);
+
+               // bool sk_paint_is_autohinted(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_autohinted (sk_paint_t param0);
+
+               // bool sk_paint_is_dev_kern_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_dev_kern_text (sk_paint_t param0);
+
+               // bool sk_paint_is_dither(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_dither (sk_paint_t param0);
+
+               // bool sk_paint_is_embedded_bitmap_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_embedded_bitmap_text (sk_paint_t param0);
+
+               // bool sk_paint_is_fake_bold_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_fake_bold_text (sk_paint_t param0);
+
+               // bool sk_paint_is_lcd_render_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_lcd_render_text (sk_paint_t param0);
+
+               // bool sk_paint_is_linear_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_linear_text (sk_paint_t param0);
+
+               // bool sk_paint_is_subpixel_text(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_subpixel_text (sk_paint_t param0);
+
+               // bool sk_paint_is_verticaltext(const sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_paint_is_verticaltext (sk_paint_t param0);
+
+               // float sk_paint_measure_text(const sk_paint_t* cpaint, const void* text, size_t length, sk_rect_t* cbounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_paint_measure_text (sk_paint_t cpaint, void* text, /* size_t */ IntPtr length, SKRect* cbounds);
+
+               // sk_paint_t* sk_paint_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_paint_t sk_paint_new ();
+
+               // void sk_paint_reset(sk_paint_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_reset (sk_paint_t param0);
+
+               // void sk_paint_set_antialias(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_antialias (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_autohinted(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_autohinted (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_blendmode(sk_paint_t*, sk_blendmode_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_blendmode (sk_paint_t param0, SKBlendMode param1);
+
+               // void sk_paint_set_color(sk_paint_t*, sk_color_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_color (sk_paint_t param0, UInt32 param1);
+
+               // void sk_paint_set_colorfilter(sk_paint_t*, sk_colorfilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_colorfilter (sk_paint_t param0, sk_colorfilter_t param1);
+
+               // void sk_paint_set_dev_kern_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_dev_kern_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_dither(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_dither (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_embedded_bitmap_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_embedded_bitmap_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_fake_bold_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_fake_bold_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_filter_quality(sk_paint_t*, sk_filter_quality_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_filter_quality (sk_paint_t param0, SKFilterQuality param1);
+
+               // void sk_paint_set_hinting(sk_paint_t*, sk_paint_hinting_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_hinting (sk_paint_t param0, SKPaintHinting param1);
+
+               // void sk_paint_set_imagefilter(sk_paint_t*, sk_imagefilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_imagefilter (sk_paint_t param0, sk_imagefilter_t param1);
+
+               // void sk_paint_set_lcd_render_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_lcd_render_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_linear_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_linear_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_maskfilter(sk_paint_t*, sk_maskfilter_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_maskfilter (sk_paint_t param0, sk_maskfilter_t param1);
+
+               // void sk_paint_set_path_effect(sk_paint_t* cpaint, sk_path_effect_t* effect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_path_effect (sk_paint_t cpaint, sk_path_effect_t effect);
+
+               // void sk_paint_set_shader(sk_paint_t*, sk_shader_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_shader (sk_paint_t param0, sk_shader_t param1);
+
+               // void sk_paint_set_stroke_cap(sk_paint_t*, sk_stroke_cap_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_stroke_cap (sk_paint_t param0, SKStrokeCap param1);
+
+               // void sk_paint_set_stroke_join(sk_paint_t*, sk_stroke_join_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_stroke_join (sk_paint_t param0, SKStrokeJoin param1);
+
+               // void sk_paint_set_stroke_miter(sk_paint_t*, float miter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_stroke_miter (sk_paint_t param0, Single miter);
+
+               // void sk_paint_set_stroke_width(sk_paint_t*, float width)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_stroke_width (sk_paint_t param0, Single width);
+
+               // void sk_paint_set_style(sk_paint_t*, sk_paint_style_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_style (sk_paint_t param0, SKPaintStyle param1);
+
+               // void sk_paint_set_subpixel_text(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_subpixel_text (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // void sk_paint_set_text_align(sk_paint_t*, sk_text_align_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_text_align (sk_paint_t param0, SKTextAlign param1);
+
+               // void sk_paint_set_text_encoding(sk_paint_t*, sk_text_encoding_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_text_encoding (sk_paint_t param0, SKTextEncoding param1);
+
+               // void sk_paint_set_text_scale_x(sk_paint_t* cpaint, float scale)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_text_scale_x (sk_paint_t cpaint, Single scale);
+
+               // void sk_paint_set_text_skew_x(sk_paint_t* cpaint, float skew)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_text_skew_x (sk_paint_t cpaint, Single skew);
+
+               // void sk_paint_set_textsize(sk_paint_t*, float)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_textsize (sk_paint_t param0, Single param1);
+
+               // void sk_paint_set_typeface(sk_paint_t*, sk_typeface_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_typeface (sk_paint_t param0, sk_typeface_t param1);
+
+               // void sk_paint_set_verticaltext(sk_paint_t*, bool)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_paint_set_verticaltext (sk_paint_t param0, [MarshalAs (UnmanagedType.I1)] bool param1);
+
+               // int sk_paint_text_to_glyphs(const sk_paint_t* cpaint, const void* text, size_t byteLength, uint16_t* glyphs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_paint_text_to_glyphs (sk_paint_t cpaint, void* text, /* size_t */ IntPtr byteLength, UInt16* glyphs);
+
+               #endregion
+
+               #region sk_path.h
+
+               // void sk_opbuilder_add(sk_opbuilder_t* builder, const sk_path_t* path, sk_pathop_t op)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_opbuilder_add (sk_opbuilder_t builder, sk_path_t path, SKPathOp op);
+
+               // void sk_opbuilder_destroy(sk_opbuilder_t* builder)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_opbuilder_destroy (sk_opbuilder_t builder);
+
+               // sk_opbuilder_t* sk_opbuilder_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_opbuilder_t sk_opbuilder_new ();
+
+               // bool sk_opbuilder_resolve(sk_opbuilder_t* builder, sk_path_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_opbuilder_resolve (sk_opbuilder_t builder, sk_path_t result);
+
+               // void sk_path_add_arc(sk_path_t* cpath, const sk_rect_t* crect, float startAngle, float sweepAngle)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_arc (sk_path_t cpath, SKRect* crect, Single startAngle, Single sweepAngle);
+
+               // void sk_path_add_circle(sk_path_t*, float x, float y, float radius, sk_path_direction_t dir)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_circle (sk_path_t param0, Single x, Single y, Single radius, SKPathDirection dir);
+
+               // void sk_path_add_oval(sk_path_t*, const sk_rect_t*, sk_path_direction_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_oval (sk_path_t param0, SKRect* param1, SKPathDirection param2);
+
+               // void sk_path_add_path(sk_path_t* cpath, sk_path_t* other, sk_path_add_mode_t add_mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_path (sk_path_t cpath, sk_path_t other, SKPathAddMode add_mode);
+
+               // void sk_path_add_path_matrix(sk_path_t* cpath, sk_path_t* other, sk_matrix_t* matrix, sk_path_add_mode_t add_mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_path_matrix (sk_path_t cpath, sk_path_t other, SKMatrix* matrix, SKPathAddMode add_mode);
+
+               // void sk_path_add_path_offset(sk_path_t* cpath, sk_path_t* other, float dx, float dy, sk_path_add_mode_t add_mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_path_offset (sk_path_t cpath, sk_path_t other, Single dx, Single dy, SKPathAddMode add_mode);
+
+               // void sk_path_add_path_reverse(sk_path_t* cpath, sk_path_t* other)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_path_reverse (sk_path_t cpath, sk_path_t other);
+
+               // void sk_path_add_poly(sk_path_t* cpath, const sk_point_t* points, int count, bool close)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_poly (sk_path_t cpath, SKPoint* points, Int32 count, [MarshalAs (UnmanagedType.I1)] bool close);
+
+               // void sk_path_add_rect(sk_path_t*, const sk_rect_t*, sk_path_direction_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_rect (sk_path_t param0, SKRect* param1, SKPathDirection param2);
+
+               // void sk_path_add_rect_start(sk_path_t* cpath, const sk_rect_t* crect, sk_path_direction_t cdir, uint32_t startIndex)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_rect_start (sk_path_t cpath, SKRect* crect, SKPathDirection cdir, UInt32 startIndex);
+
+               // void sk_path_add_rounded_rect(sk_path_t*, const sk_rect_t*, float, float, sk_path_direction_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_rounded_rect (sk_path_t param0, SKRect* param1, Single param2, Single param3, SKPathDirection param4);
+
+               // void sk_path_add_rrect(sk_path_t*, const sk_rrect_t*, sk_path_direction_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_rrect (sk_path_t param0, sk_rrect_t param1, SKPathDirection param2);
+
+               // void sk_path_add_rrect_start(sk_path_t*, const sk_rrect_t*, sk_path_direction_t, uint32_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_add_rrect_start (sk_path_t param0, sk_rrect_t param1, SKPathDirection param2, UInt32 param3);
+
+               // void sk_path_arc_to(sk_path_t*, float rx, float ry, float xAxisRotate, sk_path_arc_size_t largeArc, sk_path_direction_t sweep, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_arc_to (sk_path_t param0, Single rx, Single ry, Single xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, Single x, Single y);
+
+               // void sk_path_arc_to_with_oval(sk_path_t*, const sk_rect_t* oval, float startAngle, float sweepAngle, bool forceMoveTo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_arc_to_with_oval (sk_path_t param0, SKRect* oval, Single startAngle, Single sweepAngle, [MarshalAs (UnmanagedType.I1)] bool forceMoveTo);
+
+               // void sk_path_arc_to_with_points(sk_path_t*, float x1, float y1, float x2, float y2, float radius)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_arc_to_with_points (sk_path_t param0, Single x1, Single y1, Single x2, Single y2, Single radius);
+
+               // sk_path_t* sk_path_clone(const sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_t sk_path_clone (sk_path_t cpath);
+
+               // void sk_path_close(sk_path_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_close (sk_path_t param0);
+
+               // void sk_path_compute_tight_bounds(const sk_path_t*, sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_compute_tight_bounds (sk_path_t param0, SKRect* param1);
+
+               // void sk_path_conic_to(sk_path_t*, float x0, float y0, float x1, float y1, float w)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_conic_to (sk_path_t param0, Single x0, Single y0, Single x1, Single y1, Single w);
+
+               // bool sk_path_contains(const sk_path_t* cpath, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_contains (sk_path_t cpath, Single x, Single y);
+
+               // int sk_path_convert_conic_to_quads(const sk_point_t* p0, const sk_point_t* p1, const sk_point_t* p2, float w, sk_point_t* pts, int pow2)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_convert_conic_to_quads (SKPoint* p0, SKPoint* p1, SKPoint* p2, Single w, SKPoint* pts, Int32 pow2);
+
+               // int sk_path_count_points(const sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_count_points (sk_path_t cpath);
+
+               // int sk_path_count_verbs(const sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_count_verbs (sk_path_t cpath);
+
+               // sk_path_iterator_t* sk_path_create_iter(sk_path_t* cpath, int forceClose)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_iterator_t sk_path_create_iter (sk_path_t cpath, Int32 forceClose);
+
+               // sk_path_rawiterator_t* sk_path_create_rawiter(sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_rawiterator_t sk_path_create_rawiter (sk_path_t cpath);
+
+               // void sk_path_cubic_to(sk_path_t*, float x0, float y0, float x1, float y1, float x2, float y2)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_cubic_to (sk_path_t param0, Single x0, Single y0, Single x1, Single y1, Single x2, Single y2);
+
+               // void sk_path_delete(sk_path_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_delete (sk_path_t param0);
+
+               // void sk_path_get_bounds(const sk_path_t*, sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_get_bounds (sk_path_t param0, SKRect* param1);
+
+               // sk_path_convexity_t sk_path_get_convexity(const sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPathConvexity sk_path_get_convexity (sk_path_t cpath);
+
+               // sk_path_filltype_t sk_path_get_filltype(sk_path_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPathFillType sk_path_get_filltype (sk_path_t param0);
+
+               // bool sk_path_get_last_point(const sk_path_t* cpath, sk_point_t* point)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_get_last_point (sk_path_t cpath, SKPoint* point);
+
+               // void sk_path_get_point(const sk_path_t* cpath, int index, sk_point_t* point)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_get_point (sk_path_t cpath, Int32 index, SKPoint* point);
+
+               // int sk_path_get_points(const sk_path_t* cpath, sk_point_t* points, int max)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_get_points (sk_path_t cpath, SKPoint* points, Int32 max);
+
+               // uint32_t sk_path_get_segment_masks(sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_path_get_segment_masks (sk_path_t cpath);
+
+               // bool sk_path_is_line(sk_path_t* cpath, sk_point_t[2] line = 2)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_is_line (sk_path_t cpath, SKPoint* line);
+
+               // bool sk_path_is_oval(sk_path_t* cpath, sk_rect_t* bounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_is_oval (sk_path_t cpath, SKRect* bounds);
+
+               // bool sk_path_is_rect(sk_path_t* cpath, sk_rect_t* rect, bool* isClosed, sk_path_direction_t* direction)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_is_rect (sk_path_t cpath, SKRect* rect, Byte* isClosed, SKPathDirection* direction);
+
+               // bool sk_path_is_rrect(sk_path_t* cpath, sk_rrect_t* bounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_is_rrect (sk_path_t cpath, sk_rrect_t bounds);
+
+               // float sk_path_iter_conic_weight(sk_path_iterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_path_iter_conic_weight (sk_path_iterator_t iterator);
+
+               // void sk_path_iter_destroy(sk_path_iterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_iter_destroy (sk_path_iterator_t iterator);
+
+               // int sk_path_iter_is_close_line(sk_path_iterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_iter_is_close_line (sk_path_iterator_t iterator);
+
+               // int sk_path_iter_is_closed_contour(sk_path_iterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_path_iter_is_closed_contour (sk_path_iterator_t iterator);
+
+               // sk_path_verb_t sk_path_iter_next(sk_path_iterator_t* iterator, sk_point_t[4] points = 4, int doConsumeDegenerates, int exact)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPathVerb sk_path_iter_next (sk_path_iterator_t iterator, SKPoint* points, Int32 doConsumeDegenerates, Int32 exact);
+
+               // void sk_path_line_to(sk_path_t*, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_line_to (sk_path_t param0, Single x, Single y);
+
+               // void sk_path_move_to(sk_path_t*, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_move_to (sk_path_t param0, Single x, Single y);
+
+               // sk_path_t* sk_path_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_t sk_path_new ();
+
+               // bool sk_path_parse_svg_string(sk_path_t* cpath, const char* str)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_path_parse_svg_string (sk_path_t cpath, [MarshalAs (UnmanagedType.LPStr)] String str);
+
+               // void sk_path_quad_to(sk_path_t*, float x0, float y0, float x1, float y1)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_quad_to (sk_path_t param0, Single x0, Single y0, Single x1, Single y1);
+
+               // void sk_path_rarc_to(sk_path_t*, float rx, float ry, float xAxisRotate, sk_path_arc_size_t largeArc, sk_path_direction_t sweep, float x, float y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rarc_to (sk_path_t param0, Single rx, Single ry, Single xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, Single x, Single y);
+
+               // float sk_path_rawiter_conic_weight(sk_path_rawiterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_path_rawiter_conic_weight (sk_path_rawiterator_t iterator);
+
+               // void sk_path_rawiter_destroy(sk_path_rawiterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rawiter_destroy (sk_path_rawiterator_t iterator);
+
+               // sk_path_verb_t sk_path_rawiter_next(sk_path_rawiterator_t* iterator, sk_point_t[4] points = 4)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPathVerb sk_path_rawiter_next (sk_path_rawiterator_t iterator, SKPoint* points);
+
+               // sk_path_verb_t sk_path_rawiter_peek(sk_path_rawiterator_t* iterator)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPathVerb sk_path_rawiter_peek (sk_path_rawiterator_t iterator);
+
+               // void sk_path_rconic_to(sk_path_t*, float dx0, float dy0, float dx1, float dy1, float w)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rconic_to (sk_path_t param0, Single dx0, Single dy0, Single dx1, Single dy1, Single w);
+
+               // void sk_path_rcubic_to(sk_path_t*, float dx0, float dy0, float dx1, float dy1, float dx2, float dy2)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rcubic_to (sk_path_t param0, Single dx0, Single dy0, Single dx1, Single dy1, Single dx2, Single dy2);
+
+               // void sk_path_reset(sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_reset (sk_path_t cpath);
+
+               // void sk_path_rewind(sk_path_t* cpath)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rewind (sk_path_t cpath);
+
+               // void sk_path_rline_to(sk_path_t*, float dx, float yd)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rline_to (sk_path_t param0, Single dx, Single yd);
+
+               // void sk_path_rmove_to(sk_path_t*, float dx, float dy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rmove_to (sk_path_t param0, Single dx, Single dy);
+
+               // void sk_path_rquad_to(sk_path_t*, float dx0, float dy0, float dx1, float dy1)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_rquad_to (sk_path_t param0, Single dx0, Single dy0, Single dx1, Single dy1);
+
+               // void sk_path_set_convexity(sk_path_t* cpath, sk_path_convexity_t convexity)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_set_convexity (sk_path_t cpath, SKPathConvexity convexity);
+
+               // void sk_path_set_filltype(sk_path_t*, sk_path_filltype_t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_set_filltype (sk_path_t param0, SKPathFillType param1);
+
+               // void sk_path_to_svg_string(const sk_path_t* cpath, sk_string_t* str)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_to_svg_string (sk_path_t cpath, sk_string_t str);
+
+               // void sk_path_transform(sk_path_t* cpath, const sk_matrix_t* cmatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_transform (sk_path_t cpath, SKMatrix* cmatrix);
+
+               // void sk_path_transform_to_dest(sk_path_t* cpath, const sk_matrix_t* cmatrix, sk_path_t* destination)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_transform_to_dest (sk_path_t cpath, SKMatrix* cmatrix, sk_path_t destination);
+
+               // void sk_pathmeasure_destroy(sk_pathmeasure_t* pathMeasure)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pathmeasure_destroy (sk_pathmeasure_t pathMeasure);
+
+               // float sk_pathmeasure_get_length(sk_pathmeasure_t* pathMeasure)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_pathmeasure_get_length (sk_pathmeasure_t pathMeasure);
+
+               // bool sk_pathmeasure_get_matrix(sk_pathmeasure_t* pathMeasure, float distance, sk_matrix_t* matrix, sk_pathmeasure_matrixflags_t flags)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathmeasure_get_matrix (sk_pathmeasure_t pathMeasure, Single distance, SKMatrix* matrix, SKPathMeasureMatrixFlags flags);
+
+               // bool sk_pathmeasure_get_pos_tan(sk_pathmeasure_t* pathMeasure, float distance, sk_point_t* position, sk_vector_t* tangent)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathmeasure_get_pos_tan (sk_pathmeasure_t pathMeasure, Single distance, SKPoint* position, SKPoint* tangent);
+
+               // bool sk_pathmeasure_get_segment(sk_pathmeasure_t* pathMeasure, float start, float stop, sk_path_t* dst, bool startWithMoveTo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathmeasure_get_segment (sk_pathmeasure_t pathMeasure, Single start, Single stop, sk_path_t dst, [MarshalAs (UnmanagedType.I1)] bool startWithMoveTo);
+
+               // bool sk_pathmeasure_is_closed(sk_pathmeasure_t* pathMeasure)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathmeasure_is_closed (sk_pathmeasure_t pathMeasure);
+
+               // sk_pathmeasure_t* sk_pathmeasure_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_pathmeasure_t sk_pathmeasure_new ();
+
+               // sk_pathmeasure_t* sk_pathmeasure_new_with_path(const sk_path_t* path, bool forceClosed, float resScale)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_pathmeasure_t sk_pathmeasure_new_with_path (sk_path_t path, [MarshalAs (UnmanagedType.I1)] bool forceClosed, Single resScale);
+
+               // bool sk_pathmeasure_next_contour(sk_pathmeasure_t* pathMeasure)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathmeasure_next_contour (sk_pathmeasure_t pathMeasure);
+
+               // void sk_pathmeasure_set_path(sk_pathmeasure_t* pathMeasure, const sk_path_t* path, bool forceClosed)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pathmeasure_set_path (sk_pathmeasure_t pathMeasure, sk_path_t path, [MarshalAs (UnmanagedType.I1)] bool forceClosed);
+
+               // bool sk_pathop_op(const sk_path_t* one, const sk_path_t* two, sk_pathop_t op, sk_path_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathop_op (sk_path_t one, sk_path_t two, SKPathOp op, sk_path_t result);
+
+               // bool sk_pathop_simplify(const sk_path_t* path, sk_path_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathop_simplify (sk_path_t path, sk_path_t result);
+
+               // bool sk_pathop_tight_bounds(const sk_path_t* path, sk_rect_t* result)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pathop_tight_bounds (sk_path_t path, SKRect* result);
+
+               #endregion
+
+               #region sk_patheffect.h
+
+               // sk_path_effect_t* sk_path_effect_create_1d_path(const sk_path_t* path, float advance, float phase, sk_path_effect_1d_style_t style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_1d_path (sk_path_t path, Single advance, Single phase, SKPath1DPathEffectStyle style);
+
+               // sk_path_effect_t* sk_path_effect_create_2d_line(float width, const sk_matrix_t* matrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_2d_line (Single width, SKMatrix* matrix);
+
+               // sk_path_effect_t* sk_path_effect_create_2d_path(const sk_matrix_t* matrix, const sk_path_t* path)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_2d_path (SKMatrix* matrix, sk_path_t path);
+
+               // sk_path_effect_t* sk_path_effect_create_compose(sk_path_effect_t* outer, sk_path_effect_t* inner)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_compose (sk_path_effect_t outer, sk_path_effect_t inner);
+
+               // sk_path_effect_t* sk_path_effect_create_corner(float radius)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_corner (Single radius);
+
+               // sk_path_effect_t* sk_path_effect_create_dash(const float[-1] intervals, int count, float phase)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_dash (Single* intervals, Int32 count, Single phase);
+
+               // sk_path_effect_t* sk_path_effect_create_discrete(float segLength, float deviation, uint32_t seedAssist)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_discrete (Single segLength, Single deviation, UInt32 seedAssist);
+
+               // sk_path_effect_t* sk_path_effect_create_sum(sk_path_effect_t* first, sk_path_effect_t* second)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_sum (sk_path_effect_t first, sk_path_effect_t second);
+
+               // sk_path_effect_t* sk_path_effect_create_trim(float start, float stop, sk_path_effect_trim_mode_t mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_path_effect_t sk_path_effect_create_trim (Single start, Single stop, SKTrimPathEffectMode mode);
+
+               // void sk_path_effect_unref(sk_path_effect_t* t)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_path_effect_unref (sk_path_effect_t t);
+
+               #endregion
+
+               #region sk_picture.h
+
+               // void sk_picture_get_cull_rect(sk_picture_t*, sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_picture_get_cull_rect (sk_picture_t param0, SKRect* param1);
+
+               // sk_canvas_t* sk_picture_get_recording_canvas(sk_picture_recorder_t* crec)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_picture_get_recording_canvas (sk_picture_recorder_t crec);
+
+               // uint32_t sk_picture_get_unique_id(sk_picture_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_picture_get_unique_id (sk_picture_t param0);
+
+               // sk_canvas_t* sk_picture_recorder_begin_recording(sk_picture_recorder_t*, const sk_rect_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_picture_recorder_begin_recording (sk_picture_recorder_t param0, SKRect* param1);
+
+               // void sk_picture_recorder_delete(sk_picture_recorder_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_picture_recorder_delete (sk_picture_recorder_t param0);
+
+               // sk_picture_t* sk_picture_recorder_end_recording(sk_picture_recorder_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_picture_t sk_picture_recorder_end_recording (sk_picture_recorder_t param0);
+
+               // sk_drawable_t* sk_picture_recorder_end_recording_as_drawable(sk_picture_recorder_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_drawable_t sk_picture_recorder_end_recording_as_drawable (sk_picture_recorder_t param0);
+
+               // sk_picture_recorder_t* sk_picture_recorder_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_picture_recorder_t sk_picture_recorder_new ();
+
+               // void sk_picture_ref(sk_picture_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_picture_ref (sk_picture_t param0);
+
+               // void sk_picture_unref(sk_picture_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_picture_unref (sk_picture_t param0);
+
+               #endregion
+
+               #region sk_pixmap.h
+
+               // void sk_color_get_bit_shift(int* a, int* r, int* g, int* b)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_color_get_bit_shift (Int32* a, Int32* r, Int32* g, Int32* b);
+
+               // sk_pmcolor_t sk_color_premultiply(const sk_color_t color)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_color_premultiply (UInt32 color);
+
+               // void sk_color_premultiply_array(const sk_color_t* colors, int size, sk_pmcolor_t* pmcolors)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_color_premultiply_array (UInt32* colors, Int32 size, UInt32* pmcolors);
+
+               // sk_color_t sk_color_unpremultiply(const sk_pmcolor_t pmcolor)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_color_unpremultiply (UInt32 pmcolor);
+
+               // void sk_color_unpremultiply_array(const sk_pmcolor_t* pmcolors, int size, sk_color_t* colors)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_color_unpremultiply_array (UInt32* pmcolors, Int32 size, UInt32* colors);
+
+               // bool sk_jpegencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_jpegencoder_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_jpegencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKJpegEncoderOptions* options);
+
+               // void sk_pixmap_destructor(sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pixmap_destructor (sk_pixmap_t cpixmap);
+
+               // bool sk_pixmap_encode_image(sk_wstream_t* dst, const sk_pixmap_t* src, sk_encoded_image_format_t encoder, int quality)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_encode_image (sk_wstream_t dst, sk_pixmap_t src, SKEncodedImageFormat encoder, Int32 quality);
+
+               // bool sk_pixmap_erase_color(const sk_pixmap_t* cpixmap, sk_color_t color, const sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_erase_color (sk_pixmap_t cpixmap, UInt32 color, SKRectI* subset);
+
+               // bool sk_pixmap_erase_color4f(const sk_pixmap_t* cpixmap, const sk_color4f_t* color, const sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_erase_color4f (sk_pixmap_t cpixmap, SKColorF* color, SKRectI* subset);
+
+               // bool sk_pixmap_extract_subset(const sk_pixmap_t* cpixmap, sk_pixmap_t* result, const sk_irect_t* subset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_extract_subset (sk_pixmap_t cpixmap, sk_pixmap_t result, SKRectI* subset);
+
+               // void sk_pixmap_get_info(const sk_pixmap_t* cpixmap, sk_imageinfo_t* cinfo)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pixmap_get_info (sk_pixmap_t cpixmap, SKImageInfoNative* cinfo);
+
+               // sk_color_t sk_pixmap_get_pixel_color(const sk_pixmap_t* cpixmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_pixmap_get_pixel_color (sk_pixmap_t cpixmap, Int32 x, Int32 y);
+
+               // const void* sk_pixmap_get_pixels(const sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_pixmap_get_pixels (sk_pixmap_t cpixmap);
+
+               // const void* sk_pixmap_get_pixels_with_xy(const sk_pixmap_t* cpixmap, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_pixmap_get_pixels_with_xy (sk_pixmap_t cpixmap, Int32 x, Int32 y);
+
+               // size_t sk_pixmap_get_row_bytes(const sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_pixmap_get_row_bytes (sk_pixmap_t cpixmap);
+
+               // sk_pixmap_t* sk_pixmap_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_pixmap_t sk_pixmap_new ();
+
+               // sk_pixmap_t* sk_pixmap_new_with_params(const sk_imageinfo_t* cinfo, const void* addr, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_pixmap_t sk_pixmap_new_with_params (SKImageInfoNative* cinfo, void* addr, /* size_t */ IntPtr rowBytes);
+
+               // bool sk_pixmap_read_pixels(const sk_pixmap_t* cpixmap, const sk_imageinfo_t* dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, int srcY, sk_transfer_function_behavior_t behavior)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_read_pixels (sk_pixmap_t cpixmap, SKImageInfoNative* dstInfo, void* dstPixels, /* size_t */ IntPtr dstRowBytes, Int32 srcX, Int32 srcY, SKTransferFunctionBehavior behavior);
+
+               // void sk_pixmap_reset(sk_pixmap_t* cpixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pixmap_reset (sk_pixmap_t cpixmap);
+
+               // void sk_pixmap_reset_with_params(sk_pixmap_t* cpixmap, const sk_imageinfo_t* cinfo, const void* addr, size_t rowBytes)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_pixmap_reset_with_params (sk_pixmap_t cpixmap, SKImageInfoNative* cinfo, void* addr, /* size_t */ IntPtr rowBytes);
+
+               // bool sk_pixmap_scale_pixels(const sk_pixmap_t* cpixmap, const sk_pixmap_t* dst, sk_filter_quality_t quality)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pixmap_scale_pixels (sk_pixmap_t cpixmap, sk_pixmap_t dst, SKFilterQuality quality);
+
+               // bool sk_pngencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_pngencoder_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_pngencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKPngEncoderOptions* options);
+
+               // void sk_swizzle_swap_rb(uint32_t* dest, const uint32_t* src, int count)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_swizzle_swap_rb (UInt32* dest, UInt32* src, Int32 count);
+
+               // bool sk_webpencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_webpencoder_options_t* options)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_webpencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKWebpEncoderOptions* options);
+
+               #endregion
+
+               #region sk_region.h
+
+               // void sk_region_cliperator_delete(sk_region_cliperator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_cliperator_delete (sk_region_cliperator_t iter);
+
+               // bool sk_region_cliperator_done(sk_region_cliperator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_cliperator_done (sk_region_cliperator_t iter);
+
+               // sk_region_cliperator_t* sk_region_cliperator_new(const sk_region_t* region, const sk_irect_t* clip)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_region_cliperator_t sk_region_cliperator_new (sk_region_t region, SKRectI* clip);
+
+               // void sk_region_cliperator_next(sk_region_cliperator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_cliperator_next (sk_region_cliperator_t iter);
+
+               // void sk_region_cliperator_rect(const sk_region_cliperator_t* iter, sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_cliperator_rect (sk_region_cliperator_t iter, SKRectI* rect);
+
+               // bool sk_region_contains(const sk_region_t* r, const sk_region_t* region)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_contains (sk_region_t r, sk_region_t region);
+
+               // bool sk_region_contains_point(const sk_region_t* r, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_contains_point (sk_region_t r, Int32 x, Int32 y);
+
+               // bool sk_region_contains_rect(const sk_region_t* r, const sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_contains_rect (sk_region_t r, SKRectI* rect);
+
+               // void sk_region_delete(sk_region_t* r)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_delete (sk_region_t r);
+
+               // bool sk_region_get_boundary_path(const sk_region_t* r, sk_path_t* path)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_get_boundary_path (sk_region_t r, sk_path_t path);
+
+               // void sk_region_get_bounds(const sk_region_t* r, sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_get_bounds (sk_region_t r, SKRectI* rect);
+
+               // bool sk_region_intersects(const sk_region_t* r, const sk_region_t* src)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_intersects (sk_region_t r, sk_region_t src);
+
+               // bool sk_region_intersects_rect(const sk_region_t* r, const sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_intersects_rect (sk_region_t r, SKRectI* rect);
+
+               // bool sk_region_is_complex(const sk_region_t* r)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_is_complex (sk_region_t r);
+
+               // bool sk_region_is_empty(const sk_region_t* r)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_is_empty (sk_region_t r);
+
+               // bool sk_region_is_rect(const sk_region_t* r)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_is_rect (sk_region_t r);
+
+               // void sk_region_iterator_delete(sk_region_iterator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_iterator_delete (sk_region_iterator_t iter);
+
+               // bool sk_region_iterator_done(const sk_region_iterator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_iterator_done (sk_region_iterator_t iter);
+
+               // sk_region_iterator_t* sk_region_iterator_new(const sk_region_t* region)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_region_iterator_t sk_region_iterator_new (sk_region_t region);
+
+               // void sk_region_iterator_next(sk_region_iterator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_iterator_next (sk_region_iterator_t iter);
+
+               // void sk_region_iterator_rect(const sk_region_iterator_t* iter, sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_iterator_rect (sk_region_iterator_t iter, SKRectI* rect);
+
+               // bool sk_region_iterator_rewind(sk_region_iterator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_iterator_rewind (sk_region_iterator_t iter);
+
+               // sk_region_t* sk_region_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_region_t sk_region_new ();
+
+               // bool sk_region_op(sk_region_t* r, const sk_region_t* region, sk_region_op_t op)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_op (sk_region_t r, sk_region_t region, SKRegionOperation op);
+
+               // bool sk_region_op_rect(sk_region_t* r, const sk_irect_t* rect, sk_region_op_t op)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_op_rect (sk_region_t r, SKRectI* rect, SKRegionOperation op);
+
+               // bool sk_region_quick_contains(const sk_region_t* r, const sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_quick_contains (sk_region_t r, SKRectI* rect);
+
+               // bool sk_region_quick_reject(const sk_region_t* r, const sk_region_t* region)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_quick_reject (sk_region_t r, sk_region_t region);
+
+               // bool sk_region_quick_reject_rect(const sk_region_t* r, const sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_quick_reject_rect (sk_region_t r, SKRectI* rect);
+
+               // bool sk_region_set_empty(sk_region_t* r)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_set_empty (sk_region_t r);
+
+               // bool sk_region_set_path(sk_region_t* r, const sk_path_t* t, const sk_region_t* clip)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_set_path (sk_region_t r, sk_path_t t, sk_region_t clip);
+
+               // bool sk_region_set_rect(sk_region_t* r, const sk_irect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_set_rect (sk_region_t r, SKRectI* rect);
+
+               // bool sk_region_set_rects(sk_region_t* r, const sk_irect_t* rects, int count)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_set_rects (sk_region_t r, SKRectI* rects, Int32 count);
+
+               // bool sk_region_set_region(sk_region_t* r, const sk_region_t* region)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_set_region (sk_region_t r, sk_region_t region);
+
+               // void sk_region_spanerator_delete(sk_region_spanerator_t* iter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_spanerator_delete (sk_region_spanerator_t iter);
+
+               // sk_region_spanerator_t* sk_region_spanerator_new(const sk_region_t* region, int y, int left, int right)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_region_spanerator_t sk_region_spanerator_new (sk_region_t region, Int32 y, Int32 left, Int32 right);
+
+               // bool sk_region_spanerator_next(sk_region_spanerator_t* iter, int* left, int* right)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_region_spanerator_next (sk_region_spanerator_t iter, Int32* left, Int32* right);
+
+               // void sk_region_translate(sk_region_t* r, int x, int y)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_region_translate (sk_region_t r, Int32 x, Int32 y);
+
+               #endregion
+
+               #region sk_rrect.h
+
+               // bool sk_rrect_contains(const sk_rrect_t* rrect, const sk_rect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_rrect_contains (sk_rrect_t rrect, SKRect* rect);
+
+               // void sk_rrect_delete(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_delete (sk_rrect_t rrect);
+
+               // float sk_rrect_get_height(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_rrect_get_height (sk_rrect_t rrect);
+
+               // void sk_rrect_get_radii(const sk_rrect_t* rrect, sk_rrect_corner_t corner, sk_vector_t* radii)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_get_radii (sk_rrect_t rrect, SKRoundRectCorner corner, SKPoint* radii);
+
+               // void sk_rrect_get_rect(const sk_rrect_t* rrect, sk_rect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_get_rect (sk_rrect_t rrect, SKRect* rect);
+
+               // sk_rrect_type_t sk_rrect_get_type(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKRoundRectType sk_rrect_get_type (sk_rrect_t rrect);
+
+               // float sk_rrect_get_width(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Single sk_rrect_get_width (sk_rrect_t rrect);
+
+               // void sk_rrect_inset(sk_rrect_t* rrect, float dx, float dy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_inset (sk_rrect_t rrect, Single dx, Single dy);
+
+               // bool sk_rrect_is_valid(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_rrect_is_valid (sk_rrect_t rrect);
+
+               // sk_rrect_t* sk_rrect_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_rrect_t sk_rrect_new ();
+
+               // sk_rrect_t* sk_rrect_new_copy(const sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_rrect_t sk_rrect_new_copy (sk_rrect_t rrect);
+
+               // void sk_rrect_offset(sk_rrect_t* rrect, float dx, float dy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_offset (sk_rrect_t rrect, Single dx, Single dy);
+
+               // void sk_rrect_outset(sk_rrect_t* rrect, float dx, float dy)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_outset (sk_rrect_t rrect, Single dx, Single dy);
+
+               // void sk_rrect_set_empty(sk_rrect_t* rrect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_empty (sk_rrect_t rrect);
+
+               // void sk_rrect_set_nine_patch(sk_rrect_t* rrect, const sk_rect_t* rect, float leftRad, float topRad, float rightRad, float bottomRad)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_nine_patch (sk_rrect_t rrect, SKRect* rect, Single leftRad, Single topRad, Single rightRad, Single bottomRad);
+
+               // void sk_rrect_set_oval(sk_rrect_t* rrect, const sk_rect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_oval (sk_rrect_t rrect, SKRect* rect);
+
+               // void sk_rrect_set_rect(sk_rrect_t* rrect, const sk_rect_t* rect)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_rect (sk_rrect_t rrect, SKRect* rect);
+
+               // void sk_rrect_set_rect_radii(sk_rrect_t* rrect, const sk_rect_t* rect, const sk_vector_t* radii)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_rect_radii (sk_rrect_t rrect, SKRect* rect, SKPoint* radii);
+
+               // void sk_rrect_set_rect_xy(sk_rrect_t* rrect, const sk_rect_t* rect, float xRad, float yRad)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_rrect_set_rect_xy (sk_rrect_t rrect, SKRect* rect, Single xRad, Single yRad);
+
+               // bool sk_rrect_transform(sk_rrect_t* rrect, const sk_matrix_t* matrix, sk_rrect_t* dest)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_rrect_transform (sk_rrect_t rrect, SKMatrix* matrix, sk_rrect_t dest);
+
+               #endregion
+
+               #region sk_shader.h
+
+               // sk_shader_t* sk_shader_new_bitmap(const sk_bitmap_t* src, sk_shader_tilemode_t tmx, sk_shader_tilemode_t tmy, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_bitmap (sk_bitmap_t src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_color(sk_color_t color)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_color (UInt32 color);
+
+               // sk_shader_t* sk_shader_new_color4f(const sk_color4f_t* color, const sk_colorspace_t* colorspace)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_color4f (SKColorF* color, sk_colorspace_t colorspace);
+
+               // sk_shader_t* sk_shader_new_compose(const sk_shader_t* shaderA, const sk_shader_t* shaderB, sk_blendmode_t mode)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_compose (sk_shader_t shaderA, sk_shader_t shaderB, SKBlendMode mode);
+
+               // sk_shader_t* sk_shader_new_empty()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_empty ();
+
+               // sk_shader_t* sk_shader_new_linear_gradient(const sk_point_t[2] points = 2, const sk_color_t[-1] colors, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_linear_gradient (SKPoint* points, UInt32* colors, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_linear_gradient_color4f(const sk_point_t[2] points = 2, const sk_color4f_t* colors, const sk_colorspace_t* colorspace, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_linear_gradient_color4f (SKPoint* points, SKColorF* colors, sk_colorspace_t colorspace, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_perlin_noise_fractal_noise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, const sk_isize_t* tileSize)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_perlin_noise_fractal_noise (Single baseFrequencyX, Single baseFrequencyY, Int32 numOctaves, Single seed, SKSizeI* tileSize);
+
+               // sk_shader_t* sk_shader_new_perlin_noise_improved_noise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float z)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_perlin_noise_improved_noise (Single baseFrequencyX, Single baseFrequencyY, Int32 numOctaves, Single z);
+
+               // sk_shader_t* sk_shader_new_perlin_noise_turbulence(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, const sk_isize_t* tileSize)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_perlin_noise_turbulence (Single baseFrequencyX, Single baseFrequencyY, Int32 numOctaves, Single seed, SKSizeI* tileSize);
+
+               // sk_shader_t* sk_shader_new_picture(sk_picture_t* src, sk_shader_tilemode_t tmx, sk_shader_tilemode_t tmy, const sk_matrix_t* localMatrix, const sk_rect_t* tile)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_picture (sk_picture_t src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix* localMatrix, SKRect* tile);
+
+               // sk_shader_t* sk_shader_new_radial_gradient(const sk_point_t* center, float radius, const sk_color_t[-1] colors, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_radial_gradient (SKPoint* center, Single radius, UInt32* colors, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_radial_gradient_color4f(const sk_point_t* center, float radius, const sk_color4f_t* colors, const sk_colorspace_t* colorspace, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_radial_gradient_color4f (SKPoint* center, Single radius, SKColorF* colors, sk_colorspace_t colorspace, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_sweep_gradient(const sk_point_t* center, const sk_color_t[-1] colors, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, float startAngle, float endAngle, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_sweep_gradient (SKPoint* center, UInt32* colors, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, Single startAngle, Single endAngle, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_sweep_gradient_color4f(const sk_point_t* center, const sk_color4f_t* colors, const sk_colorspace_t* colorspace, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, float startAngle, float endAngle, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_sweep_gradient_color4f (SKPoint* center, SKColorF* colors, sk_colorspace_t colorspace, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, Single startAngle, Single endAngle, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_two_point_conical_gradient(const sk_point_t* start, float startRadius, const sk_point_t* end, float endRadius, const sk_color_t[-1] colors, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_two_point_conical_gradient (SKPoint* start, Single startRadius, SKPoint* end, Single endRadius, UInt32* colors, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // sk_shader_t* sk_shader_new_two_point_conical_gradient_color4f(const sk_point_t* start, float startRadius, const sk_point_t* end, float endRadius, const sk_color4f_t* colors, const sk_colorspace_t* colorspace, const float[-1] colorPos, int colorCount, sk_shader_tilemode_t tileMode, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_new_two_point_conical_gradient_color4f (SKPoint* start, Single startRadius, SKPoint* end, Single endRadius, SKColorF* colors, sk_colorspace_t colorspace, Single* colorPos, Int32 colorCount, SKShaderTileMode tileMode, SKMatrix* localMatrix);
+
+               // void sk_shader_ref(sk_shader_t* shader)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_shader_ref (sk_shader_t shader);
+
+               // void sk_shader_unref(sk_shader_t* shader)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_shader_unref (sk_shader_t shader);
+
+               // sk_shader_t* sk_shader_with_color_filter(const sk_shader_t* shader, const sk_colorfilter_t* filter)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_with_color_filter (sk_shader_t shader, sk_colorfilter_t filter);
+
+               // sk_shader_t* sk_shader_with_local_matrix(const sk_shader_t* shader, const sk_matrix_t* localMatrix)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_shader_t sk_shader_with_local_matrix (sk_shader_t shader, SKMatrix* localMatrix);
+
+               #endregion
+
+               #region sk_stream.h
+
+               // void sk_dynamicmemorywstream_copy_to(sk_wstream_dynamicmemorystream_t* cstream, void* data)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_dynamicmemorywstream_copy_to (sk_wstream_dynamicmemorystream_t cstream, void* data);
+
+               // void sk_dynamicmemorywstream_destroy(sk_wstream_dynamicmemorystream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_dynamicmemorywstream_destroy (sk_wstream_dynamicmemorystream_t cstream);
+
+               // sk_data_t* sk_dynamicmemorywstream_detach_as_data(sk_wstream_dynamicmemorystream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_data_t sk_dynamicmemorywstream_detach_as_data (sk_wstream_dynamicmemorystream_t cstream);
+
+               // sk_stream_asset_t* sk_dynamicmemorywstream_detach_as_stream(sk_wstream_dynamicmemorystream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_asset_t sk_dynamicmemorywstream_detach_as_stream (sk_wstream_dynamicmemorystream_t cstream);
+
+               // sk_wstream_dynamicmemorystream_t* sk_dynamicmemorywstream_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_wstream_dynamicmemorystream_t sk_dynamicmemorywstream_new ();
+
+               // bool sk_dynamicmemorywstream_write_to_stream(sk_wstream_dynamicmemorystream_t* cstream, sk_wstream_t* dst)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_dynamicmemorywstream_write_to_stream (sk_wstream_dynamicmemorystream_t cstream, sk_wstream_t dst);
+
+               // void sk_filestream_destroy(sk_stream_filestream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_filestream_destroy (sk_stream_filestream_t cstream);
+
+               // bool sk_filestream_is_valid(sk_stream_filestream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_filestream_is_valid (sk_stream_filestream_t cstream);
+
+               // sk_stream_filestream_t* sk_filestream_new(const char* path)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_filestream_t sk_filestream_new (/* char */ void* path);
+
+               // void sk_filewstream_destroy(sk_wstream_filestream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_filewstream_destroy (sk_wstream_filestream_t cstream);
+
+               // bool sk_filewstream_is_valid(sk_wstream_filestream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_filewstream_is_valid (sk_wstream_filestream_t cstream);
+
+               // sk_wstream_filestream_t* sk_filewstream_new(const char* path)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_wstream_filestream_t sk_filewstream_new (/* char */ void* path);
+
+               // void sk_memorystream_destroy(sk_stream_memorystream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_memorystream_destroy (sk_stream_memorystream_t cstream);
+
+               // sk_stream_memorystream_t* sk_memorystream_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_memorystream_t sk_memorystream_new ();
+
+               // sk_stream_memorystream_t* sk_memorystream_new_with_data(const void* data, size_t length, bool copyData)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_memorystream_t sk_memorystream_new_with_data (void* data, /* size_t */ IntPtr length, [MarshalAs (UnmanagedType.I1)] bool copyData);
+
+               // sk_stream_memorystream_t* sk_memorystream_new_with_length(size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_memorystream_t sk_memorystream_new_with_length (/* size_t */ IntPtr length);
+
+               // sk_stream_memorystream_t* sk_memorystream_new_with_skdata(sk_data_t* data)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_memorystream_t sk_memorystream_new_with_skdata (sk_data_t data);
+
+               // void sk_memorystream_set_memory(sk_stream_memorystream_t* cmemorystream, const void* data, size_t length, bool copyData)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_memorystream_set_memory (sk_stream_memorystream_t cmemorystream, void* data, /* size_t */ IntPtr length, [MarshalAs (UnmanagedType.I1)] bool copyData);
+
+               // void sk_stream_asset_destroy(sk_stream_asset_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_stream_asset_destroy (sk_stream_asset_t cstream);
+
+               // void sk_stream_destroy(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_stream_destroy (sk_stream_t cstream);
+
+               // sk_stream_t* sk_stream_duplicate(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_t sk_stream_duplicate (sk_stream_t cstream);
+
+               // sk_stream_t* sk_stream_fork(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_t sk_stream_fork (sk_stream_t cstream);
+
+               // size_t sk_stream_get_length(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_stream_get_length (sk_stream_t cstream);
+
+               // const void* sk_stream_get_memory_base(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void* sk_stream_get_memory_base (sk_stream_t cstream);
+
+               // size_t sk_stream_get_position(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_stream_get_position (sk_stream_t cstream);
+
+               // bool sk_stream_has_length(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_has_length (sk_stream_t cstream);
+
+               // bool sk_stream_has_position(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_has_position (sk_stream_t cstream);
+
+               // bool sk_stream_is_at_end(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_is_at_end (sk_stream_t cstream);
+
+               // bool sk_stream_move(sk_stream_t* cstream, int offset)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_move (sk_stream_t cstream, Int32 offset);
+
+               // size_t sk_stream_peek(sk_stream_t* cstream, void* buffer, size_t size)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_stream_peek (sk_stream_t cstream, void* buffer, /* size_t */ IntPtr size);
+
+               // size_t sk_stream_read(sk_stream_t* cstream, void* buffer, size_t size)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_stream_read (sk_stream_t cstream, void* buffer, /* size_t */ IntPtr size);
+
+               // bool sk_stream_read_bool(sk_stream_t* cstream, bool* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_bool (sk_stream_t cstream, Byte* buffer);
+
+               // bool sk_stream_read_s16(sk_stream_t* cstream, int16_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_s16 (sk_stream_t cstream, Int16* buffer);
+
+               // bool sk_stream_read_s32(sk_stream_t* cstream, int32_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_s32 (sk_stream_t cstream, Int32* buffer);
+
+               // bool sk_stream_read_s8(sk_stream_t* cstream, int8_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_s8 (sk_stream_t cstream, SByte* buffer);
+
+               // bool sk_stream_read_u16(sk_stream_t* cstream, uint16_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_u16 (sk_stream_t cstream, UInt16* buffer);
+
+               // bool sk_stream_read_u32(sk_stream_t* cstream, uint32_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_u32 (sk_stream_t cstream, UInt32* buffer);
+
+               // bool sk_stream_read_u8(sk_stream_t* cstream, uint8_t* buffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_read_u8 (sk_stream_t cstream, Byte* buffer);
+
+               // bool sk_stream_rewind(sk_stream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_rewind (sk_stream_t cstream);
+
+               // bool sk_stream_seek(sk_stream_t* cstream, size_t position)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_stream_seek (sk_stream_t cstream, /* size_t */ IntPtr position);
+
+               // size_t sk_stream_skip(sk_stream_t* cstream, size_t size)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_stream_skip (sk_stream_t cstream, /* size_t */ IntPtr size);
+
+               // size_t sk_wstream_bytes_written(sk_wstream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_wstream_bytes_written (sk_wstream_t cstream);
+
+               // void sk_wstream_flush(sk_wstream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_wstream_flush (sk_wstream_t cstream);
+
+               // int sk_wstream_get_size_of_packed_uint(size_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_wstream_get_size_of_packed_uint (/* size_t */ IntPtr value);
+
+               // bool sk_wstream_newline(sk_wstream_t* cstream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_newline (sk_wstream_t cstream);
+
+               // bool sk_wstream_write(sk_wstream_t* cstream, const void* buffer, size_t size)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write (sk_wstream_t cstream, void* buffer, /* size_t */ IntPtr size);
+
+               // bool sk_wstream_write_16(sk_wstream_t* cstream, uint16_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_16 (sk_wstream_t cstream, UInt16 value);
+
+               // bool sk_wstream_write_32(sk_wstream_t* cstream, uint32_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_32 (sk_wstream_t cstream, UInt32 value);
+
+               // bool sk_wstream_write_8(sk_wstream_t* cstream, uint8_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_8 (sk_wstream_t cstream, Byte value);
+
+               // bool sk_wstream_write_bigdec_as_text(sk_wstream_t* cstream, int64_t value, int minDigits)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_bigdec_as_text (sk_wstream_t cstream, Int64 value, Int32 minDigits);
+
+               // bool sk_wstream_write_bool(sk_wstream_t* cstream, bool value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_bool (sk_wstream_t cstream, [MarshalAs (UnmanagedType.I1)] bool value);
+
+               // bool sk_wstream_write_dec_as_text(sk_wstream_t* cstream, int32_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_dec_as_text (sk_wstream_t cstream, Int32 value);
+
+               // bool sk_wstream_write_hex_as_text(sk_wstream_t* cstream, uint32_t value, int minDigits)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_hex_as_text (sk_wstream_t cstream, UInt32 value, Int32 minDigits);
+
+               // bool sk_wstream_write_packed_uint(sk_wstream_t* cstream, size_t value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_packed_uint (sk_wstream_t cstream, /* size_t */ IntPtr value);
+
+               // bool sk_wstream_write_scalar(sk_wstream_t* cstream, float value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_scalar (sk_wstream_t cstream, Single value);
+
+               // bool sk_wstream_write_scalar_as_text(sk_wstream_t* cstream, float value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_scalar_as_text (sk_wstream_t cstream, Single value);
+
+               // bool sk_wstream_write_stream(sk_wstream_t* cstream, sk_stream_t* input, size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_stream (sk_wstream_t cstream, sk_stream_t input, /* size_t */ IntPtr length);
+
+               // bool sk_wstream_write_text(sk_wstream_t* cstream, const char* value)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_wstream_write_text (sk_wstream_t cstream, [MarshalAs (UnmanagedType.LPStr)] String value);
+
+               #endregion
+
+               #region sk_string.h
+
+               // void sk_string_destructor(const sk_string_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_string_destructor (sk_string_t param0);
+
+               // const char* sk_string_get_c_str(const sk_string_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* char */ void* sk_string_get_c_str (sk_string_t param0);
+
+               // size_t sk_string_get_size(const sk_string_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_string_get_size (sk_string_t param0);
+
+               // sk_string_t* sk_string_new_empty()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_string_t sk_string_new_empty ();
+
+               // sk_string_t* sk_string_new_with_copy(const char* src, size_t length)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_string_t sk_string_new_with_copy (/* char */ void* src, /* size_t */ IntPtr length);
+
+               #endregion
+
+               #region sk_surface.h
+
+               // void sk_surface_draw(sk_surface_t* surface, sk_canvas_t* canvas, float x, float y, const sk_paint_t* paint)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_surface_draw (sk_surface_t surface, sk_canvas_t canvas, Single x, Single y, sk_paint_t paint);
+
+               // sk_canvas_t* sk_surface_get_canvas(sk_surface_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_surface_get_canvas (sk_surface_t param0);
+
+               // const sk_surfaceprops_t* sk_surface_get_props(sk_surface_t* surface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surfaceprops_t sk_surface_get_props (sk_surface_t surface);
+
+               // sk_surface_t* sk_surface_new_backend_render_target(gr_context_t* context, const gr_backendrendertarget_t* target, gr_surfaceorigin_t origin, sk_colortype_t colorType, sk_colorspace_t* colorspace, const sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_backend_render_target (gr_context_t context, gr_backendrendertarget_t target, GRSurfaceOrigin origin, SKColorType colorType, sk_colorspace_t colorspace, sk_surfaceprops_t props);
+
+               // sk_surface_t* sk_surface_new_backend_texture(gr_context_t* context, const gr_backendtexture_t* texture, gr_surfaceorigin_t origin, int samples, sk_colortype_t colorType, sk_colorspace_t* colorspace, const sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_backend_texture (gr_context_t context, gr_backendtexture_t texture, GRSurfaceOrigin origin, Int32 samples, SKColorType colorType, sk_colorspace_t colorspace, sk_surfaceprops_t props);
+
+               // sk_surface_t* sk_surface_new_backend_texture_as_render_target(gr_context_t* context, const gr_backendtexture_t* texture, gr_surfaceorigin_t origin, int samples, sk_colortype_t colorType, sk_colorspace_t* colorspace, const sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_backend_texture_as_render_target (gr_context_t context, gr_backendtexture_t texture, GRSurfaceOrigin origin, Int32 samples, SKColorType colorType, sk_colorspace_t colorspace, sk_surfaceprops_t props);
+
+               // sk_image_t* sk_surface_new_image_snapshot(sk_surface_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_image_t sk_surface_new_image_snapshot (sk_surface_t param0);
+
+               // sk_surface_t* sk_surface_new_null(int width, int height)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_null (Int32 width, Int32 height);
+
+               // sk_surface_t* sk_surface_new_raster(const sk_imageinfo_t*, size_t rowBytes, const sk_surfaceprops_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_raster (SKImageInfoNative* param0, /* size_t */ IntPtr rowBytes, sk_surfaceprops_t param2);
+
+               // sk_surface_t* sk_surface_new_raster_direct(const sk_imageinfo_t*, void* pixels, size_t rowBytes, const sk_surface_raster_release_proc releaseProc, void* context, const sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_raster_direct (SKImageInfoNative* param0, void* pixels, /* size_t */ IntPtr rowBytes, SKSurfaceRasterReleaseProxyDelegate releaseProc, void* context, sk_surfaceprops_t props);
+
+               // sk_surface_t* sk_surface_new_render_target(gr_context_t* context, bool budgeted, const sk_imageinfo_t* cinfo, int sampleCount, gr_surfaceorigin_t origin, const sk_surfaceprops_t* props, bool shouldCreateWithMips)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surface_t sk_surface_new_render_target (gr_context_t context, [MarshalAs (UnmanagedType.I1)] bool budgeted, SKImageInfoNative* cinfo, Int32 sampleCount, GRSurfaceOrigin origin, sk_surfaceprops_t props, [MarshalAs (UnmanagedType.I1)] bool shouldCreateWithMips);
+
+               // bool sk_surface_peek_pixels(sk_surface_t* surface, sk_pixmap_t* pixmap)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_surface_peek_pixels (sk_surface_t surface, sk_pixmap_t pixmap);
+
+               // bool sk_surface_read_pixels(sk_surface_t* surface, sk_imageinfo_t* dstInfo, void* dstPixels, size_t dstRowBytes, int srcX, int srcY)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_surface_read_pixels (sk_surface_t surface, SKImageInfoNative* dstInfo, void* dstPixels, /* size_t */ IntPtr dstRowBytes, Int32 srcX, Int32 srcY);
+
+               // void sk_surface_unref(sk_surface_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_surface_unref (sk_surface_t param0);
+
+               // void sk_surfaceprops_delete(sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_surfaceprops_delete (sk_surfaceprops_t props);
+
+               // uint32_t sk_surfaceprops_get_flags(sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_surfaceprops_get_flags (sk_surfaceprops_t props);
+
+               // sk_pixelgeometry_t sk_surfaceprops_get_pixel_geometry(sk_surfaceprops_t* props)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKPixelGeometry sk_surfaceprops_get_pixel_geometry (sk_surfaceprops_t props);
+
+               // sk_surfaceprops_t* sk_surfaceprops_new(uint32_t flags, sk_pixelgeometry_t geometry)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_surfaceprops_t sk_surfaceprops_new (UInt32 flags, SKPixelGeometry geometry);
+
+               #endregion
+
+               #region sk_svg.h
+
+               // sk_canvas_t* sk_svgcanvas_create_with_stream(const sk_rect_t* bounds, sk_wstream_t* stream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_svgcanvas_create_with_stream (SKRect* bounds, sk_wstream_t stream);
+
+               // sk_canvas_t* sk_svgcanvas_create_with_writer(const sk_rect_t* bounds, sk_xmlwriter_t* writer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_canvas_t sk_svgcanvas_create_with_writer (SKRect* bounds, sk_xmlwriter_t writer);
+
+               #endregion
+
+               #region sk_textblob.h
+
+               // void sk_textblob_builder_alloc_run_text(sk_textblob_builder_t* builder, const sk_paint_t* font, int count, float x, float y, int textByteCount, const sk_rect_t* bounds, sk_textblob_builder_runbuffer_t* runbuffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_builder_alloc_run_text (sk_textblob_builder_t builder, sk_paint_t font, Int32 count, Single x, Single y, Int32 textByteCount, SKRect* bounds, SKRunBufferInternal* runbuffer);
+
+               // void sk_textblob_builder_alloc_run_text_pos(sk_textblob_builder_t* builder, const sk_paint_t* font, int count, int textByteCount, const sk_rect_t* bounds, sk_textblob_builder_runbuffer_t* runbuffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_builder_alloc_run_text_pos (sk_textblob_builder_t builder, sk_paint_t font, Int32 count, Int32 textByteCount, SKRect* bounds, SKRunBufferInternal* runbuffer);
+
+               // void sk_textblob_builder_alloc_run_text_pos_h(sk_textblob_builder_t* builder, const sk_paint_t* font, int count, float y, int textByteCount, const sk_rect_t* bounds, sk_textblob_builder_runbuffer_t* runbuffer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_builder_alloc_run_text_pos_h (sk_textblob_builder_t builder, sk_paint_t font, Int32 count, Single y, Int32 textByteCount, SKRect* bounds, SKRunBufferInternal* runbuffer);
+
+               // void sk_textblob_builder_delete(sk_textblob_builder_t* builder)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_builder_delete (sk_textblob_builder_t builder);
+
+               // sk_textblob_t* sk_textblob_builder_make(sk_textblob_builder_t* builder)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_textblob_t sk_textblob_builder_make (sk_textblob_builder_t builder);
+
+               // sk_textblob_builder_t* sk_textblob_builder_new()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_textblob_builder_t sk_textblob_builder_new ();
+
+               // void sk_textblob_get_bounds(const sk_textblob_t* blob, sk_rect_t* bounds)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_get_bounds (sk_textblob_t blob, SKRect* bounds);
+
+               // uint32_t sk_textblob_get_unique_id(const sk_textblob_t* blob)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern UInt32 sk_textblob_get_unique_id (sk_textblob_t blob);
+
+               // void sk_textblob_ref(const sk_textblob_t* blob)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_ref (sk_textblob_t blob);
+
+               // void sk_textblob_unref(const sk_textblob_t* blob)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_textblob_unref (sk_textblob_t blob);
+
+               #endregion
+
+               #region sk_typeface.h
+
+               // int sk_fontmgr_count_families(sk_fontmgr_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_fontmgr_count_families (sk_fontmgr_t param0);
+
+               // sk_fontmgr_t* sk_fontmgr_create_default()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontmgr_t sk_fontmgr_create_default ();
+
+               // sk_typeface_t* sk_fontmgr_create_from_data(sk_fontmgr_t*, sk_data_t* data, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_create_from_data (sk_fontmgr_t param0, sk_data_t data, Int32 index);
+
+               // sk_typeface_t* sk_fontmgr_create_from_file(sk_fontmgr_t*, const char* path, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_create_from_file (sk_fontmgr_t param0, /* char */ void* path, Int32 index);
+
+               // sk_typeface_t* sk_fontmgr_create_from_stream(sk_fontmgr_t*, sk_stream_asset_t* stream, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_create_from_stream (sk_fontmgr_t param0, sk_stream_asset_t stream, Int32 index);
+
+               // sk_fontstyleset_t* sk_fontmgr_create_styleset(sk_fontmgr_t*, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontstyleset_t sk_fontmgr_create_styleset (sk_fontmgr_t param0, Int32 index);
+
+               // void sk_fontmgr_get_family_name(sk_fontmgr_t*, int index, sk_string_t* familyName)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_fontmgr_get_family_name (sk_fontmgr_t param0, Int32 index, sk_string_t familyName);
+
+               // sk_typeface_t* sk_fontmgr_match_face_style(sk_fontmgr_t*, const sk_typeface_t* face, sk_fontstyle_t* style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_match_face_style (sk_fontmgr_t param0, sk_typeface_t face, sk_fontstyle_t style);
+
+               // sk_fontstyleset_t* sk_fontmgr_match_family(sk_fontmgr_t*, const char* familyName)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontstyleset_t sk_fontmgr_match_family (sk_fontmgr_t param0, [MarshalAs (UnmanagedType.LPStr)] String familyName);
+
+               // sk_typeface_t* sk_fontmgr_match_family_style(sk_fontmgr_t*, const char* familyName, sk_fontstyle_t* style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_match_family_style (sk_fontmgr_t param0, [MarshalAs (UnmanagedType.LPStr)] String familyName, sk_fontstyle_t style);
+
+               // sk_typeface_t* sk_fontmgr_match_family_style_character(sk_fontmgr_t*, const char* familyName, sk_fontstyle_t* style, const char** bcp47, int bcp47Count, int32_t character)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontmgr_match_family_style_character (sk_fontmgr_t param0, [MarshalAs (UnmanagedType.LPStr)] String familyName, sk_fontstyle_t style, [MarshalAs (UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] String[] bcp47, Int32 bcp47Count, Int32 character);
+
+               // sk_fontmgr_t* sk_fontmgr_ref_default()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontmgr_t sk_fontmgr_ref_default ();
+
+               // void sk_fontmgr_unref(sk_fontmgr_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_fontmgr_unref (sk_fontmgr_t param0);
+
+               // void sk_fontstyle_delete(sk_fontstyle_t* fs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_fontstyle_delete (sk_fontstyle_t fs);
+
+               // sk_font_style_slant_t sk_fontstyle_get_slant(const sk_fontstyle_t* fs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKFontStyleSlant sk_fontstyle_get_slant (sk_fontstyle_t fs);
+
+               // int sk_fontstyle_get_weight(const sk_fontstyle_t* fs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_fontstyle_get_weight (sk_fontstyle_t fs);
+
+               // int sk_fontstyle_get_width(const sk_fontstyle_t* fs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_fontstyle_get_width (sk_fontstyle_t fs);
+
+               // sk_fontstyle_t* sk_fontstyle_new(int weight, int width, sk_font_style_slant_t slant)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontstyle_t sk_fontstyle_new (Int32 weight, Int32 width, SKFontStyleSlant slant);
+
+               // sk_fontstyleset_t* sk_fontstyleset_create_empty()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontstyleset_t sk_fontstyleset_create_empty ();
+
+               // sk_typeface_t* sk_fontstyleset_create_typeface(sk_fontstyleset_t* fss, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontstyleset_create_typeface (sk_fontstyleset_t fss, Int32 index);
+
+               // int sk_fontstyleset_get_count(sk_fontstyleset_t* fss)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_fontstyleset_get_count (sk_fontstyleset_t fss);
+
+               // void sk_fontstyleset_get_style(sk_fontstyleset_t* fss, int index, sk_fontstyle_t* fs, sk_string_t* style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_fontstyleset_get_style (sk_fontstyleset_t fss, Int32 index, sk_fontstyle_t fs, sk_string_t style);
+
+               // sk_typeface_t* sk_fontstyleset_match_style(sk_fontstyleset_t* fss, sk_fontstyle_t* style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_fontstyleset_match_style (sk_fontstyleset_t fss, sk_fontstyle_t style);
+
+               // void sk_fontstyleset_unref(sk_fontstyleset_t* fss)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_fontstyleset_unref (sk_fontstyleset_t fss);
+
+               // int sk_typeface_chars_to_glyphs(sk_typeface_t* typeface, const char* chars, sk_encoding_t encoding, uint16_t[-1] glyphs, int glyphCount)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_chars_to_glyphs (sk_typeface_t typeface, /* char */ void* chars, SKEncoding encoding, UInt16* glyphs, Int32 glyphCount);
+
+               // int sk_typeface_count_glyphs(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_count_glyphs (sk_typeface_t typeface);
+
+               // int sk_typeface_count_tables(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_count_tables (sk_typeface_t typeface);
+
+               // sk_typeface_t* sk_typeface_create_default()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_typeface_create_default ();
+
+               // sk_typeface_t* sk_typeface_create_from_file(const char* path, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_typeface_create_from_file (/* char */ void* path, Int32 index);
+
+               // sk_typeface_t* sk_typeface_create_from_name_with_font_style(const char* familyName, sk_fontstyle_t* style)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_typeface_create_from_name_with_font_style ([MarshalAs (UnmanagedType.LPStr)] String familyName, sk_fontstyle_t style);
+
+               // sk_typeface_t* sk_typeface_create_from_stream(sk_stream_asset_t* stream, int index)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_typeface_create_from_stream (sk_stream_asset_t stream, Int32 index);
+
+               // sk_string_t* sk_typeface_get_family_name(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_string_t sk_typeface_get_family_name (sk_typeface_t typeface);
+
+               // sk_font_style_slant_t sk_typeface_get_font_slant(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern SKFontStyleSlant sk_typeface_get_font_slant (sk_typeface_t typeface);
+
+               // int sk_typeface_get_font_weight(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_get_font_weight (sk_typeface_t typeface);
+
+               // int sk_typeface_get_font_width(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_get_font_width (sk_typeface_t typeface);
+
+               // sk_fontstyle_t* sk_typeface_get_fontstyle(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_fontstyle_t sk_typeface_get_fontstyle (sk_typeface_t typeface);
+
+               // bool sk_typeface_get_kerning_pair_adjustments(sk_typeface_t* typeface, const uint16_t* glyphs, int count, int32_t* adjustments)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_typeface_get_kerning_pair_adjustments (sk_typeface_t typeface, UInt16* glyphs, Int32 count, Int32* adjustments);
+
+               // size_t sk_typeface_get_table_data(sk_typeface_t* typeface, sk_font_table_tag_t tag, size_t offset, size_t length, void* data)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_typeface_get_table_data (sk_typeface_t typeface, UInt32 tag, /* size_t */ IntPtr offset, /* size_t */ IntPtr length, void* data);
+
+               // size_t sk_typeface_get_table_size(sk_typeface_t* typeface, sk_font_table_tag_t tag)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern /* size_t */ IntPtr sk_typeface_get_table_size (sk_typeface_t typeface, UInt32 tag);
+
+               // int sk_typeface_get_table_tags(sk_typeface_t* typeface, sk_font_table_tag_t[-1] tags)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_get_table_tags (sk_typeface_t typeface, UInt32* tags);
+
+               // int sk_typeface_get_units_per_em(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern Int32 sk_typeface_get_units_per_em (sk_typeface_t typeface);
+
+               // bool sk_typeface_is_fixed_pitch(sk_typeface_t* typeface)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               [return: MarshalAs (UnmanagedType.I1)]
+               internal static extern bool sk_typeface_is_fixed_pitch (sk_typeface_t typeface);
+
+               // sk_stream_asset_t* sk_typeface_open_stream(sk_typeface_t* typeface, int* ttcIndex)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_asset_t sk_typeface_open_stream (sk_typeface_t typeface, Int32* ttcIndex);
+
+               // sk_typeface_t* sk_typeface_ref_default()
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_typeface_t sk_typeface_ref_default ();
+
+               // void sk_typeface_unref(sk_typeface_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_typeface_unref (sk_typeface_t param0);
+
+               #endregion
+
+               #region sk_vertices.h
+
+               // sk_vertices_t* sk_vertices_make_copy(sk_vertices_vertex_mode_t vmode, int vertexCount, const sk_point_t* positions, const sk_point_t* texs, const sk_color_t* colors, int indexCount, const uint16_t* indices)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_vertices_t sk_vertices_make_copy (SKVertexMode vmode, Int32 vertexCount, SKPoint* positions, SKPoint* texs, UInt32* colors, Int32 indexCount, UInt16* indices);
+
+               // void sk_vertices_ref(sk_vertices_t* cvertices)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_vertices_ref (sk_vertices_t cvertices);
+
+               // void sk_vertices_unref(sk_vertices_t* cvertices)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_vertices_unref (sk_vertices_t cvertices);
+
+               #endregion
+
+               #region sk_xml.h
+
+               // void sk_xmlstreamwriter_delete(sk_xmlstreamwriter_t* writer)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_xmlstreamwriter_delete (sk_xmlstreamwriter_t writer);
+
+               // sk_xmlstreamwriter_t* sk_xmlstreamwriter_new(sk_wstream_t* stream)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_xmlstreamwriter_t sk_xmlstreamwriter_new (sk_wstream_t stream);
+
+               #endregion
+
+               #region sk_manageddrawable.h
+
+               // sk_manageddrawable_t* sk_manageddrawable_new(void* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_manageddrawable_t sk_manageddrawable_new (void* context);
+
+               // void sk_manageddrawable_set_procs(sk_manageddrawable_procs_t procs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_manageddrawable_set_procs (SKManagedDrawableDelegates procs);
+
+               // void sk_manageddrawable_unref(sk_manageddrawable_t*)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_manageddrawable_unref (sk_manageddrawable_t param0);
+
+               #endregion
+
+               #region sk_managedstream.h
+
+               // void sk_managedstream_destroy(sk_stream_managedstream_t* s)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_managedstream_destroy (sk_stream_managedstream_t s);
+
+               // sk_stream_managedstream_t* sk_managedstream_new(void* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_stream_managedstream_t sk_managedstream_new (void* context);
+
+               // void sk_managedstream_set_procs(sk_managedstream_procs_t procs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_managedstream_set_procs (SKManagedStreamDelegates procs);
+
+               // void sk_managedwstream_destroy(sk_wstream_managedstream_t* s)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_managedwstream_destroy (sk_wstream_managedstream_t s);
+
+               // sk_wstream_managedstream_t* sk_managedwstream_new(void* context)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern sk_wstream_managedstream_t sk_managedwstream_new (void* context);
+
+               // void sk_managedwstream_set_procs(sk_managedwstream_procs_t procs)
+               [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
+               internal static extern void sk_managedwstream_set_procs (SKManagedWStreamDelegates procs);
+
+               #endregion
+
+       }
+
+       #region Delegates
+
+       // typedef void (*)()* gr_gl_func_ptr
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void GRGlFuncPtr();
+
+       // typedef gr_gl_func_ptr (*)(void* ctx, const char* name)* gr_gl_get_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate IntPtr GRGlGetProcProxyDelegate(void* ctx, [MarshalAs (UnmanagedType.LPStr)] String name);
+
+       // typedef void (*)(void* addr, void* context)* sk_bitmap_release_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKBitmapReleaseProxyDelegate(void* addr, void* context);
+
+       // typedef void (*)(const void* ptr, void* context)* sk_data_release_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKDataReleaseProxyDelegate(void* ptr, void* context);
+
+       // typedef void (*)(const void* addr, void* context)* sk_image_raster_release_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKImageRasterReleaseProxyDelegate(void* addr, void* context);
+
+       // typedef void (*)(void* context)* sk_image_texture_release_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKImageTextureReleaseProxyDelegate(void* context);
+
+       // typedef void (*)(sk_manageddrawable_t* d, void* context)* sk_manageddrawable_destroy_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedDrawableDestroyProxyDelegate(sk_manageddrawable_t d, void* context);
+
+       // typedef void (*)(sk_manageddrawable_t* d, void* context, sk_canvas_t* ccanvas)* sk_manageddrawable_draw_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedDrawableDrawProxyDelegate(sk_manageddrawable_t d, void* context, sk_canvas_t ccanvas);
+
+       // typedef void (*)(sk_manageddrawable_t* d, void* context, sk_rect_t* rect)* sk_manageddrawable_getBounds_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedDrawableGetBoundsProxyDelegate(sk_manageddrawable_t d, void* context, SKRect* rect);
+
+       // typedef sk_picture_t* (*)(sk_manageddrawable_t* d, void* context)* sk_manageddrawable_newPictureSnapshot_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate sk_picture_t SKManagedDrawableNewPictureSnapshotProxyDelegate(sk_manageddrawable_t d, void* context);
+
+       // typedef void (*)(sk_stream_managedstream_t* s, void* context)* sk_managedstream_destroy_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedStreamDestroyProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef sk_stream_managedstream_t* (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_duplicate_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate sk_stream_managedstream_t SKManagedStreamDuplicateProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef sk_stream_managedstream_t* (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_fork_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate sk_stream_managedstream_t SKManagedStreamForkProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef size_t (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_getLength_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate /* size_t */ IntPtr SKManagedStreamGetLengthProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef size_t (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_getPosition_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate /* size_t */ IntPtr SKManagedStreamGetPositionProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef bool (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_hasLength_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamHasLengthProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef bool (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_hasPosition_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamHasPositionProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef bool (*)(const sk_stream_managedstream_t* s, void* context)* sk_managedstream_isAtEnd_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamIsAtEndProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef bool (*)(sk_stream_managedstream_t* s, void* context, int offset)* sk_managedstream_move_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamMoveProxyDelegate(sk_stream_managedstream_t s, void* context, Int32 offset);
+
+       // typedef size_t (*)(const sk_stream_managedstream_t* s, void* context, void* buffer, size_t size)* sk_managedstream_peek_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate /* size_t */ IntPtr SKManagedStreamPeekProxyDelegate(sk_stream_managedstream_t s, void* context, void* buffer, /* size_t */ IntPtr size);
+
+       // typedef size_t (*)(sk_stream_managedstream_t* s, void* context, void* buffer, size_t size)* sk_managedstream_read_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate /* size_t */ IntPtr SKManagedStreamReadProxyDelegate(sk_stream_managedstream_t s, void* context, void* buffer, /* size_t */ IntPtr size);
+
+       // typedef bool (*)(sk_stream_managedstream_t* s, void* context)* sk_managedstream_rewind_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamRewindProxyDelegate(sk_stream_managedstream_t s, void* context);
+
+       // typedef bool (*)(sk_stream_managedstream_t* s, void* context, size_t position)* sk_managedstream_seek_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedStreamSeekProxyDelegate(sk_stream_managedstream_t s, void* context, /* size_t */ IntPtr position);
+
+       // typedef size_t (*)(const sk_wstream_managedstream_t* s, void* context)* sk_managedwstream_bytesWritten_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate /* size_t */ IntPtr SKManagedWStreamBytesWrittenProxyDelegate(sk_wstream_managedstream_t s, void* context);
+
+       // typedef void (*)(sk_wstream_managedstream_t* s, void* context)* sk_managedwstream_destroy_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedWStreamDestroyProxyDelegate(sk_wstream_managedstream_t s, void* context);
+
+       // typedef void (*)(sk_wstream_managedstream_t* s, void* context)* sk_managedwstream_flush_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKManagedWStreamFlushProxyDelegate(sk_wstream_managedstream_t s, void* context);
+
+       // typedef bool (*)(sk_wstream_managedstream_t* s, void* context, const void* buffer, size_t size)* sk_managedwstream_write_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       [return: MarshalAs (UnmanagedType.I1)]
+       internal unsafe delegate bool SKManagedWStreamWriteProxyDelegate(sk_wstream_managedstream_t s, void* context, void* buffer, /* size_t */ IntPtr size);
+
+       // typedef void (*)(void* addr, void* context)* sk_surface_raster_release_proc
+       [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+       internal unsafe delegate void SKSurfaceRasterReleaseProxyDelegate(void* addr, void* context);
+
+       #endregion
+
+       #region Structs
+
+       // gr_gl_framebufferinfo_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct GRGlFramebufferInfo : IEquatable<GRGlFramebufferInfo> {
+               // public unsigned int fFBOID
+               private UInt32 fFBOID;
+               public UInt32 FramebufferObjectId {
+                       readonly get => fFBOID;
+                       set => fFBOID = value;
+               }
+
+               // public unsigned int fFormat
+               private UInt32 fFormat;
+               public UInt32 Format {
+                       readonly get => fFormat;
+                       set => fFormat = value;
+               }
+
+               public readonly bool Equals (GRGlFramebufferInfo obj) =>
+                       fFBOID == obj.fFBOID && fFormat == obj.fFormat;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is GRGlFramebufferInfo f && Equals (f);
+
+               public static bool operator == (GRGlFramebufferInfo left, GRGlFramebufferInfo right) =>
+                       left.Equals (right);
+
+               public static bool operator != (GRGlFramebufferInfo left, GRGlFramebufferInfo right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fFBOID);
+                       hash.Add (fFormat);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // gr_gl_textureinfo_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct GRGlTextureInfo : IEquatable<GRGlTextureInfo> {
+               // public unsigned int fTarget
+               private UInt32 fTarget;
+               public UInt32 Target {
+                       readonly get => fTarget;
+                       set => fTarget = value;
+               }
+
+               // public unsigned int fID
+               private UInt32 fID;
+               public UInt32 Id {
+                       readonly get => fID;
+                       set => fID = value;
+               }
+
+               // public unsigned int fFormat
+               private UInt32 fFormat;
+               public UInt32 Format {
+                       readonly get => fFormat;
+                       set => fFormat = value;
+               }
+
+               public readonly bool Equals (GRGlTextureInfo obj) =>
+                       fTarget == obj.fTarget && fID == obj.fID && fFormat == obj.fFormat;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is GRGlTextureInfo f && Equals (f);
+
+               public static bool operator == (GRGlTextureInfo left, GRGlTextureInfo right) =>
+                       left.Equals (right);
+
+               public static bool operator != (GRGlTextureInfo left, GRGlTextureInfo right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fTarget);
+                       hash.Add (fID);
+                       hash.Add (fFormat);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_codec_frameinfo_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKCodecFrameInfo : IEquatable<SKCodecFrameInfo> {
+               // public int fRequiredFrame
+               private Int32 fRequiredFrame;
+               public Int32 RequiredFrame {
+                       readonly get => fRequiredFrame;
+                       set => fRequiredFrame = value;
+               }
+
+               // public int fDuration
+               private Int32 fDuration;
+               public Int32 Duration {
+                       readonly get => fDuration;
+                       set => fDuration = value;
+               }
+
+               // public bool fFullyReceived
+               private Byte fFullyReceived;
+               public bool FullyRecieved {
+                       readonly get => fFullyReceived > 0;
+                       set => fFullyReceived = value ? (byte)1 : (byte)0;
+               }
+
+               // public sk_alphatype_t fAlphaType
+               private SKAlphaType fAlphaType;
+               public SKAlphaType AlphaType {
+                       readonly get => fAlphaType;
+                       set => fAlphaType = value;
+               }
+
+               // public sk_codecanimation_disposalmethod_t fDisposalMethod
+               private SKCodecAnimationDisposalMethod fDisposalMethod;
+               public SKCodecAnimationDisposalMethod DisposalMethod {
+                       readonly get => fDisposalMethod;
+                       set => fDisposalMethod = value;
+               }
+
+               public readonly bool Equals (SKCodecFrameInfo obj) =>
+                       fRequiredFrame == obj.fRequiredFrame && fDuration == obj.fDuration && fFullyReceived == obj.fFullyReceived && fAlphaType == obj.fAlphaType && fDisposalMethod == obj.fDisposalMethod;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKCodecFrameInfo f && Equals (f);
+
+               public static bool operator == (SKCodecFrameInfo left, SKCodecFrameInfo right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKCodecFrameInfo left, SKCodecFrameInfo right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fRequiredFrame);
+                       hash.Add (fDuration);
+                       hash.Add (fFullyReceived);
+                       hash.Add (fAlphaType);
+                       hash.Add (fDisposalMethod);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_codec_options_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKCodecOptionsInternal : IEquatable<SKCodecOptionsInternal> {
+               // public sk_codec_zero_initialized_t fZeroInitialized
+               public SKZeroInitialized fZeroInitialized;
+
+               // public sk_irect_t* fSubset
+               public SKRectI* fSubset;
+
+               // public int fFrameIndex
+               public Int32 fFrameIndex;
+
+               // public int fPriorFrame
+               public Int32 fPriorFrame;
+
+               // public sk_transfer_function_behavior_t fPremulBehavior
+               public SKTransferFunctionBehavior fPremulBehavior;
+
+               public readonly bool Equals (SKCodecOptionsInternal obj) =>
+                       fZeroInitialized == obj.fZeroInitialized && fSubset == obj.fSubset && fFrameIndex == obj.fFrameIndex && fPriorFrame == obj.fPriorFrame && fPremulBehavior == obj.fPremulBehavior;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKCodecOptionsInternal f && Equals (f);
+
+               public static bool operator == (SKCodecOptionsInternal left, SKCodecOptionsInternal right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKCodecOptionsInternal left, SKCodecOptionsInternal right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fZeroInitialized);
+                       hash.Add (fSubset);
+                       hash.Add (fFrameIndex);
+                       hash.Add (fPriorFrame);
+                       hash.Add (fPremulBehavior);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_color4f_t
+       [StructLayout (LayoutKind.Sequential)]
+       public readonly unsafe partial struct SKColorF : IEquatable<SKColorF> {
+               // public float fR
+               private readonly Single fR;
+               public readonly Single Red => fR;
+
+               // public float fG
+               private readonly Single fG;
+               public readonly Single Green => fG;
+
+               // public float fB
+               private readonly Single fB;
+               public readonly Single Blue => fB;
+
+               // public float fA
+               private readonly Single fA;
+               public readonly Single Alpha => fA;
+
+               public readonly bool Equals (SKColorF obj) =>
+                       fR == obj.fR && fG == obj.fG && fB == obj.fB && fA == obj.fA;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKColorF f && Equals (f);
+
+               public static bool operator == (SKColorF left, SKColorF right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKColorF left, SKColorF right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fR);
+                       hash.Add (fG);
+                       hash.Add (fB);
+                       hash.Add (fA);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_colorspace_transfer_fn_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKColorSpaceTransferFn : IEquatable<SKColorSpaceTransferFn> {
+               // public float fG
+               private Single fG;
+               public Single G {
+                       readonly get => fG;
+                       set => fG = value;
+               }
+
+               // public float fA
+               private Single fA;
+               public Single A {
+                       readonly get => fA;
+                       set => fA = value;
+               }
+
+               // public float fB
+               private Single fB;
+               public Single B {
+                       readonly get => fB;
+                       set => fB = value;
+               }
+
+               // public float fC
+               private Single fC;
+               public Single C {
+                       readonly get => fC;
+                       set => fC = value;
+               }
+
+               // public float fD
+               private Single fD;
+               public Single D {
+                       readonly get => fD;
+                       set => fD = value;
+               }
+
+               // public float fE
+               private Single fE;
+               public Single E {
+                       readonly get => fE;
+                       set => fE = value;
+               }
+
+               // public float fF
+               private Single fF;
+               public Single F {
+                       readonly get => fF;
+                       set => fF = value;
+               }
+
+               public readonly bool Equals (SKColorSpaceTransferFn obj) =>
+                       fG == obj.fG && fA == obj.fA && fB == obj.fB && fC == obj.fC && fD == obj.fD && fE == obj.fE && fF == obj.fF;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKColorSpaceTransferFn f && Equals (f);
+
+               public static bool operator == (SKColorSpaceTransferFn left, SKColorSpaceTransferFn right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKColorSpaceTransferFn left, SKColorSpaceTransferFn right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fG);
+                       hash.Add (fA);
+                       hash.Add (fB);
+                       hash.Add (fC);
+                       hash.Add (fD);
+                       hash.Add (fE);
+                       hash.Add (fF);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_colorspaceprimaries_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKColorSpacePrimaries : IEquatable<SKColorSpacePrimaries> {
+               // public float fRX
+               private Single fRX;
+               public Single RX {
+                       readonly get => fRX;
+                       set => fRX = value;
+               }
+
+               // public float fRY
+               private Single fRY;
+               public Single RY {
+                       readonly get => fRY;
+                       set => fRY = value;
+               }
+
+               // public float fGX
+               private Single fGX;
+               public Single GX {
+                       readonly get => fGX;
+                       set => fGX = value;
+               }
+
+               // public float fGY
+               private Single fGY;
+               public Single GY {
+                       readonly get => fGY;
+                       set => fGY = value;
+               }
+
+               // public float fBX
+               private Single fBX;
+               public Single BX {
+                       readonly get => fBX;
+                       set => fBX = value;
+               }
+
+               // public float fBY
+               private Single fBY;
+               public Single BY {
+                       readonly get => fBY;
+                       set => fBY = value;
+               }
+
+               // public float fWX
+               private Single fWX;
+               public Single WX {
+                       readonly get => fWX;
+                       set => fWX = value;
+               }
+
+               // public float fWY
+               private Single fWY;
+               public Single WY {
+                       readonly get => fWY;
+                       set => fWY = value;
+               }
+
+               public readonly bool Equals (SKColorSpacePrimaries obj) =>
+                       fRX == obj.fRX && fRY == obj.fRY && fGX == obj.fGX && fGY == obj.fGY && fBX == obj.fBX && fBY == obj.fBY && fWX == obj.fWX && fWY == obj.fWY;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKColorSpacePrimaries f && Equals (f);
+
+               public static bool operator == (SKColorSpacePrimaries left, SKColorSpacePrimaries right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKColorSpacePrimaries left, SKColorSpacePrimaries right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fRX);
+                       hash.Add (fRY);
+                       hash.Add (fGX);
+                       hash.Add (fGY);
+                       hash.Add (fBX);
+                       hash.Add (fBY);
+                       hash.Add (fWX);
+                       hash.Add (fWY);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_document_pdf_metadata_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKDocumentPdfMetadataInternal : IEquatable<SKDocumentPdfMetadataInternal> {
+               // public sk_string_t* fTitle
+               public sk_string_t fTitle;
+
+               // public sk_string_t* fAuthor
+               public sk_string_t fAuthor;
+
+               // public sk_string_t* fSubject
+               public sk_string_t fSubject;
+
+               // public sk_string_t* fKeywords
+               public sk_string_t fKeywords;
+
+               // public sk_string_t* fCreator
+               public sk_string_t fCreator;
+
+               // public sk_string_t* fProducer
+               public sk_string_t fProducer;
+
+               // public sk_time_datetime_t* fCreation
+               public SKTimeDateTimeInternal* fCreation;
+
+               // public sk_time_datetime_t* fModified
+               public SKTimeDateTimeInternal* fModified;
+
+               // public float fRasterDPI
+               public Single fRasterDPI;
+
+               // public bool fPDFA
+               public Byte fPDFA;
+
+               // public int fEncodingQuality
+               public Int32 fEncodingQuality;
+
+               public readonly bool Equals (SKDocumentPdfMetadataInternal obj) =>
+                       fTitle == obj.fTitle && fAuthor == obj.fAuthor && fSubject == obj.fSubject && fKeywords == obj.fKeywords && fCreator == obj.fCreator && fProducer == obj.fProducer && fCreation == obj.fCreation && fModified == obj.fModified && fRasterDPI == obj.fRasterDPI && fPDFA == obj.fPDFA && fEncodingQuality == obj.fEncodingQuality;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKDocumentPdfMetadataInternal f && Equals (f);
+
+               public static bool operator == (SKDocumentPdfMetadataInternal left, SKDocumentPdfMetadataInternal right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKDocumentPdfMetadataInternal left, SKDocumentPdfMetadataInternal right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fTitle);
+                       hash.Add (fAuthor);
+                       hash.Add (fSubject);
+                       hash.Add (fKeywords);
+                       hash.Add (fCreator);
+                       hash.Add (fProducer);
+                       hash.Add (fCreation);
+                       hash.Add (fModified);
+                       hash.Add (fRasterDPI);
+                       hash.Add (fPDFA);
+                       hash.Add (fEncodingQuality);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_fontmetrics_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKFontMetrics : IEquatable<SKFontMetrics> {
+               // public uint32_t fFlags
+               private UInt32 fFlags;
+
+               // public float fTop
+               private Single fTop;
+
+               // public float fAscent
+               private Single fAscent;
+
+               // public float fDescent
+               private Single fDescent;
+
+               // public float fBottom
+               private Single fBottom;
+
+               // public float fLeading
+               private Single fLeading;
+
+               // public float fAvgCharWidth
+               private Single fAvgCharWidth;
+
+               // public float fMaxCharWidth
+               private Single fMaxCharWidth;
+
+               // public float fXMin
+               private Single fXMin;
+
+               // public float fXMax
+               private Single fXMax;
+
+               // public float fXHeight
+               private Single fXHeight;
+
+               // public float fCapHeight
+               private Single fCapHeight;
+
+               // public float fUnderlineThickness
+               private Single fUnderlineThickness;
+
+               // public float fUnderlinePosition
+               private Single fUnderlinePosition;
+
+               // public float fStrikeoutThickness
+               private Single fStrikeoutThickness;
+
+               // public float fStrikeoutPosition
+               private Single fStrikeoutPosition;
+
+               public readonly bool Equals (SKFontMetrics obj) =>
+                       fFlags == obj.fFlags && fTop == obj.fTop && fAscent == obj.fAscent && fDescent == obj.fDescent && fBottom == obj.fBottom && fLeading == obj.fLeading && fAvgCharWidth == obj.fAvgCharWidth && fMaxCharWidth == obj.fMaxCharWidth && fXMin == obj.fXMin && fXMax == obj.fXMax && fXHeight == obj.fXHeight && fCapHeight == obj.fCapHeight && fUnderlineThickness == obj.fUnderlineThickness && fUnderlinePosition == obj.fUnderlinePosition && fStrikeoutThickness == obj.fStrikeoutThickness && fStrikeoutPosition == obj.fStrikeoutPosition;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKFontMetrics f && Equals (f);
+
+               public static bool operator == (SKFontMetrics left, SKFontMetrics right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKFontMetrics left, SKFontMetrics right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fFlags);
+                       hash.Add (fTop);
+                       hash.Add (fAscent);
+                       hash.Add (fDescent);
+                       hash.Add (fBottom);
+                       hash.Add (fLeading);
+                       hash.Add (fAvgCharWidth);
+                       hash.Add (fMaxCharWidth);
+                       hash.Add (fXMin);
+                       hash.Add (fXMax);
+                       hash.Add (fXHeight);
+                       hash.Add (fCapHeight);
+                       hash.Add (fUnderlineThickness);
+                       hash.Add (fUnderlinePosition);
+                       hash.Add (fStrikeoutThickness);
+                       hash.Add (fStrikeoutPosition);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_highcontrastconfig_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKHighContrastConfig : IEquatable<SKHighContrastConfig> {
+               // public bool fGrayscale
+               private Byte fGrayscale;
+               public bool Grayscale {
+                       readonly get => fGrayscale > 0;
+                       set => fGrayscale = value ? (byte)1 : (byte)0;
+               }
+
+               // public sk_highcontrastconfig_invertstyle_t fInvertStyle
+               private SKHighContrastConfigInvertStyle fInvertStyle;
+               public SKHighContrastConfigInvertStyle InvertStyle {
+                       readonly get => fInvertStyle;
+                       set => fInvertStyle = value;
+               }
+
+               // public float fContrast
+               private Single fContrast;
+               public Single Contrast {
+                       readonly get => fContrast;
+                       set => fContrast = value;
+               }
+
+               public readonly bool Equals (SKHighContrastConfig obj) =>
+                       fGrayscale == obj.fGrayscale && fInvertStyle == obj.fInvertStyle && fContrast == obj.fContrast;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKHighContrastConfig f && Equals (f);
+
+               public static bool operator == (SKHighContrastConfig left, SKHighContrastConfig right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKHighContrastConfig left, SKHighContrastConfig right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fGrayscale);
+                       hash.Add (fInvertStyle);
+                       hash.Add (fContrast);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_imageinfo_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKImageInfoNative : IEquatable<SKImageInfoNative> {
+               // public sk_colorspace_t* colorspace
+               public sk_colorspace_t colorspace;
+
+               // public int32_t width
+               public Int32 width;
+
+               // public int32_t height
+               public Int32 height;
+
+               // public sk_colortype_t colorType
+               public SKColorType colorType;
+
+               // public sk_alphatype_t alphaType
+               public SKAlphaType alphaType;
+
+               public readonly bool Equals (SKImageInfoNative obj) =>
+                       colorspace == obj.colorspace && width == obj.width && height == obj.height && colorType == obj.colorType && alphaType == obj.alphaType;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKImageInfoNative f && Equals (f);
+
+               public static bool operator == (SKImageInfoNative left, SKImageInfoNative right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKImageInfoNative left, SKImageInfoNative right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (colorspace);
+                       hash.Add (width);
+                       hash.Add (height);
+                       hash.Add (colorType);
+                       hash.Add (alphaType);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_ipoint_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKPointI : IEquatable<SKPointI> {
+               // public int32_t x
+               private Int32 x;
+               public Int32 X {
+                       readonly get => x;
+                       set => x = value;
+               }
+
+               // public int32_t y
+               private Int32 y;
+               public Int32 Y {
+                       readonly get => y;
+                       set => y = value;
+               }
+
+               public readonly bool Equals (SKPointI obj) =>
+                       x == obj.x && y == obj.y;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKPointI f && Equals (f);
+
+               public static bool operator == (SKPointI left, SKPointI right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKPointI left, SKPointI right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (x);
+                       hash.Add (y);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_irect_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKRectI : IEquatable<SKRectI> {
+               // public int32_t left
+               private Int32 left;
+               public Int32 Left {
+                       readonly get => left;
+                       set => left = value;
+               }
+
+               // public int32_t top
+               private Int32 top;
+               public Int32 Top {
+                       readonly get => top;
+                       set => top = value;
+               }
+
+               // public int32_t right
+               private Int32 right;
+               public Int32 Right {
+                       readonly get => right;
+                       set => right = value;
+               }
+
+               // public int32_t bottom
+               private Int32 bottom;
+               public Int32 Bottom {
+                       readonly get => bottom;
+                       set => bottom = value;
+               }
+
+               public readonly bool Equals (SKRectI obj) =>
+                       left == obj.left && top == obj.top && right == obj.right && bottom == obj.bottom;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKRectI f && Equals (f);
+
+               public static bool operator == (SKRectI left, SKRectI right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKRectI left, SKRectI right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (left);
+                       hash.Add (top);
+                       hash.Add (right);
+                       hash.Add (bottom);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_isize_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKSizeI : IEquatable<SKSizeI> {
+               // public int32_t w
+               private Int32 w;
+               public Int32 Width {
+                       readonly get => w;
+                       set => w = value;
+               }
+
+               // public int32_t h
+               private Int32 h;
+               public Int32 Height {
+                       readonly get => h;
+                       set => h = value;
+               }
+
+               public readonly bool Equals (SKSizeI obj) =>
+                       w == obj.w && h == obj.h;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKSizeI f && Equals (f);
+
+               public static bool operator == (SKSizeI left, SKSizeI right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKSizeI left, SKSizeI right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (w);
+                       hash.Add (h);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_jpegencoder_options_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKJpegEncoderOptions : IEquatable<SKJpegEncoderOptions> {
+               // public int fQuality
+               private Int32 fQuality;
+               public Int32 Quality {
+                       readonly get => fQuality;
+                       set => fQuality = value;
+               }
+
+               // public sk_jpegencoder_downsample_t fDownsample
+               private SKJpegEncoderDownsample fDownsample;
+               public SKJpegEncoderDownsample Downsample {
+                       readonly get => fDownsample;
+                       set => fDownsample = value;
+               }
+
+               // public sk_jpegencoder_alphaoption_t fAlphaOption
+               private SKJpegEncoderAlphaOption fAlphaOption;
+               public SKJpegEncoderAlphaOption AlphaOption {
+                       readonly get => fAlphaOption;
+                       set => fAlphaOption = value;
+               }
+
+               // public sk_transfer_function_behavior_t fBlendBehavior
+               private SKTransferFunctionBehavior fBlendBehavior;
+               public SKTransferFunctionBehavior BlendBehavior {
+                       readonly get => fBlendBehavior;
+                       set => fBlendBehavior = value;
+               }
+
+               public readonly bool Equals (SKJpegEncoderOptions obj) =>
+                       fQuality == obj.fQuality && fDownsample == obj.fDownsample && fAlphaOption == obj.fAlphaOption && fBlendBehavior == obj.fBlendBehavior;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKJpegEncoderOptions f && Equals (f);
+
+               public static bool operator == (SKJpegEncoderOptions left, SKJpegEncoderOptions right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKJpegEncoderOptions left, SKJpegEncoderOptions right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fQuality);
+                       hash.Add (fDownsample);
+                       hash.Add (fAlphaOption);
+                       hash.Add (fBlendBehavior);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_lattice_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKLatticeInternal : IEquatable<SKLatticeInternal> {
+               // public const int* fXDivs
+               public Int32* fXDivs;
+
+               // public const int* fYDivs
+               public Int32* fYDivs;
+
+               // public const sk_lattice_recttype_t* fRectTypes
+               public SKLatticeRectType* fRectTypes;
+
+               // public int fXCount
+               public Int32 fXCount;
+
+               // public int fYCount
+               public Int32 fYCount;
+
+               // public const sk_irect_t* fBounds
+               public SKRectI* fBounds;
+
+               // public const sk_color_t* fColors
+               public UInt32* fColors;
+
+               public readonly bool Equals (SKLatticeInternal obj) =>
+                       fXDivs == obj.fXDivs && fYDivs == obj.fYDivs && fRectTypes == obj.fRectTypes && fXCount == obj.fXCount && fYCount == obj.fYCount && fBounds == obj.fBounds && fColors == obj.fColors;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKLatticeInternal f && Equals (f);
+
+               public static bool operator == (SKLatticeInternal left, SKLatticeInternal right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKLatticeInternal left, SKLatticeInternal right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fXDivs);
+                       hash.Add (fYDivs);
+                       hash.Add (fRectTypes);
+                       hash.Add (fXCount);
+                       hash.Add (fYCount);
+                       hash.Add (fBounds);
+                       hash.Add (fColors);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_manageddrawable_procs_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKManagedDrawableDelegates : IEquatable<SKManagedDrawableDelegates> {
+               // public sk_manageddrawable_draw_proc fDraw
+               public SKManagedDrawableDrawProxyDelegate fDraw;
+
+               // public sk_manageddrawable_getBounds_proc fGetBounds
+               public SKManagedDrawableGetBoundsProxyDelegate fGetBounds;
+
+               // public sk_manageddrawable_newPictureSnapshot_proc fNewPictureSnapshot
+               public SKManagedDrawableNewPictureSnapshotProxyDelegate fNewPictureSnapshot;
+
+               // public sk_manageddrawable_destroy_proc fDestroy
+               public SKManagedDrawableDestroyProxyDelegate fDestroy;
+
+               public readonly bool Equals (SKManagedDrawableDelegates obj) =>
+                       fDraw == obj.fDraw && fGetBounds == obj.fGetBounds && fNewPictureSnapshot == obj.fNewPictureSnapshot && fDestroy == obj.fDestroy;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKManagedDrawableDelegates f && Equals (f);
+
+               public static bool operator == (SKManagedDrawableDelegates left, SKManagedDrawableDelegates right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKManagedDrawableDelegates left, SKManagedDrawableDelegates right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fDraw);
+                       hash.Add (fGetBounds);
+                       hash.Add (fNewPictureSnapshot);
+                       hash.Add (fDestroy);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_managedstream_procs_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKManagedStreamDelegates : IEquatable<SKManagedStreamDelegates> {
+               // public sk_managedstream_read_proc fRead
+               public SKManagedStreamReadProxyDelegate fRead;
+
+               // public sk_managedstream_peek_proc fPeek
+               public SKManagedStreamPeekProxyDelegate fPeek;
+
+               // public sk_managedstream_isAtEnd_proc fIsAtEnd
+               public SKManagedStreamIsAtEndProxyDelegate fIsAtEnd;
+
+               // public sk_managedstream_hasPosition_proc fHasPosition
+               public SKManagedStreamHasPositionProxyDelegate fHasPosition;
+
+               // public sk_managedstream_hasLength_proc fHasLength
+               public SKManagedStreamHasLengthProxyDelegate fHasLength;
+
+               // public sk_managedstream_rewind_proc fRewind
+               public SKManagedStreamRewindProxyDelegate fRewind;
+
+               // public sk_managedstream_getPosition_proc fGetPosition
+               public SKManagedStreamGetPositionProxyDelegate fGetPosition;
+
+               // public sk_managedstream_seek_proc fSeek
+               public SKManagedStreamSeekProxyDelegate fSeek;
+
+               // public sk_managedstream_move_proc fMove
+               public SKManagedStreamMoveProxyDelegate fMove;
+
+               // public sk_managedstream_getLength_proc fGetLength
+               public SKManagedStreamGetLengthProxyDelegate fGetLength;
+
+               // public sk_managedstream_duplicate_proc fDuplicate
+               public SKManagedStreamDuplicateProxyDelegate fDuplicate;
+
+               // public sk_managedstream_fork_proc fFork
+               public SKManagedStreamForkProxyDelegate fFork;
+
+               // public sk_managedstream_destroy_proc fDestroy
+               public SKManagedStreamDestroyProxyDelegate fDestroy;
+
+               public readonly bool Equals (SKManagedStreamDelegates obj) =>
+                       fRead == obj.fRead && fPeek == obj.fPeek && fIsAtEnd == obj.fIsAtEnd && fHasPosition == obj.fHasPosition && fHasLength == obj.fHasLength && fRewind == obj.fRewind && fGetPosition == obj.fGetPosition && fSeek == obj.fSeek && fMove == obj.fMove && fGetLength == obj.fGetLength && fDuplicate == obj.fDuplicate && fFork == obj.fFork && fDestroy == obj.fDestroy;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKManagedStreamDelegates f && Equals (f);
+
+               public static bool operator == (SKManagedStreamDelegates left, SKManagedStreamDelegates right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKManagedStreamDelegates left, SKManagedStreamDelegates right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fRead);
+                       hash.Add (fPeek);
+                       hash.Add (fIsAtEnd);
+                       hash.Add (fHasPosition);
+                       hash.Add (fHasLength);
+                       hash.Add (fRewind);
+                       hash.Add (fGetPosition);
+                       hash.Add (fSeek);
+                       hash.Add (fMove);
+                       hash.Add (fGetLength);
+                       hash.Add (fDuplicate);
+                       hash.Add (fFork);
+                       hash.Add (fDestroy);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_managedwstream_procs_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKManagedWStreamDelegates : IEquatable<SKManagedWStreamDelegates> {
+               // public sk_managedwstream_write_proc fWrite
+               public SKManagedWStreamWriteProxyDelegate fWrite;
+
+               // public sk_managedwstream_flush_proc fFlush
+               public SKManagedWStreamFlushProxyDelegate fFlush;
+
+               // public sk_managedwstream_bytesWritten_proc fBytesWritten
+               public SKManagedWStreamBytesWrittenProxyDelegate fBytesWritten;
+
+               // public sk_managedwstream_destroy_proc fDestroy
+               public SKManagedWStreamDestroyProxyDelegate fDestroy;
+
+               public readonly bool Equals (SKManagedWStreamDelegates obj) =>
+                       fWrite == obj.fWrite && fFlush == obj.fFlush && fBytesWritten == obj.fBytesWritten && fDestroy == obj.fDestroy;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKManagedWStreamDelegates f && Equals (f);
+
+               public static bool operator == (SKManagedWStreamDelegates left, SKManagedWStreamDelegates right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKManagedWStreamDelegates left, SKManagedWStreamDelegates right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fWrite);
+                       hash.Add (fFlush);
+                       hash.Add (fBytesWritten);
+                       hash.Add (fDestroy);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_mask_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKMask : IEquatable<SKMask> {
+               // public uint8_t* fImage
+               private Byte* fImage;
+
+               // public sk_irect_t fBounds
+               private SKRectI fBounds;
+
+               // public uint32_t fRowBytes
+               private UInt32 fRowBytes;
+
+               // public sk_mask_format_t fFormat
+               private SKMaskFormat fFormat;
+
+               public readonly bool Equals (SKMask obj) =>
+                       fImage == obj.fImage && fBounds == obj.fBounds && fRowBytes == obj.fRowBytes && fFormat == obj.fFormat;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKMask f && Equals (f);
+
+               public static bool operator == (SKMask left, SKMask right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKMask left, SKMask right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fImage);
+                       hash.Add (fBounds);
+                       hash.Add (fRowBytes);
+                       hash.Add (fFormat);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_matrix_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKMatrix : IEquatable<SKMatrix> {
+               // public float scaleX
+               private Single scaleX;
+               public Single ScaleX {
+                       readonly get => scaleX;
+                       set => scaleX = value;
+               }
+
+               // public float skewX
+               private Single skewX;
+               public Single SkewX {
+                       readonly get => skewX;
+                       set => skewX = value;
+               }
+
+               // public float transX
+               private Single transX;
+               public Single TransX {
+                       readonly get => transX;
+                       set => transX = value;
+               }
+
+               // public float skewY
+               private Single skewY;
+               public Single SkewY {
+                       readonly get => skewY;
+                       set => skewY = value;
+               }
+
+               // public float scaleY
+               private Single scaleY;
+               public Single ScaleY {
+                       readonly get => scaleY;
+                       set => scaleY = value;
+               }
+
+               // public float transY
+               private Single transY;
+               public Single TransY {
+                       readonly get => transY;
+                       set => transY = value;
+               }
+
+               // public float persp0
+               private Single persp0;
+               public Single Persp0 {
+                       readonly get => persp0;
+                       set => persp0 = value;
+               }
+
+               // public float persp1
+               private Single persp1;
+               public Single Persp1 {
+                       readonly get => persp1;
+                       set => persp1 = value;
+               }
+
+               // public float persp2
+               private Single persp2;
+               public Single Persp2 {
+                       readonly get => persp2;
+                       set => persp2 = value;
+               }
+
+               public readonly bool Equals (SKMatrix obj) =>
+                       scaleX == obj.scaleX && skewX == obj.skewX && transX == obj.transX && skewY == obj.skewY && scaleY == obj.scaleY && transY == obj.transY && persp0 == obj.persp0 && persp1 == obj.persp1 && persp2 == obj.persp2;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKMatrix f && Equals (f);
+
+               public static bool operator == (SKMatrix left, SKMatrix right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKMatrix left, SKMatrix right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (scaleX);
+                       hash.Add (skewX);
+                       hash.Add (transX);
+                       hash.Add (skewY);
+                       hash.Add (scaleY);
+                       hash.Add (transY);
+                       hash.Add (persp0);
+                       hash.Add (persp1);
+                       hash.Add (persp2);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_pngencoder_options_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKPngEncoderOptions : IEquatable<SKPngEncoderOptions> {
+               // public sk_pngencoder_filterflags_t fFilterFlags
+               private SKPngEncoderFilterFlags fFilterFlags;
+
+               // public int fZLibLevel
+               private Int32 fZLibLevel;
+
+               // public sk_transfer_function_behavior_t fUnpremulBehavior
+               private SKTransferFunctionBehavior fUnpremulBehavior;
+
+               // public void* fComments
+               private void* fComments;
+
+               public readonly bool Equals (SKPngEncoderOptions obj) =>
+                       fFilterFlags == obj.fFilterFlags && fZLibLevel == obj.fZLibLevel && fUnpremulBehavior == obj.fUnpremulBehavior && fComments == obj.fComments;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKPngEncoderOptions f && Equals (f);
+
+               public static bool operator == (SKPngEncoderOptions left, SKPngEncoderOptions right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKPngEncoderOptions left, SKPngEncoderOptions right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fFilterFlags);
+                       hash.Add (fZLibLevel);
+                       hash.Add (fUnpremulBehavior);
+                       hash.Add (fComments);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_point_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKPoint : IEquatable<SKPoint> {
+               // public float x
+               private Single x;
+               public Single X {
+                       readonly get => x;
+                       set => x = value;
+               }
+
+               // public float y
+               private Single y;
+               public Single Y {
+                       readonly get => y;
+                       set => y = value;
+               }
+
+               public readonly bool Equals (SKPoint obj) =>
+                       x == obj.x && y == obj.y;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKPoint f && Equals (f);
+
+               public static bool operator == (SKPoint left, SKPoint right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKPoint left, SKPoint right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (x);
+                       hash.Add (y);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_point3_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKPoint3 : IEquatable<SKPoint3> {
+               // public float x
+               private Single x;
+               public Single X {
+                       readonly get => x;
+                       set => x = value;
+               }
+
+               // public float y
+               private Single y;
+               public Single Y {
+                       readonly get => y;
+                       set => y = value;
+               }
+
+               // public float z
+               private Single z;
+               public Single Z {
+                       readonly get => z;
+                       set => z = value;
+               }
+
+               public readonly bool Equals (SKPoint3 obj) =>
+                       x == obj.x && y == obj.y && z == obj.z;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKPoint3 f && Equals (f);
+
+               public static bool operator == (SKPoint3 left, SKPoint3 right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKPoint3 left, SKPoint3 right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (x);
+                       hash.Add (y);
+                       hash.Add (z);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_rect_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKRect : IEquatable<SKRect> {
+               // public float left
+               private Single left;
+               public Single Left {
+                       readonly get => left;
+                       set => left = value;
+               }
+
+               // public float top
+               private Single top;
+               public Single Top {
+                       readonly get => top;
+                       set => top = value;
+               }
+
+               // public float right
+               private Single right;
+               public Single Right {
+                       readonly get => right;
+                       set => right = value;
+               }
+
+               // public float bottom
+               private Single bottom;
+               public Single Bottom {
+                       readonly get => bottom;
+                       set => bottom = value;
+               }
+
+               public readonly bool Equals (SKRect obj) =>
+                       left == obj.left && top == obj.top && right == obj.right && bottom == obj.bottom;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKRect f && Equals (f);
+
+               public static bool operator == (SKRect left, SKRect right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKRect left, SKRect right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (left);
+                       hash.Add (top);
+                       hash.Add (right);
+                       hash.Add (bottom);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_rsxform_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKRotationScaleMatrix : IEquatable<SKRotationScaleMatrix> {
+               // public float fSCos
+               private Single fSCos;
+               public Single SCos {
+                       readonly get => fSCos;
+                       set => fSCos = value;
+               }
+
+               // public float fSSin
+               private Single fSSin;
+               public Single SSin {
+                       readonly get => fSSin;
+                       set => fSSin = value;
+               }
+
+               // public float fTX
+               private Single fTX;
+               public Single TX {
+                       readonly get => fTX;
+                       set => fTX = value;
+               }
+
+               // public float fTY
+               private Single fTY;
+               public Single TY {
+                       readonly get => fTY;
+                       set => fTY = value;
+               }
+
+               public readonly bool Equals (SKRotationScaleMatrix obj) =>
+                       fSCos == obj.fSCos && fSSin == obj.fSSin && fTX == obj.fTX && fTY == obj.fTY;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKRotationScaleMatrix f && Equals (f);
+
+               public static bool operator == (SKRotationScaleMatrix left, SKRotationScaleMatrix right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKRotationScaleMatrix left, SKRotationScaleMatrix right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fSCos);
+                       hash.Add (fSSin);
+                       hash.Add (fTX);
+                       hash.Add (fTY);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_size_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKSize : IEquatable<SKSize> {
+               // public float w
+               private Single w;
+               public Single Width {
+                       readonly get => w;
+                       set => w = value;
+               }
+
+               // public float h
+               private Single h;
+               public Single Height {
+                       readonly get => h;
+                       set => h = value;
+               }
+
+               public readonly bool Equals (SKSize obj) =>
+                       w == obj.w && h == obj.h;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKSize f && Equals (f);
+
+               public static bool operator == (SKSize left, SKSize right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKSize left, SKSize right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (w);
+                       hash.Add (h);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_textblob_builder_runbuffer_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKRunBufferInternal : IEquatable<SKRunBufferInternal> {
+               // public void* glyphs
+               public void* glyphs;
+
+               // public void* pos
+               public void* pos;
+
+               // public void* utf8text
+               public void* utf8text;
+
+               // public void* clusters
+               public void* clusters;
+
+               public readonly bool Equals (SKRunBufferInternal obj) =>
+                       glyphs == obj.glyphs && pos == obj.pos && utf8text == obj.utf8text && clusters == obj.clusters;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKRunBufferInternal f && Equals (f);
+
+               public static bool operator == (SKRunBufferInternal left, SKRunBufferInternal right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKRunBufferInternal left, SKRunBufferInternal right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (glyphs);
+                       hash.Add (pos);
+                       hash.Add (utf8text);
+                       hash.Add (clusters);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_time_datetime_t
+       [StructLayout (LayoutKind.Sequential)]
+       internal unsafe partial struct SKTimeDateTimeInternal : IEquatable<SKTimeDateTimeInternal> {
+               // public int16_t fTimeZoneMinutes
+               public Int16 fTimeZoneMinutes;
+
+               // public uint16_t fYear
+               public UInt16 fYear;
+
+               // public uint8_t fMonth
+               public Byte fMonth;
+
+               // public uint8_t fDayOfWeek
+               public Byte fDayOfWeek;
+
+               // public uint8_t fDay
+               public Byte fDay;
+
+               // public uint8_t fHour
+               public Byte fHour;
+
+               // public uint8_t fMinute
+               public Byte fMinute;
+
+               // public uint8_t fSecond
+               public Byte fSecond;
+
+               public readonly bool Equals (SKTimeDateTimeInternal obj) =>
+                       fTimeZoneMinutes == obj.fTimeZoneMinutes && fYear == obj.fYear && fMonth == obj.fMonth && fDayOfWeek == obj.fDayOfWeek && fDay == obj.fDay && fHour == obj.fHour && fMinute == obj.fMinute && fSecond == obj.fSecond;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKTimeDateTimeInternal f && Equals (f);
+
+               public static bool operator == (SKTimeDateTimeInternal left, SKTimeDateTimeInternal right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKTimeDateTimeInternal left, SKTimeDateTimeInternal right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fTimeZoneMinutes);
+                       hash.Add (fYear);
+                       hash.Add (fMonth);
+                       hash.Add (fDayOfWeek);
+                       hash.Add (fDay);
+                       hash.Add (fHour);
+                       hash.Add (fMinute);
+                       hash.Add (fSecond);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       // sk_webpencoder_options_t
+       [StructLayout (LayoutKind.Sequential)]
+       public unsafe partial struct SKWebpEncoderOptions : IEquatable<SKWebpEncoderOptions> {
+               // public sk_webpencoder_compression_t fCompression
+               private SKWebpEncoderCompression fCompression;
+               public SKWebpEncoderCompression Compression {
+                       readonly get => fCompression;
+                       set => fCompression = value;
+               }
+
+               // public float fQuality
+               private Single fQuality;
+               public Single Quality {
+                       readonly get => fQuality;
+                       set => fQuality = value;
+               }
+
+               // public sk_transfer_function_behavior_t fUnpremulBehavior
+               private SKTransferFunctionBehavior fUnpremulBehavior;
+               public SKTransferFunctionBehavior UnpremulBehavior {
+                       readonly get => fUnpremulBehavior;
+                       set => fUnpremulBehavior = value;
+               }
+
+               public readonly bool Equals (SKWebpEncoderOptions obj) =>
+                       fCompression == obj.fCompression && fQuality == obj.fQuality && fUnpremulBehavior == obj.fUnpremulBehavior;
+
+               public readonly override bool Equals (object obj) =>
+                       obj is SKWebpEncoderOptions f && Equals (f);
+
+               public static bool operator == (SKWebpEncoderOptions left, SKWebpEncoderOptions right) =>
+                       left.Equals (right);
+
+               public static bool operator != (SKWebpEncoderOptions left, SKWebpEncoderOptions right) =>
+                       !left.Equals (right);
+
+               public readonly override int GetHashCode ()
+               {
+                       var hash = new HashCode ();
+                       hash.Add (fCompression);
+                       hash.Add (fQuality);
+                       hash.Add (fUnpremulBehavior);
+                       return hash.ToHashCode ();
+               }
+
+       }
+
+       #endregion
+
+       #region Enums
+
+       // gr_backend_t
+       public enum GRBackend {
+               // METAL_GR_BACKEND = 0
+               Metal = 0,
+               // OPENGL_GR_BACKEND = 1
+               OpenGL = 1,
+               // VULKAN_GR_BACKEND = 2
+               Vulkan = 2,
+       }
+
+       // gr_pixelconfig_t
+       public enum GRPixelConfig {
+               // UNKNOWN_GR_PIXEL_CONFIG = 0
+               Unknown = 0,
+               // ALPHA_8_GR_PIXEL_CONFIG = 1
+               Alpha8 = 1,
+               // GRAY_8_GR_PIXEL_CONFIG = 2
+               Gray8 = 2,
+               // RGB_565_GR_PIXEL_CONFIG = 3
+               Rgb565 = 3,
+               // RGBA_4444_GR_PIXEL_CONFIG = 4
+               Rgba4444 = 4,
+               // RGBA_8888_GR_PIXEL_CONFIG = 5
+               Rgba8888 = 5,
+               // RGB_888_GR_PIXEL_CONFIG = 6
+               Rgb888 = 6,
+               // BGRA_8888_GR_PIXEL_CONFIG = 7
+               Bgra8888 = 7,
+               // SRGBA_8888_GR_PIXEL_CONFIG = 8
+               Srgba8888 = 8,
+               // SBGRA_8888_GR_PIXEL_CONFIG = 9
+               Sbgra8888 = 9,
+               // RGBA_1010102_GR_PIXEL_CONFIG = 10
+               Rgba1010102 = 10,
+               // RGBA_FLOAT_GR_PIXEL_CONFIG = 11
+               RgbaFloat = 11,
+               // RG_FLOAT_GR_PIXEL_CONFIG = 12
+               RgFloat = 12,
+               // ALPHA_HALF_GR_PIXEL_CONFIG = 13
+               AlphaHalf = 13,
+               // RGBA_HALF_GR_PIXEL_CONFIG = 14
+               RgbaHalf = 14,
+       }
+
+       // gr_surfaceorigin_t
+       public enum GRSurfaceOrigin {
+               // TOP_LEFT_GR_SURFACE_ORIGIN = 0
+               TopLeft = 0,
+               // BOTTOM_LEFT_GR_SURFACE_ORIGIN = 1
+               BottomLeft = 1,
+       }
+
+       // sk_alphatype_t
+       public enum SKAlphaType {
+               // UNKNOWN_SK_ALPHATYPE = 0
+               Unknown = 0,
+               // OPAQUE_SK_ALPHATYPE = 1
+               Opaque = 1,
+               // PREMUL_SK_ALPHATYPE = 2
+               Premul = 2,
+               // UNPREMUL_SK_ALPHATYPE = 3
+               Unpremul = 3,
+       }
+
+       // sk_bitmap_allocflags_t
+       [Flags]
+       public enum SKBitmapAllocFlags {
+               // NONE_SK_BITMAP_ALLOC_FLAGS = 0
+               None = 0,
+               // ZERO_PIXELS_SK_BITMAP_ALLOC_FLAGS = 1 << 0
+               ZeroPixels = 1,
+       }
+
+       // sk_blendmode_t
+       public enum SKBlendMode {
+               // CLEAR_SK_BLENDMODE = 0
+               Clear = 0,
+               // SRC_SK_BLENDMODE = 1
+               Src = 1,
+               // DST_SK_BLENDMODE = 2
+               Dst = 2,
+               // SRCOVER_SK_BLENDMODE = 3
+               SrcOver = 3,
+               // DSTOVER_SK_BLENDMODE = 4
+               DstOver = 4,
+               // SRCIN_SK_BLENDMODE = 5
+               SrcIn = 5,
+               // DSTIN_SK_BLENDMODE = 6
+               DstIn = 6,
+               // SRCOUT_SK_BLENDMODE = 7
+               SrcOut = 7,
+               // DSTOUT_SK_BLENDMODE = 8
+               DstOut = 8,
+               // SRCATOP_SK_BLENDMODE = 9
+               SrcATop = 9,
+               // DSTATOP_SK_BLENDMODE = 10
+               DstATop = 10,
+               // XOR_SK_BLENDMODE = 11
+               Xor = 11,
+               // PLUS_SK_BLENDMODE = 12
+               Plus = 12,
+               // MODULATE_SK_BLENDMODE = 13
+               Modulate = 13,
+               // SCREEN_SK_BLENDMODE = 14
+               Screen = 14,
+               // OVERLAY_SK_BLENDMODE = 15
+               Overlay = 15,
+               // DARKEN_SK_BLENDMODE = 16
+               Darken = 16,
+               // LIGHTEN_SK_BLENDMODE = 17
+               Lighten = 17,
+               // COLORDODGE_SK_BLENDMODE = 18
+               ColorDodge = 18,
+               // COLORBURN_SK_BLENDMODE = 19
+               ColorBurn = 19,
+               // HARDLIGHT_SK_BLENDMODE = 20
+               HardLight = 20,
+               // SOFTLIGHT_SK_BLENDMODE = 21
+               SoftLight = 21,
+               // DIFFERENCE_SK_BLENDMODE = 22
+               Difference = 22,
+               // EXCLUSION_SK_BLENDMODE = 23
+               Exclusion = 23,
+               // MULTIPLY_SK_BLENDMODE = 24
+               Multiply = 24,
+               // HUE_SK_BLENDMODE = 25
+               Hue = 25,
+               // SATURATION_SK_BLENDMODE = 26
+               Saturation = 26,
+               // COLOR_SK_BLENDMODE = 27
+               Color = 27,
+               // LUMINOSITY_SK_BLENDMODE = 28
+               Luminosity = 28,
+       }
+
+       // sk_blurstyle_t
+       public enum SKBlurStyle {
+               // NORMAL_SK_BLUR_STYLE = 0
+               Normal = 0,
+               // SOLID_SK_BLUR_STYLE = 1
+               Solid = 1,
+               // OUTER_SK_BLUR_STYLE = 2
+               Outer = 2,
+               // INNER_SK_BLUR_STYLE = 3
+               Inner = 3,
+       }
+
+       // sk_clipop_t
+       public enum SKClipOperation {
+               // DIFFERENCE_SK_CLIPOP = 0
+               Difference = 0,
+               // INTERSECT_SK_CLIPOP = 1
+               Intersect = 1,
+       }
+
+       // sk_codec_result_t
+       public enum SKCodecResult {
+               // SUCCESS_SK_CODEC_RESULT = 0
+               Success = 0,
+               // INCOMPLETE_INPUT_SK_CODEC_RESULT = 1
+               IncompleteInput = 1,
+               // ERROR_IN_INPUT_SK_CODEC_RESULT = 2
+               ErrorInInput = 2,
+               // INVALID_CONVERSION_SK_CODEC_RESULT = 3
+               InvalidConversion = 3,
+               // INVALID_SCALE_SK_CODEC_RESULT = 4
+               InvalidScale = 4,
+               // INVALID_PARAMETERS_SK_CODEC_RESULT = 5
+               InvalidParameters = 5,
+               // INVALID_INPUT_SK_CODEC_RESULT = 6
+               InvalidInput = 6,
+               // COULD_NOT_REWIND_SK_CODEC_RESULT = 7
+               CouldNotRewind = 7,
+               // INTERNAL_ERROR_SK_CODEC_RESULT = 8
+               InternalError = 8,
+               // UNIMPLEMENTED_SK_CODEC_RESULT = 9
+               Unimplemented = 9,
+       }
+
+       // sk_codec_scanline_order_t
+       public enum SKCodecScanlineOrder {
+               // TOP_DOWN_SK_CODEC_SCANLINE_ORDER = 0
+               TopDown = 0,
+               // BOTTOM_UP_SK_CODEC_SCANLINE_ORDER = 1
+               BottomUp = 1,
+       }
+
+       // sk_codec_zero_initialized_t
+       public enum SKZeroInitialized {
+               // YES_SK_CODEC_ZERO_INITIALIZED = 0
+               Yes = 0,
+               // NO_SK_CODEC_ZERO_INITIALIZED = 1
+               No = 1,
+       }
+
+       // sk_codecanimation_disposalmethod_t
+       public enum SKCodecAnimationDisposalMethod {
+               // KEEP_SK_CODEC_ANIMATION_DISPOSAL_METHOD = 1
+               Keep = 1,
+               // RESTORE_BG_COLOR_SK_CODEC_ANIMATION_DISPOSAL_METHOD = 2
+               RestoreBackgroundColor = 2,
+               // RESTORE_PREVIOUS_SK_CODEC_ANIMATION_DISPOSAL_METHOD = 3
+               RestorePrevious = 3,
+       }
+
+       // sk_colorspace_gamut_t
+       public enum SKColorSpaceGamut {
+               // SRGB_SK_COLORSPACE_GAMUT = 0
+               Srgb = 0,
+               // ADOBE_RGB_SK_COLORSPACE_GAMUT = 1
+               AdobeRgb = 1,
+               // DCIP3_D65_SK_COLORSPACE_GAMUT = 2
+               Dcip3D65 = 2,
+               // REC2020_SK_COLORSPACE_GAMUT = 3
+               Rec2020 = 3,
+       }
+
+       // sk_colorspace_render_target_gamma_t
+       public enum SKColorSpaceRenderTargetGamma {
+               // LINEAR_SK_COLORSPACE_RENDER_TARGET_GAMMA = 0
+               Linear = 0,
+               // SRGB_SK_COLORSPACE_RENDER_TARGET_GAMMA = 1
+               Srgb = 1,
+       }
+
+       // sk_colorspace_type_t
+       public enum SKColorSpaceType {
+               // RGB_SK_COLORSPACE_TYPE = 0
+               Rgb = 0,
+               // CMYK_SK_COLORSPACE_TYPE = 1
+               Cmyk = 1,
+               // GRAY_SK_COLORSPACE_TYPE = 2
+               Gray = 2,
+       }
+
+       // sk_colortype_t
+       public enum SKColorType {
+               // UNKNOWN_SK_COLORTYPE = 0
+               Unknown = 0,
+               // ALPHA_8_SK_COLORTYPE = 1
+               Alpha8 = 1,
+               // RGB_565_SK_COLORTYPE = 2
+               Rgb565 = 2,
+               // ARGB_4444_SK_COLORTYPE = 3
+               Argb4444 = 3,
+               // RGBA_8888_SK_COLORTYPE = 4
+               Rgba8888 = 4,
+               // RGB_888X_SK_COLORTYPE = 5
+               Rgb888x = 5,
+               // BGRA_8888_SK_COLORTYPE = 6
+               Bgra8888 = 6,
+               // RGBA_1010102_SK_COLORTYPE = 7
+               Rgba1010102 = 7,
+               // RGB_101010X_SK_COLORTYPE = 8
+               Rgb101010x = 8,
+               // GRAY_8_SK_COLORTYPE = 9
+               Gray8 = 9,
+               // RGBA_F16_SK_COLORTYPE = 10
+               RgbaF16 = 10,
+       }
+
+       // sk_crop_rect_flags_t
+       [Flags]
+       public enum SKCropRectFlags {
+               // HAS_NONE_SK_CROP_RECT_FLAG = 0x00
+               HasNone = 0,
+               // HAS_LEFT_SK_CROP_RECT_FLAG = 0x01
+               HasLeft = 1,
+               // HAS_TOP_SK_CROP_RECT_FLAG = 0x02
+               HasTop = 2,
+               // HAS_WIDTH_SK_CROP_RECT_FLAG = 0x04
+               HasWidth = 4,
+               // HAS_HEIGHT_SK_CROP_RECT_FLAG = 0x08
+               HasHeight = 8,
+               // HAS_ALL_SK_CROP_RECT_FLAG = 0x0F
+               HasAll = 15,
+       }
+
+       // sk_displacement_map_effect_channel_selector_type_t
+       public enum SKDisplacementMapEffectChannelSelectorType {
+               // UNKNOWN_SK_DISPLACEMENT_MAP_EFFECT_CHANNEL_SELECTOR_TYPE = 0
+               Unknown = 0,
+               // R_SK_DISPLACEMENT_MAP_EFFECT_CHANNEL_SELECTOR_TYPE = 1
+               R = 1,
+               // G_SK_DISPLACEMENT_MAP_EFFECT_CHANNEL_SELECTOR_TYPE = 2
+               G = 2,
+               // B_SK_DISPLACEMENT_MAP_EFFECT_CHANNEL_SELECTOR_TYPE = 3
+               B = 3,
+               // A_SK_DISPLACEMENT_MAP_EFFECT_CHANNEL_SELECTOR_TYPE = 4
+               A = 4,
+       }
+
+       // sk_drop_shadow_image_filter_shadow_mode_t
+       public enum SKDropShadowImageFilterShadowMode {
+               // DRAW_SHADOW_AND_FOREGROUND_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE = 0
+               DrawShadowAndForeground = 0,
+               // DRAW_SHADOW_ONLY_SK_DROP_SHADOW_IMAGE_FILTER_SHADOW_MODE = 1
+               DrawShadowOnly = 1,
+       }
+
+       // sk_encoded_image_format_t
+       public enum SKEncodedImageFormat {
+               // BMP_SK_ENCODED_FORMAT = 0
+               Bmp = 0,
+               // GIF_SK_ENCODED_FORMAT = 1
+               Gif = 1,
+               // ICO_SK_ENCODED_FORMAT = 2
+               Ico = 2,
+               // JPEG_SK_ENCODED_FORMAT = 3
+               Jpeg = 3,
+               // PNG_SK_ENCODED_FORMAT = 4
+               Png = 4,
+               // WBMP_SK_ENCODED_FORMAT = 5
+               Wbmp = 5,
+               // WEBP_SK_ENCODED_FORMAT = 6
+               Webp = 6,
+               // PKM_SK_ENCODED_FORMAT = 7
+               Pkm = 7,
+               // KTX_SK_ENCODED_FORMAT = 8
+               Ktx = 8,
+               // ASTC_SK_ENCODED_FORMAT = 9
+               Astc = 9,
+               // DNG_SK_ENCODED_FORMAT = 10
+               Dng = 10,
+               // HEIF_SK_ENCODED_FORMAT = 11
+               Heif = 11,
+       }
+
+       // sk_encodedorigin_t
+       public enum SKEncodedOrigin {
+               // TOP_LEFT_SK_ENCODED_ORIGIN = 1
+               TopLeft = 1,
+               // TOP_RIGHT_SK_ENCODED_ORIGIN = 2
+               TopRight = 2,
+               // BOTTOM_RIGHT_SK_ENCODED_ORIGIN = 3
+               BottomRight = 3,
+               // BOTTOM_LEFT_SK_ENCODED_ORIGIN = 4
+               BottomLeft = 4,
+               // LEFT_TOP_SK_ENCODED_ORIGIN = 5
+               LeftTop = 5,
+               // RIGHT_TOP_SK_ENCODED_ORIGIN = 6
+               RightTop = 6,
+               // RIGHT_BOTTOM_SK_ENCODED_ORIGIN = 7
+               RightBottom = 7,
+               // LEFT_BOTTOM_SK_ENCODED_ORIGIN = 8
+               LeftBottom = 8,
+               // DEFAULT_SK_ENCODED_ORIGIN = TOP_LEFT_SK_ENCODED_ORIGIN
+               Default = 1,
+       }
+
+       // sk_encoding_t
+       public enum SKEncoding {
+               // UTF8_SK_ENCODING = 0
+               Utf8 = 0,
+               // UTF16_SK_ENCODING = 1
+               Utf16 = 1,
+               // UTF32_SK_ENCODING = 2
+               Utf32 = 2,
+       }
+
+       // sk_filter_quality_t
+       public enum SKFilterQuality {
+               // NONE_SK_FILTER_QUALITY = 0
+               None = 0,
+               // LOW_SK_FILTER_QUALITY = 1
+               Low = 1,
+               // MEDIUM_SK_FILTER_QUALITY = 2
+               Medium = 2,
+               // HIGH_SK_FILTER_QUALITY = 3
+               High = 3,
+       }
+
+       // sk_font_style_slant_t
+       public enum SKFontStyleSlant {
+               // UPRIGHT_SK_FONT_STYLE_SLANT = 0
+               Upright = 0,
+               // ITALIC_SK_FONT_STYLE_SLANT = 1
+               Italic = 1,
+               // OBLIQUE_SK_FONT_STYLE_SLANT = 2
+               Oblique = 2,
+       }
+
+       // sk_gamma_named_t
+       public enum SKNamedGamma {
+               // LINEAR_SK_GAMMA_NAMED = 0
+               Linear = 0,
+               // SRGB_SK_GAMMA_NAMED = 1
+               Srgb = 1,
+               // TWO_DOT_TWO_CURVE_SK_GAMMA_NAMED = 2
+               TwoDotTwoCurve = 2,
+               // NON_STANDARD_SK_GAMMA_NAMED = 3
+               NonStandard = 3,
+       }
+
+       // sk_highcontrastconfig_invertstyle_t
+       public enum SKHighContrastConfigInvertStyle {
+               // NO_INVERT_SK_HIGH_CONTRAST_CONFIG_INVERT_STYLE = 0
+               NoInvert = 0,
+               // INVERT_BRIGHTNESS_SK_HIGH_CONTRAST_CONFIG_INVERT_STYLE = 1
+               InvertBrightness = 1,
+               // INVERT_LIGHTNESS_SK_HIGH_CONTRAST_CONFIG_INVERT_STYLE = 2
+               InvertLightness = 2,
+       }
+
+       // sk_image_caching_hint_t
+       public enum SKImageCachingHint {
+               // ALLOW_SK_IMAGE_CACHING_HINT = 0
+               Allow = 0,
+               // DISALLOW_SK_IMAGE_CACHING_HINT = 1
+               Disallow = 1,
+       }
+
+       // sk_jpegencoder_alphaoption_t
+       public enum SKJpegEncoderAlphaOption {
+               // IGNORE_SK_JPEGENCODER_ALPHA_OPTION = 0
+               Ignore = 0,
+               // BLEND_ON_BLACK_SK_JPEGENCODER_ALPHA_OPTION = 1
+               BlendOnBlack = 1,
+       }
+
+       // sk_jpegencoder_downsample_t
+       public enum SKJpegEncoderDownsample {
+               // DOWNSAMPLE_420_SK_JPEGENCODER_DOWNSAMPLE = 0
+               Downsample420 = 0,
+               // DOWNSAMPLE_422_SK_JPEGENCODER_DOWNSAMPLE = 1
+               Downsample422 = 1,
+               // DOWNSAMPLE_444_SK_JPEGENCODER_DOWNSAMPLE = 2
+               Downsample444 = 2,
+       }
+
+       // sk_lattice_recttype_t
+       public enum SKLatticeRectType {
+               // DEFAULT_SK_LATTICE_RECT_TYPE = 0
+               Default = 0,
+               // TRANSPARENT_SK_LATTICE_RECT_TYPE = 1
+               Transparent = 1,
+               // FIXED_COLOR_SK_LATTICE_RECT_TYPE = 2
+               FixedColor = 2,
+       }
+
+       // sk_mask_format_t
+       public enum SKMaskFormat {
+               // BW_SK_MASK_FORMAT = 0
+               BW = 0,
+               // A8_SK_MASK_FORMAT = 1
+               A8 = 1,
+               // THREE_D_SK_MASK_FORMAT = 2
+               ThreeD = 2,
+               // ARGB32_SK_MASK_FORMAT = 3
+               Argb32 = 3,
+               // LCD16_SK_MASK_FORMAT = 4
+               Lcd16 = 4,
+       }
+
+       // sk_matrix_convolution_tilemode_t
+       public enum SKMatrixConvolutionTileMode {
+               // CLAMP_SK_MATRIX_CONVOLUTION_TILEMODE = 0
+               Clamp = 0,
+               // REPEAT_SK_MATRIX_CONVOLUTION_TILEMODE = 1
+               Repeat = 1,
+               // CLAMP_TO_BLACK_SK_MATRIX_CONVOLUTION_TILEMODE = 2
+               ClampToBlack = 2,
+       }
+
+       // sk_matrix44_type_mask_t
+       [Flags]
+       public enum SKMatrix44TypeMask {
+               // IDENTITY_SK_MATRIX44_TYPE_MASK = 0
+               Identity = 0,
+               // TRANSLATE_SK_MATRIX44_TYPE_MASK = 0x01
+               Translate = 1,
+               // SCALE_SK_MATRIX44_TYPE_MASK = 0x02
+               Scale = 2,
+               // AFFINE_SK_MATRIX44_TYPE_MASK = 0x04
+               Affine = 4,
+               // PERSPECTIVE_SK_MATRIX44_TYPE_MASK = 0x08
+               Perspective = 8,
+       }
+
+       // sk_paint_hinting_t
+       public enum SKPaintHinting {
+               // NO_HINTING_SK_PAINT_HINTING = 0
+               NoHinting = 0,
+               // SLIGHT_HINTING_SK_PAINT_HINTING = 1
+               Slight = 1,
+               // NORMAL_HINTING_SK_PAINT_HINTING = 2
+               Normal = 2,
+               // FULL_HINTING_SK_PAINT_HINTING = 3
+               Full = 3,
+       }
+
+       // sk_paint_style_t
+       public enum SKPaintStyle {
+               // FILL_SK_PAINT_STYLE = 0
+               Fill = 0,
+               // STROKE_SK_PAINT_STYLE = 1
+               Stroke = 1,
+               // STROKE_AND_FILL_SK_PAINT_STYLE = 2
+               StrokeAndFill = 2,
+       }
+
+       // sk_path_add_mode_t
+       public enum SKPathAddMode {
+               // APPEND_SK_PATH_ADD_MODE = 0
+               Append = 0,
+               // EXTEND_SK_PATH_ADD_MODE = 1
+               Extend = 1,
+       }
+
+       // sk_path_arc_size_t
+       public enum SKPathArcSize {
+               // SMALL_SK_PATH_ARC_SIZE = 0
+               Small = 0,
+               // LARGE_SK_PATH_ARC_SIZE = 1
+               Large = 1,
+       }
+
+       // sk_path_convexity_t
+       public enum SKPathConvexity {
+               // UNKNOWN_SK_PATH_CONVEXITY = 0
+               Unknown = 0,
+               // CONVEX_SK_PATH_CONVEXITY = 1
+               Convex = 1,
+               // CONCAVE_SK_PATH_CONVEXITY = 2
+               Concave = 2,
+       }
+
+       // sk_path_direction_t
+       public enum SKPathDirection {
+               // CW_SK_PATH_DIRECTION = 0
+               Clockwise = 0,
+               // CCW_SK_PATH_DIRECTION = 1
+               CounterClockwise = 1,
+       }
+
+       // sk_path_effect_1d_style_t
+       public enum SKPath1DPathEffectStyle {
+               // TRANSLATE_SK_PATH_EFFECT_1D_STYLE = 0
+               Translate = 0,
+               // ROTATE_SK_PATH_EFFECT_1D_STYLE = 1
+               Rotate = 1,
+               // MORPH_SK_PATH_EFFECT_1D_STYLE = 2
+               Morph = 2,
+       }
+
+       // sk_path_effect_trim_mode_t
+       public enum SKTrimPathEffectMode {
+               // NORMAL_SK_PATH_EFFECT_TRIM_MODE = 0
+               Normal = 0,
+               // INVERTED_SK_PATH_EFFECT_TRIM_MODE = 1
+               Inverted = 1,
+       }
+
+       // sk_path_filltype_t
+       public enum SKPathFillType {
+               // WINDING_SK_PATH_FILLTYPE = 0
+               Winding = 0,
+               // EVENODD_SK_PATH_FILLTYPE = 1
+               EvenOdd = 1,
+               // INVERSE_WINDING_SK_PATH_FILLTYPE = 2
+               InverseWinding = 2,
+               // INVERSE_EVENODD_SK_PATH_FILLTYPE = 3
+               InverseEvenOdd = 3,
+       }
+
+       // sk_path_segment_mask_t
+       [Flags]
+       public enum SKPathSegmentMask {
+               // LINE_SK_PATH_SEGMENT_MASK = 1 << 0
+               Line = 1,
+               // QUAD_SK_PATH_SEGMENT_MASK = 1 << 1
+               Quad = 2,
+               // CONIC_SK_PATH_SEGMENT_MASK = 1 << 2
+               Conic = 4,
+               // CUBIC_SK_PATH_SEGMENT_MASK = 1 << 3
+               Cubic = 8,
+       }
+
+       // sk_path_verb_t
+       public enum SKPathVerb {
+               // MOVE_SK_PATH_VERB = 0
+               Move = 0,
+               // LINE_SK_PATH_VERB = 1
+               Line = 1,
+               // QUAD_SK_PATH_VERB = 2
+               Quad = 2,
+               // CONIC_SK_PATH_VERB = 3
+               Conic = 3,
+               // CUBIC_SK_PATH_VERB = 4
+               Cubic = 4,
+               // CLOSE_SK_PATH_VERB = 5
+               Close = 5,
+               // DONE_SK_PATH_VERB = 6
+               Done = 6,
+       }
+
+       // sk_pathmeasure_matrixflags_t
+       [Flags]
+       public enum SKPathMeasureMatrixFlags {
+               // GET_POSITION_SK_PATHMEASURE_MATRIXFLAGS = 0x01
+               GetPosition = 1,
+               // GET_TANGENT_SK_PATHMEASURE_MATRIXFLAGS = 0x02
+               GetTangent = 2,
+               // GET_POS_AND_TAN_SK_PATHMEASURE_MATRIXFLAGS = GET_POSITION_SK_PATHMEASURE_MATRIXFLAGS | GET_TANGENT_SK_PATHMEASURE_MATRIXFLAGS
+               GetPositionAndTangent = 3,
+       }
+
+       // sk_pathop_t
+       public enum SKPathOp {
+               // DIFFERENCE_SK_PATHOP = 0
+               Difference = 0,
+               // INTERSECT_SK_PATHOP = 1
+               Intersect = 1,
+               // UNION_SK_PATHOP = 2
+               Union = 2,
+               // XOR_SK_PATHOP = 3
+               Xor = 3,
+               // REVERSE_DIFFERENCE_SK_PATHOP = 4
+               ReverseDifference = 4,
+       }
+
+       // sk_pixelgeometry_t
+       public enum SKPixelGeometry {
+               // UNKNOWN_SK_PIXELGEOMETRY = 0
+               Unknown = 0,
+               // RGB_H_SK_PIXELGEOMETRY = 1
+               RgbHorizontal = 1,
+               // BGR_H_SK_PIXELGEOMETRY = 2
+               BgrHorizontal = 2,
+               // RGB_V_SK_PIXELGEOMETRY = 3
+               RgbVertical = 3,
+               // BGR_V_SK_PIXELGEOMETRY = 4
+               BgrVertical = 4,
+       }
+
+       // sk_pngencoder_filterflags_t
+       [Flags]
+       public enum SKPngEncoderFilterFlags {
+               // ZERO_SK_PNGENCODER_FILTER_FLAGS = 0x00
+               NoFilters = 0,
+               // NONE_SK_PNGENCODER_FILTER_FLAGS = 0x08
+               None = 8,
+               // SUB_SK_PNGENCODER_FILTER_FLAGS = 0x10
+               Sub = 16,
+               // UP_SK_PNGENCODER_FILTER_FLAGS = 0x20
+               Up = 32,
+               // AVG_SK_PNGENCODER_FILTER_FLAGS = 0x40
+               Avg = 64,
+               // PAETH_SK_PNGENCODER_FILTER_FLAGS = 0x80
+               Paeth = 128,
+               // ALL_SK_PNGENCODER_FILTER_FLAGS = NONE_SK_PNGENCODER_FILTER_FLAGS | SUB_SK_PNGENCODER_FILTER_FLAGS | UP_SK_PNGENCODER_FILTER_FLAGS | AVG_SK_PNGENCODER_FILTER_FLAGS | PAETH_SK_PNGENCODER_FILTER_FLAGS
+               AllFilters = 248,
+       }
+
+       // sk_point_mode_t
+       public enum SKPointMode {
+               // POINTS_SK_POINT_MODE = 0
+               Points = 0,
+               // LINES_SK_POINT_MODE = 1
+               Lines = 1,
+               // POLYGON_SK_POINT_MODE = 2
+               Polygon = 2,
+       }
+
+       // sk_region_op_t
+       public enum SKRegionOperation {
+               // DIFFERENCE_SK_REGION_OP = 0
+               Difference = 0,
+               // INTERSECT_SK_REGION_OP = 1
+               Intersect = 1,
+               // UNION_SK_REGION_OP = 2
+               Union = 2,
+               // XOR_SK_REGION_OP = 3
+               XOR = 3,
+               // REVERSE_DIFFERENCE_SK_REGION_OP = 4
+               ReverseDifference = 4,
+               // REPLACE_SK_REGION_OP = 5
+               Replace = 5,
+       }
+
+       // sk_rrect_corner_t
+       public enum SKRoundRectCorner {
+               // UPPER_LEFT_SK_RRECT_CORNER = 0
+               UpperLeft = 0,
+               // UPPER_RIGHT_SK_RRECT_CORNER = 1
+               UpperRight = 1,
+               // LOWER_RIGHT_SK_RRECT_CORNER = 2
+               LowerRight = 2,
+               // LOWER_LEFT_SK_RRECT_CORNER = 3
+               LowerLeft = 3,
+       }
+
+       // sk_rrect_type_t
+       public enum SKRoundRectType {
+               // EMPTY_SK_RRECT_TYPE = 0
+               Empty = 0,
+               // RECT_SK_RRECT_TYPE = 1
+               Rect = 1,
+               // OVAL_SK_RRECT_TYPE = 2
+               Oval = 2,
+               // SIMPLE_SK_RRECT_TYPE = 3
+               Simple = 3,
+               // NINE_PATCH_SK_RRECT_TYPE = 4
+               NinePatch = 4,
+               // COMPLEX_SK_RRECT_TYPE = 5
+               Complex = 5,
+       }
+
+       // sk_shader_tilemode_t
+       public enum SKShaderTileMode {
+               // CLAMP_SK_SHADER_TILEMODE = 0
+               Clamp = 0,
+               // REPEAT_SK_SHADER_TILEMODE = 1
+               Repeat = 1,
+               // MIRROR_SK_SHADER_TILEMODE = 2
+               Mirror = 2,
+       }
+
+       // sk_stroke_cap_t
+       public enum SKStrokeCap {
+               // BUTT_SK_STROKE_CAP = 0
+               Butt = 0,
+               // ROUND_SK_STROKE_CAP = 1
+               Round = 1,
+               // SQUARE_SK_STROKE_CAP = 2
+               Square = 2,
+       }
+
+       // sk_stroke_join_t
+       public enum SKStrokeJoin {
+               // MITER_SK_STROKE_JOIN = 0
+               Miter = 0,
+               // ROUND_SK_STROKE_JOIN = 1
+               Round = 1,
+               // BEVEL_SK_STROKE_JOIN = 2
+               Bevel = 2,
+       }
+
+       // sk_surfaceprops_flags_t
+       [Flags]
+       public enum SKSurfacePropsFlags {
+               // NONE_SK_SURFACE_PROPS_FLAGS = 0
+               None = 0,
+               // USE_DEVICE_INDEPENDENT_FONTS_SK_SURFACE_PROPS_FLAGS = 1 << 0
+               UseDeviceIndependentFonts = 1,
+       }
+
+       // sk_text_align_t
+       public enum SKTextAlign {
+               // LEFT_SK_TEXT_ALIGN = 0
+               Left = 0,
+               // CENTER_SK_TEXT_ALIGN = 1
+               Center = 1,
+               // RIGHT_SK_TEXT_ALIGN = 2
+               Right = 2,
+       }
+
+       // sk_text_encoding_t
+       public enum SKTextEncoding {
+               // UTF8_SK_TEXT_ENCODING = 0
+               Utf8 = 0,
+               // UTF16_SK_TEXT_ENCODING = 1
+               Utf16 = 1,
+               // UTF32_SK_TEXT_ENCODING = 2
+               Utf32 = 2,
+               // GLYPH_ID_SK_TEXT_ENCODING = 3
+               GlyphId = 3,
+       }
+
+       // sk_transfer_function_behavior_t
+       public enum SKTransferFunctionBehavior {
+               // RESPECT_SK_TRANSFER_FUNCTION_BEHAVIOR = 0
+               Respect = 0,
+               // IGNORE_SK_TRANSFER_FUNCTION_BEHAVIOR = 1
+               Ignore = 1,
+       }
+
+       // sk_vertices_vertex_mode_t
+       public enum SKVertexMode {
+               // TRIANGLES_SK_VERTICES_VERTEX_MODE = 0
+               Triangles = 0,
+               // TRIANGLE_STRIP_SK_VERTICES_VERTEX_MODE = 1
+               TriangleStrip = 1,
+               // TRIANGLE_FAN_SK_VERTICES_VERTEX_MODE = 2
+               TriangleFan = 2,
+       }
+
+       // sk_webpencoder_compression_t
+       public enum SKWebpEncoderCompression {
+               // LOSSY_SK_WEBPENCODER_COMPTRESSION = 0
+               Lossy = 0,
+               // LOSSLESS_SK_WEBPENCODER_COMPTRESSION = 1
+               Lossless = 1,
+       }
+
+       #endregion
+}
diff --git a/src/XSF/SkiaSharp/Util.cs b/src/XSF/SkiaSharp/Util.cs
new file mode 100644 (file)
index 0000000..4dcaf60
--- /dev/null
@@ -0,0 +1,127 @@
+using System;
+using System.ComponentModel;
+using System.Text;
+
+namespace SkiaSharp
+{
+       internal unsafe static class Utils
+       {
+               internal const float NearlyZero = 1.0f / (1 << 12);
+
+               internal static Span<byte> AsSpan (this IntPtr ptr, int size) =>
+                       new Span<byte> ((void*)ptr, size);
+
+               internal static ReadOnlySpan<byte> AsReadOnlySpan (this IntPtr ptr, int size) =>
+                       new ReadOnlySpan<byte> ((void*)ptr, size);
+
+               internal static bool NearlyEqual (float a, float b, float tolerance) =>
+                       Math.Abs (a - b) <= tolerance;
+
+               internal static byte[] GetBytes (this Encoding encoding, ReadOnlySpan<char> text)
+               {
+                       if (text.Length == 0)
+                               return new byte[0];
+
+                       fixed (char* t = text) {
+                               var byteCount = encoding.GetByteCount (t, text.Length);
+                               if (byteCount == 0)
+                                       return new byte[0];
+
+                               var bytes = new byte[byteCount];
+                               fixed (byte* b = bytes) {
+                                       encoding.GetBytes (t, text.Length, b, byteCount);
+                               }
+                               return bytes;
+                       }
+               }
+       }
+
+       public unsafe static class StringUtilities
+       {
+               // GetUnicodeStringLength
+
+               private static int GetUnicodeStringLength (SKTextEncoding encoding) =>
+                       encoding switch
+                       {
+                               SKTextEncoding.Utf8 => 1,
+                               SKTextEncoding.Utf16 => 1,
+                               SKTextEncoding.Utf32 => 2,
+                               _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported.")
+                       };
+
+               // GetUnicodeCharacterCode
+
+               public static int GetUnicodeCharacterCode (string character, SKTextEncoding encoding)
+               {
+                       if (character == null)
+                               throw new ArgumentNullException (nameof (character));
+                       if (GetUnicodeStringLength (encoding) != character.Length)
+                               throw new ArgumentException (nameof (character), $"Only a single character can be specified.");
+
+                       var bytes = GetEncodedText (character, encoding);
+                       return BitConverter.ToInt32 (bytes, 0);
+               }
+
+               // GetEncodedText
+
+               [EditorBrowsable (EditorBrowsableState.Never)]
+               [Obsolete ("Use GetEncodedText(string, SKTextEncoding) instead.")]
+               public static byte[] GetEncodedText (string text, SKEncoding encoding) =>
+                       GetEncodedText (text.AsSpan (), encoding.ToTextEncoding ());
+
+               public static byte[] GetEncodedText (string text, SKTextEncoding encoding) =>
+                       GetEncodedText (text.AsSpan (), encoding);
+
+               public static byte[] GetEncodedText (ReadOnlySpan<char> text, SKTextEncoding encoding) =>
+                       encoding switch
+                       {
+                               SKTextEncoding.Utf8 => Encoding.UTF8.GetBytes (text),
+                               SKTextEncoding.Utf16 => Encoding.Unicode.GetBytes (text),
+                               SKTextEncoding.Utf32 => Encoding.UTF32.GetBytes (text),
+                               _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
+                       };
+
+               // GetString
+
+               public static string GetString (IntPtr data, int dataLength, SKTextEncoding encoding) =>
+                       GetString (data.AsReadOnlySpan (dataLength), 0, dataLength, encoding);
+
+               public static string GetString (byte[] data, SKTextEncoding encoding) =>
+                       GetString (data, 0, data.Length, encoding);
+
+               public static string GetString (byte[] data, int index, int count, SKTextEncoding encoding)
+               {
+                       if (data == null)
+                               throw new ArgumentNullException (nameof (data));
+
+                       return encoding switch
+                       {
+                               SKTextEncoding.Utf8 => Encoding.UTF8.GetString (data, index, count),
+                               SKTextEncoding.Utf16 => Encoding.Unicode.GetString (data, index, count),
+                               SKTextEncoding.Utf32 => Encoding.UTF32.GetString (data, index, count),
+                               _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
+                       };
+               }
+
+               public static string GetString (ReadOnlySpan<byte> data, SKTextEncoding encoding) =>
+                       GetString (data, 0, data.Length, encoding);
+
+               public static string GetString (ReadOnlySpan<byte> data, int index, int count, SKTextEncoding encoding)
+               {
+                       data = data.Slice (index, count);
+
+                       if (data.Length == 0)
+                               return string.Empty;
+
+                       fixed (byte* bp = data) {
+                               return encoding switch
+                               {
+                                       SKTextEncoding.Utf8 => Encoding.UTF8.GetString (bp, data.Length),
+                                       SKTextEncoding.Utf16 => Encoding.Unicode.GetString (bp, data.Length),
+                                       SKTextEncoding.Utf32 => Encoding.UTF32.GetString (bp, data.Length),
+                                       _ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
+                               };
+                       }
+               }
+       }
+}
index 4448dda..317f478 100644 (file)
@@ -6,10 +6,13 @@
     <AssemblyOriginatorKeyFile>..\XSF.snk</AssemblyOriginatorKeyFile>
     <Deterministic>True</Deterministic>
     <ProduceReferenceAssembly>True</ProduceReferenceAssembly>
+    <LangVersion>8.0</LangVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
   <ItemGroup>
     <PackageReference Include="Tizen.NET" Version="6.0.0.14995" />
+    <PackageReference Include="System.Memory" Version="4.5.3" />
   </ItemGroup>
 
   <ItemGroup>
     <EmbeddedResource Include="Resources\circle.png" />
     <EmbeddedResource Include="Resources\wc_visual_cue.png" />
   </ItemGroup>
-
-  <ItemGroup>
-    <None Include="lib/x86/liblottie-player.so.0">
-      <Pack>true</Pack>
-      <PackagePath>runtimes/linux-x86/native/liblottie-player.so.0</PackagePath>
-    </None>
-  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/XSF/lib/armel/libHarfBuzzSharp.2.6.1.so b/src/XSF/lib/armel/libHarfBuzzSharp.2.6.1.so
new file mode 100755 (executable)
index 0000000..ef177a7
Binary files /dev/null and b/src/XSF/lib/armel/libHarfBuzzSharp.2.6.1.so differ
diff --git a/src/XSF/lib/armel/libSkiaSharp.1.68.2.so b/src/XSF/lib/armel/libSkiaSharp.1.68.2.so
new file mode 100755 (executable)
index 0000000..0a4a7f4
Binary files /dev/null and b/src/XSF/lib/armel/libSkiaSharp.1.68.2.so differ
diff --git a/src/XSF/lib/x86/libHarfBuzzSharp.2.6.1.so b/src/XSF/lib/x86/libHarfBuzzSharp.2.6.1.so
new file mode 100755 (executable)
index 0000000..48a891c
Binary files /dev/null and b/src/XSF/lib/x86/libHarfBuzzSharp.2.6.1.so differ
diff --git a/src/XSF/lib/x86/libSkiaSharp.1.68.2.so b/src/XSF/lib/x86/libSkiaSharp.1.68.2.so
new file mode 100755 (executable)
index 0000000..bbc6a26
Binary files /dev/null and b/src/XSF/lib/x86/libSkiaSharp.1.68.2.so differ