50 changed files with 1684 additions and 1428 deletions
File diff suppressed because it is too large
@ -1,29 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
using Android.App; |
|||
using Android.Content; |
|||
using Android.Graphics; |
|||
using Android.OS; |
|||
using Android.Runtime; |
|||
using Android.Views; |
|||
using Android.Widget; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
class MethodTableImpl : MethodTable |
|||
{ |
|||
[DllImport(@"perspesk")] |
|||
private static extern IntPtr GetPerspexMethodTable(); |
|||
[DllImport(@"perspesk")] |
|||
private static extern IntPtr PerspexJniInit(IntPtr jniEnv); |
|||
|
|||
public MethodTableImpl() : base(GetPerspexMethodTable()) |
|||
{ |
|||
PerspexJniInit(JNIEnv.Handle); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="monoandroid60" /> |
|||
</packages> |
|||
@ -1,235 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
|
|||
//Library loaders were taken from https://github.com/kekekeks/evhttp-sharp/tree/master/EvHttpSharp/Interop (MIT licensed)
|
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
class MethodTableImpl : MethodTable |
|||
{ |
|||
public MethodTableImpl() : base(GetMethodTable()) |
|||
{ |
|||
} |
|||
|
|||
class DetectedPlatformInfo |
|||
{ |
|||
public DetectedPlatformInfo(IDynLoader loader, string name, string cpuArch, string dllExtension) |
|||
{ |
|||
Loader = loader; |
|||
Name = name; |
|||
CpuArch = cpuArch; |
|||
DllExtension = dllExtension; |
|||
} |
|||
|
|||
public IDynLoader Loader { get; } |
|||
public string Name { get; } |
|||
public string CpuArch { get;} |
|||
public string DllExtension { get; } |
|||
} |
|||
|
|||
static DetectedPlatformInfo DetectPlatform() |
|||
{ |
|||
var macInfo = new DetectedPlatformInfo(new OsxLoader(), "Darwin", IntPtr.Size == 4 ? "i686" : "x86_64", ".dylib"); |
|||
switch (Environment.OSVersion.Platform) |
|||
{ |
|||
case PlatformID.Unix: |
|||
// Well, there are chances MacOSX is reported as Unix instead of MacOSX.
|
|||
// Instead of platform check, we'll do a feature checks (Mac specific root folders)
|
|||
if (Directory.Exists("/Applications") |
|||
& Directory.Exists("/System") |
|||
& Directory.Exists("/Users") |
|||
& Directory.Exists("/Volumes")) |
|||
return macInfo; |
|||
else |
|||
{ |
|||
string cpuArch; |
|||
using (var proc = |
|||
Process.Start( |
|||
(new ProcessStartInfo("uname", "-p") |
|||
{ |
|||
UseShellExecute = false, |
|||
RedirectStandardOutput = true |
|||
}))) |
|||
{ |
|||
proc.WaitForExit(); |
|||
cpuArch = proc.StandardOutput.ReadToEnd().Trim(); |
|||
} |
|||
return new DetectedPlatformInfo(new LinuxLoader(), "Linux", cpuArch, ".so"); |
|||
} |
|||
|
|||
case PlatformID.MacOSX: |
|||
return macInfo; |
|||
|
|||
default: |
|||
return new DetectedPlatformInfo(new Win32Loader(), "Windows", IntPtr.Size == 4 ? "i686" : "x86_64", ".dll"); |
|||
} |
|||
} |
|||
|
|||
static IEnumerable<string> GeneratePossiblePaths() |
|||
{ |
|||
foreach ( |
|||
var basePath in |
|||
new[] {Assembly.GetEntryAssembly(), typeof (MethodTable).Assembly}.Select( |
|||
a => Path.GetDirectoryName(a?.GetModules()?[0]?.FullyQualifiedName)) |
|||
.Concat(new[] { Directory.GetCurrentDirectory()})) |
|||
{ |
|||
if(basePath == null) |
|||
continue; |
|||
yield return Path.Combine(basePath); |
|||
yield return Path.Combine(basePath, "native"); |
|||
yield return Path.Combine(basePath, "lib"); |
|||
yield return Path.Combine(basePath, "lib", "native"); |
|||
} |
|||
} |
|||
|
|||
static IntPtr GetMethodTable() |
|||
{ |
|||
var plat = DetectPlatform(); |
|||
|
|||
var name = "libperspesk" + plat.DllExtension; |
|||
var paths = GeneratePossiblePaths().Select(p => Path.Combine(p, plat.Name, plat.CpuArch, name)).ToList(); |
|||
var dll = paths.FirstOrDefault(File.Exists); |
|||
var msg = "Unable to find native library in following paths: '" + |
|||
string.Join("', '", paths) + "'"; |
|||
Console.Error.WriteLine(msg); |
|||
if (dll == null) |
|||
throw new FileNotFoundException(msg); |
|||
IntPtr hLib = plat.Loader.LoadLibrary(dll); |
|||
IntPtr pGetMethodTable = plat.Loader.GetProcAddress(hLib, "GetPerspexMethodTable"); |
|||
var getTable = (GetPerspexMethodTableDelegate) |
|||
Marshal.GetDelegateForFunctionPointer(pGetMethodTable, typeof (GetPerspexMethodTableDelegate)); |
|||
return getTable(); |
|||
} |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
delegate IntPtr GetPerspexMethodTableDelegate(); |
|||
|
|||
|
|||
internal interface IDynLoader |
|||
{ |
|||
IntPtr LoadLibrary(string dll); |
|||
IntPtr GetProcAddress(IntPtr dll, string proc); |
|||
|
|||
} |
|||
|
|||
class LinuxLoader : IDynLoader |
|||
{ |
|||
// ReSharper disable InconsistentNaming
|
|||
[DllImport("libdl.so.2")] |
|||
private static extern IntPtr dlopen(string path, int flags); |
|||
|
|||
[DllImport("libdl.so.2")] |
|||
private static extern IntPtr dlsym(IntPtr handle, string symbol); |
|||
|
|||
[DllImport("libdl.so.2")] |
|||
private static extern IntPtr dlerror(); |
|||
|
|||
// ReSharper restore InconsistentNaming
|
|||
|
|||
static string DlError() |
|||
{ |
|||
return Marshal.PtrToStringAuto(dlerror()); |
|||
} |
|||
|
|||
public IntPtr LoadLibrary(string dll) |
|||
{ |
|||
var handle = dlopen(dll, 1); |
|||
if (handle == IntPtr.Zero) |
|||
throw new Win32Exception(DlError()); |
|||
return handle; |
|||
} |
|||
|
|||
public IntPtr GetProcAddress(IntPtr dll, string proc) |
|||
{ |
|||
var ptr = dlsym(dll, proc); |
|||
if (ptr == IntPtr.Zero) |
|||
throw new Win32Exception(DlError()); |
|||
return ptr; |
|||
} |
|||
} |
|||
|
|||
internal class Win32Loader : IDynLoader |
|||
{ |
|||
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] |
|||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); |
|||
|
|||
[DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] |
|||
private static extern IntPtr LoadLibrary(string lpszLib); |
|||
|
|||
[DllImport("kernel32", EntryPoint = "SetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)] |
|||
extern static bool SetDllDirectory(string lpPathName); |
|||
|
|||
[DllImport("kernel32", EntryPoint = "GetDllDirectoryW", SetLastError = true, CharSet = CharSet.Unicode)] |
|||
extern static int GetDllDirectory(int nBufferLength, char[] lpBuffer); |
|||
|
|||
IntPtr IDynLoader.LoadLibrary(string dll) |
|||
{ |
|||
var buffer = new char[2048]; |
|||
int oldLen = GetDllDirectory(buffer.Length, buffer); |
|||
var oldDir = new string(buffer, 0, oldLen); |
|||
|
|||
SetDllDirectory(Path.GetDirectoryName(dll)); |
|||
|
|||
var handle = LoadLibrary(dll); |
|||
if (handle == IntPtr.Zero) |
|||
throw new Win32Exception(Marshal.GetLastWin32Error()); |
|||
SetDllDirectory(oldDir); |
|||
|
|||
return handle; |
|||
} |
|||
|
|||
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc) |
|||
{ |
|||
var ptr = GetProcAddress(dll, proc); |
|||
if (ptr == IntPtr.Zero) |
|||
throw new Win32Exception(Marshal.GetLastWin32Error()); |
|||
return ptr; |
|||
} |
|||
} |
|||
|
|||
class OsxLoader : IDynLoader |
|||
{ |
|||
// ReSharper disable InconsistentNaming
|
|||
[DllImport("/usr/lib/libSystem.dylib")] |
|||
public static extern IntPtr dlopen(string path, int mode); |
|||
|
|||
[DllImport("/usr/lib/libSystem.dylib")] |
|||
private static extern IntPtr dlsym(IntPtr handle, string symbol); |
|||
|
|||
[DllImport("/usr/lib/libSystem.dylib")] |
|||
private static extern IntPtr dlerror(); |
|||
|
|||
// ReSharper restore InconsistentNaming
|
|||
|
|||
static string DlError() |
|||
{ |
|||
return Marshal.PtrToStringAuto(dlerror()); |
|||
} |
|||
|
|||
public IntPtr LoadLibrary(string dll) |
|||
{ |
|||
var handle = dlopen(dll, 1); |
|||
if (handle == IntPtr.Zero) |
|||
throw new Win32Exception(DlError()); |
|||
return handle; |
|||
} |
|||
|
|||
public IntPtr GetProcAddress(IntPtr dll, string proc) |
|||
{ |
|||
var ptr = dlsym(dll, proc); |
|||
if (ptr == IntPtr.Zero) |
|||
throw new Win32Exception(DlError()); |
|||
return ptr; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="net45" /> |
|||
</packages> |
|||
@ -1,19 +0,0 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using ObjCRuntime; |
|||
|
|||
[assembly: LinkWith("Perspex.Skia.iOS.libperspesk_standalone.a", ForceLoad = true, SmartLink = true, IsCxx = true, Frameworks = "ImageIO MobileCoreServices CoreText")] |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
class MethodTableImpl : MethodTable |
|||
{ |
|||
[DllImport("__Internal")] |
|||
static extern IntPtr GetPerspexMethodTable(); |
|||
|
|||
public MethodTableImpl() : base(GetPerspexMethodTable()) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="SkiaSharp" version="1.49.2.0-beta" targetFramework="xamarinios10" /> |
|||
</packages> |
|||
@ -1,238 +0,0 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Perspex.Media; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
unsafe abstract class MethodTable |
|||
{ |
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateWindowRenderTarget(IntPtr nativeHandle); |
|||
|
|||
public _CreateWindowRenderTarget CreateWindowRenderTarget; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _RenderTargetCreateRenderingContext(IntPtr target); |
|||
|
|||
public _RenderTargetCreateRenderingContext RenderTargetCreateRenderingContext; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeRenderTarget(IntPtr target); |
|||
|
|||
public _DisposeRenderTarget DisposeRenderTarget; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeRenderingContext(IntPtr ctx); |
|||
|
|||
public _DisposeRenderingContext DisposeRenderingContext; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawRectangle(IntPtr ctx, void* brush, ref SkRect rect, float borderRadius); |
|||
|
|||
public _DrawRectangle DrawRectangle; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _PushClip(IntPtr ctx, ref SkRect rect); |
|||
|
|||
public _PushClip PushClip; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _PopClip(IntPtr ctx); |
|||
|
|||
public _PopClip PopClip; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _SetTransform(IntPtr ctx, void* matrix6); |
|||
|
|||
public _SetTransform SetTransformNative; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawLine(IntPtr ctx, void* brush, float x1, float y1, float x2, float y2); |
|||
|
|||
public _DrawLine DrawLine; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreatePath(SkiaGeometryElement[] elements, int count, out SkRect bounds); |
|||
|
|||
public _CreatePath CreatePath; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposePath(IntPtr handle); |
|||
|
|||
public _DisposePath DisposePath; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _TransformPath(IntPtr path, void* matrix6); |
|||
|
|||
public _TransformPath TransformPathNative; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawGeometry(IntPtr ctx, IntPtr path, void* fill, void* stroke, bool useEvenOdd); |
|||
|
|||
public _DrawGeometry DrawGeometry; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DestroySkData(IntPtr handle); |
|||
|
|||
public _DestroySkData DestroySkData; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate bool _LoadImage(byte[] data, int len, out IntPtr image, out int width, out int height); |
|||
|
|||
public _LoadImage LoadImage; |
|||
|
|||
public enum SkiaImageType |
|||
{ |
|||
Png,Gif,Jpeg |
|||
} |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _SaveImage(IntPtr image, SkiaImageType type, int quality); |
|||
|
|||
public _SaveImage SaveImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawImage(IntPtr ctx, IntPtr image, float opacity, ref SkRect srcRect, ref SkRect destRect); |
|||
|
|||
public _DrawImage DrawImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateRenderTargetBitmap(int width, int height); |
|||
|
|||
public _CreateRenderTargetBitmap CreateRenderTargetBitmap; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeImage(IntPtr image); |
|||
|
|||
public _DisposeImage DisposeImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate int _GetSkDataSize(IntPtr data); |
|||
|
|||
public _GetSkDataSize GetSkDataSize; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _ReadSkData(IntPtr data, byte[] buffer, int count); |
|||
|
|||
public _ReadSkData ReadSkData; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate NativeDrawingContextSettings* _GetDrawingContextSettingsPtr(IntPtr ctx); |
|||
|
|||
public _GetDrawingContextSettingsPtr GetDrawingContextSettingsPtr; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateTypeface(void* name, int style); |
|||
|
|||
public _CreateTypeface CreateTypeface; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateFormattedText(void*utf16, int len, IntPtr typeface, float fontSize, TextAlignment align, NativeFormattedText** shared); |
|||
|
|||
public _CreateFormattedText CreateFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _RebuildFormattedText(IntPtr handle); |
|||
|
|||
public _RebuildFormattedText RebuildFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DestroyFormattedText(IntPtr handle); |
|||
|
|||
public _DestroyFormattedText DestroyFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawFormattedText(IntPtr ctx, void* brush, IntPtr text, float x, float y); |
|||
|
|||
public _DrawFormattedText DrawFormattedText; |
|||
|
|||
public enum Option |
|||
{ |
|||
ForceSoftware = 0 |
|||
} |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _SetOption(Option option, IntPtr value); |
|||
|
|||
public _SetOption SetOption; |
|||
|
|||
private static readonly Type[] TableOrder = new Type[] |
|||
{ |
|||
typeof (_CreateWindowRenderTarget), |
|||
typeof (_DisposeRenderTarget), |
|||
typeof (_RenderTargetCreateRenderingContext), |
|||
typeof (_DisposeRenderingContext), |
|||
typeof (_DrawRectangle), |
|||
typeof (_PushClip), |
|||
typeof (_PopClip), |
|||
typeof (_SetTransform), |
|||
typeof (_DrawLine), |
|||
typeof (_CreatePath), |
|||
typeof (_DisposePath), |
|||
typeof (_DrawGeometry), |
|||
typeof (_GetSkDataSize), |
|||
typeof (_ReadSkData), |
|||
typeof (_DestroySkData), |
|||
typeof (_LoadImage), |
|||
typeof (_SaveImage), |
|||
typeof (_DrawImage), |
|||
typeof (_CreateRenderTargetBitmap), |
|||
typeof (_DisposeImage), |
|||
typeof (_GetDrawingContextSettingsPtr), |
|||
typeof (_CreateTypeface), |
|||
typeof (_CreateFormattedText), |
|||
typeof (_RebuildFormattedText), |
|||
typeof (_DestroyFormattedText), |
|||
typeof (_DrawFormattedText), |
|||
typeof (_SetOption), |
|||
typeof (_TransformPath) |
|||
}; |
|||
|
|||
void ConvertMatrix(Matrix value, float* target) |
|||
{ |
|||
target[0] = (float)value.M11; |
|||
target[1] = (float)value.M21; |
|||
target[2] = (float)value.M31; |
|||
|
|||
target[3] = (float)value.M12; |
|||
target[4] = (float)value.M22; |
|||
target[5] = (float)value.M32; |
|||
} |
|||
|
|||
public unsafe IntPtr TransformPath(IntPtr path, Matrix matrix) |
|||
{ |
|||
var tmp = stackalloc float[6]; |
|||
ConvertMatrix(matrix, tmp); |
|||
return TransformPathNative(path, tmp); |
|||
} |
|||
|
|||
public unsafe void SetTransform(IntPtr ctx, Matrix matrix) |
|||
{ |
|||
var tmp = stackalloc float[6]; |
|||
ConvertMatrix(matrix, tmp); |
|||
SetTransformNative(ctx, tmp); |
|||
} |
|||
|
|||
protected MethodTable(IntPtr methodTable) |
|||
{ |
|||
var dic = typeof (MethodTable).GetFields().ToDictionary(f => f.FieldType, f => f); |
|||
|
|||
for (var c = 0; c < TableOrder.Length; c++) |
|||
{ |
|||
IntPtr pMethod = Marshal.ReadIntPtr(methodTable, IntPtr.Size*c); |
|||
var t = TableOrder[c]; |
|||
dic[t].SetValue(this, Marshal.GetDelegateForFunctionPointer(pMethod, t)); |
|||
} |
|||
} |
|||
|
|||
public static readonly MethodTable Instance = new MethodTableImpl(); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -1,113 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
internal enum NativeBrushType |
|||
{ |
|||
Solid, |
|||
LinearGradient, |
|||
RadialGradient, |
|||
Image |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
internal unsafe struct NativeBrush |
|||
{ |
|||
public const int MaxGradientStops = 1024; |
|||
public const int MaxDashCount = 1024; |
|||
public NativeBrushType Type; |
|||
public double Opacity; |
|||
public uint Color; |
|||
|
|||
//Strokes
|
|||
public bool Stroke; |
|||
public float StrokeThickness; |
|||
public PenLineJoin StrokeLineJoin; |
|||
public float StrokeMiterLimit; |
|||
public int StrokeDashCount; |
|||
public float StrokeDashOffset; |
|||
public PenLineCap StrokeLineCap; |
|||
|
|||
|
|||
//Gradients
|
|||
public int GradientStopCount; |
|||
public GradientSpreadMethod GradientSpreadMethod; |
|||
public SkiaPoint GradientStartPoint, GradientEndPoint; |
|||
public float GradientRadius; |
|||
|
|||
//Image Brush
|
|||
public IntPtr Bitmap; |
|||
public TileMode BitmapTileMode; |
|||
public SkiaPoint BitmapTranslation; |
|||
|
|||
//Blobs
|
|||
public fixed uint GradientStopColors [MaxGradientStops]; |
|||
public fixed float GradientStops [MaxGradientStops]; |
|||
public fixed float StrokeDashes [MaxDashCount]; |
|||
|
|||
public void Reset() |
|||
{ |
|||
Type = NativeBrushType.Solid; |
|||
Opacity = 1f; |
|||
Color = 0; |
|||
Stroke = false; |
|||
StrokeThickness = 1; |
|||
GradientStopCount = 0; |
|||
StrokeDashCount = 0; |
|||
StrokeLineCap = PenLineCap.Flat; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
unsafe class NativeBrushContainer : IDisposable |
|||
{ |
|||
private readonly NativeBrushPool _pool; |
|||
public NativeBrush* Brush; |
|||
|
|||
readonly List<IDisposable> _disposables = new List<IDisposable>(); |
|||
|
|||
public NativeBrushContainer(NativeBrushPool pool) |
|||
{ |
|||
_pool = pool; |
|||
Brush = (NativeBrush*) Marshal.AllocHGlobal(Marshal.SizeOf(typeof (NativeBrush))).ToPointer(); |
|||
Brush->Reset(); |
|||
} |
|||
|
|||
public void AddDisposable(IDisposable disp) |
|||
{ |
|||
_disposables.Add(disp); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
foreach (var disp in _disposables) |
|||
disp.Dispose(); |
|||
_disposables.Clear(); |
|||
Brush->Reset(); |
|||
_pool?.Return(this); |
|||
} |
|||
} |
|||
|
|||
class NativeBrushPool |
|||
{ |
|||
public static NativeBrushPool Instance { get; } = new NativeBrushPool(); |
|||
readonly Stack<NativeBrushContainer> _pool = new Stack<NativeBrushContainer>(); |
|||
|
|||
public void Return(NativeBrushContainer c) |
|||
{ |
|||
_pool.Push(c); |
|||
} |
|||
|
|||
public NativeBrushContainer Get() |
|||
{ |
|||
if (_pool.Count == 0) |
|||
return new NativeBrushContainer(this); |
|||
return _pool.Pop(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct NativeDrawingContextSettings |
|||
{ |
|||
public double Opacity; |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
unsafe struct NativeFormattedText |
|||
{ |
|||
public float WidthConstraint; |
|||
public int LineCount; |
|||
public NativeFormattedTextLine* Lines; |
|||
public SkRect* Bounds; |
|||
}; |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
unsafe struct NativeFormattedTextLine |
|||
{ |
|||
public float Top; |
|||
public int Start; |
|||
public int Length; |
|||
public float Height; |
|||
public float Width; |
|||
}; |
|||
} |
|||
@ -1,106 +0,0 @@ |
|||
using System; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
abstract class PerspexHandleHolder : IDisposable |
|||
{ |
|||
private readonly IntPtr _handle; |
|||
|
|||
public IntPtr Handle |
|||
{ |
|||
get |
|||
{ |
|||
CheckDisposed(); |
|||
return _handle; |
|||
} |
|||
} |
|||
|
|||
public bool IsDisposed { get; private set; } |
|||
|
|||
public void CheckDisposed() |
|||
{ |
|||
if (IsDisposed) |
|||
throw new ObjectDisposedException(GetType().FullName); |
|||
} |
|||
|
|||
protected PerspexHandleHolder(IntPtr handle) |
|||
{ |
|||
_handle = handle; |
|||
} |
|||
|
|||
protected abstract void Delete(IntPtr handle); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if(IsDisposed) |
|||
return; |
|||
IsDisposed = true; |
|||
Delete(_handle); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~PerspexHandleHolder() |
|||
{ |
|||
Dispose(); |
|||
} |
|||
} |
|||
|
|||
class RefCountable<T> : IDisposable where T : PerspexHandleHolder |
|||
{ |
|||
class Shared |
|||
{ |
|||
public readonly T Target; |
|||
private int _refCount = 1; |
|||
|
|||
public Shared(T target) |
|||
{ |
|||
Target = target; |
|||
} |
|||
|
|||
public void AddRef() => _refCount++; |
|||
public void Release() |
|||
{ |
|||
_refCount--; |
|||
if (_refCount <= 0) |
|||
Target.Dispose(); |
|||
} |
|||
} |
|||
|
|||
public bool IsDisposed => _shared == null; |
|||
private Shared _shared; |
|||
public void CheckDisposed() |
|||
{ |
|||
if (IsDisposed) |
|||
throw new ObjectDisposedException(GetType().FullName); |
|||
} |
|||
|
|||
public IntPtr Handle |
|||
{ |
|||
get |
|||
{ |
|||
CheckDisposed(); |
|||
return _shared.Target.Handle; |
|||
} |
|||
} |
|||
|
|||
public RefCountable(T handle) |
|||
{ |
|||
_shared = new Shared(handle); |
|||
} |
|||
|
|||
public RefCountable(RefCountable<T> other) |
|||
{ |
|||
other._shared.Target.CheckDisposed(); |
|||
other._shared.AddRef(); |
|||
_shared = other._shared; |
|||
} |
|||
|
|||
public RefCountable<T> Clone() => new RefCountable<T>(this); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_shared?.Release(); |
|||
_shared = null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct SkRect |
|||
{ |
|||
public float Left, Top, Right, Bottom; |
|||
|
|||
public static SkRect FromRect(Rect rc) |
|||
{ |
|||
return new SkRect() |
|||
{ |
|||
Left = (float) rc.X, |
|||
Top = (float) rc.Y, |
|||
Right = (float) rc.Right, |
|||
Bottom = (float) rc.Bottom |
|||
}; |
|||
} |
|||
|
|||
public Rect ToRect() => new Rect(Left, Top, Right - Left, Bottom - Top); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct SkiaPoint |
|||
{ |
|||
public float X, Y; |
|||
|
|||
public SkiaPoint(float x, float y) |
|||
{ |
|||
X = x; |
|||
Y = y; |
|||
} |
|||
|
|||
public SkiaPoint(double x, double y) |
|||
{ |
|||
X = (float)x; |
|||
Y = (float)y; |
|||
} |
|||
|
|||
public SkiaPoint(Point p) : this(p.X, p.Y) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public static implicit operator SkiaPoint(Point pt) => new SkiaPoint(pt); |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using Perspex.Media; |
|||
using SkiaSharp; |
|||
|
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
public static class SkiaSharpExtensions |
|||
{ |
|||
public static SKPoint ToSKPoint(this Point p) |
|||
{ |
|||
return new SKPoint((float)p.X, (float)p.Y); |
|||
} |
|||
|
|||
public static SKRect ToSKRect(this Rect r) |
|||
{ |
|||
return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom); |
|||
} |
|||
|
|||
public static Rect ToPerspexRect(this SKRect r) |
|||
{ |
|||
return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top); |
|||
} |
|||
|
|||
public static SKMatrix ToSKMatrix(this Matrix m) |
|||
{ |
|||
var sm = new SKMatrix |
|||
{ |
|||
ScaleX = (float)m.M11, |
|||
SkewX = (float)m.M21, |
|||
TransX = (float)m.M31, |
|||
SkewY = (float)m.M12, |
|||
ScaleY = (float)m.M22, |
|||
TransY = (float)m.M32, |
|||
Persp0 = 0, |
|||
Persp1 = 0, |
|||
Persp2 = 1 |
|||
}; |
|||
|
|||
return sm; |
|||
} |
|||
|
|||
public static SKColor ToSKColor(this Media.Color c) |
|||
{ |
|||
return new SKColor(c.R, c.G, c.B, c.A); |
|||
} |
|||
|
|||
public static SKShaderTileMode ToSKShaderTileMode(this Media.GradientSpreadMethod m) |
|||
{ |
|||
switch (m) |
|||
{ |
|||
default: |
|||
case Media.GradientSpreadMethod.Pad: return SKShaderTileMode.Clamp; |
|||
case Media.GradientSpreadMethod.Reflect: return SKShaderTileMode.Mirror; |
|||
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat; |
|||
} |
|||
} |
|||
|
|||
public static SKTextAlign ToSKTextAlign(this TextAlignment a) |
|||
{ |
|||
switch (a) |
|||
{ |
|||
default: |
|||
case TextAlignment.Left: return SKTextAlign.Left; |
|||
case TextAlignment.Center: return SKTextAlign.Center; |
|||
case TextAlignment.Right: return SKTextAlign.Right; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
// not sure we need this yet
|
|||
internal class WindowDrawingContextImpl : DrawingContextImpl |
|||
{ |
|||
WindowRenderTarget _target; |
|||
|
|||
public WindowDrawingContextImpl(WindowRenderTarget target) |
|||
: base(target.Surface.Canvas) |
|||
{ |
|||
_target = target; |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
base.Dispose(); |
|||
_target.Present(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
TODO: |
|||
|
|||
BitmapImpl |
|||
- constructor from Width/Height |
|||
- Save |
|||
|
|||
DrawingContextImpl |
|||
- DrawRoundRect is not properly implemented due to lack of support in SkiaSharp |
|||
- Alpha support missing as SkiaSharp does not expose this |
|||
- Gradient Shader caching? |
|||
- TileBrushes |
|||
- Pen Dash styles |
|||
|
|||
Formatted Text Rendering |
|||
- minor polish |
|||
|
|||
RenderTarget |
|||
- Figure out a cleaner implementation across all platforms |
|||
- HW acceleration |
|||
|
|||
StreamGeometry |
|||
- Paths within Paths may not work right |
|||
- Paths cannot be Cloned (lack of SkiaSupport) |
|||
- Paths cannot be transformed (lack of SkiaSupport) |
|||
- ArcTo |
|||
|
|||
App Bootstrapping |
|||
- Cleanup the testapplications across all platforms |
|||
- Add a cleaner Fluent API for the subsystems |
|||
- ie. app.UseDirect2D() (via platform specific extension methods) |
|||
|
|||
Android |
|||
- Not tested at all yet |
|||
|
|||
iOS |
|||
- Get GLView working again. See HW above |
|||
|
|||
Win32 |
|||
- Cleanup the unmanaged methods (BITMAPINFO) if possible |
|||
|
|||
General |
|||
- Cleanup/eliminate obsolete files |
|||
- Finish cleanup of the many Test Applications |
|||
- Get Skia Unit Tests passing |
|||
|
|||
|
|||
@ -1,75 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Perspex.Controls.Platform; |
|||
using Perspex.Input; |
|||
using Perspex.Input.Platform; |
|||
using Perspex.Platform; |
|||
using Perspex.Shared.PlatformSupport; |
|||
using Perspex.Skia; |
|||
using UIKit; |
|||
|
|||
namespace Perspex.iOS |
|||
{ |
|||
public class PerspexAppDelegate : UIApplicationDelegate |
|||
{ |
|||
static bool _initialized = false; |
|||
internal static MouseDevice MouseDevice; |
|||
internal static KeyboardDevice KeyboardDevice; |
|||
|
|||
protected void InitPerspex(Type appType) |
|||
{ |
|||
if(_initialized) |
|||
return; |
|||
_initialized = true; |
|||
|
|||
var window = new UIWindow(UIScreen.MainScreen.Bounds); |
|||
var controller = new PerspexViewController(window); |
|||
window.RootViewController = controller; |
|||
window.MakeKeyAndVisible(); |
|||
|
|||
Application.RegisterPlatformCallback(() => |
|||
{ |
|||
MouseDevice = new MouseDevice(); |
|||
KeyboardDevice = new KeyboardDevice(); |
|||
SharedPlatform.Register(appType.Assembly); |
|||
PerspexLocator.CurrentMutable |
|||
.Bind<IClipboard>().ToTransient<Clipboard>() |
|||
//.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
|
|||
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>() |
|||
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice) |
|||
.Bind<IMouseDevice>().ToConstant(MouseDevice) |
|||
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>() |
|||
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance) |
|||
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatform(controller.PerspexView)); |
|||
SkiaPlatform.Initialize(); |
|||
}); |
|||
} |
|||
|
|||
class WindowingPlatform : IWindowingPlatform |
|||
{ |
|||
private readonly IWindowImpl _window; |
|||
|
|||
public WindowingPlatform(IWindowImpl window) |
|||
{ |
|||
_window = window; |
|||
} |
|||
|
|||
public IWindowImpl CreateWindow() |
|||
{ |
|||
return _window; |
|||
} |
|||
|
|||
public IWindowImpl CreateEmbeddableWindow() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IPopupImpl CreatePopup() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using Perspex.Platform; |
|||
using System; |
|||
|
|||
namespace Perspex.iOS |
|||
{ |
|||
// This is somewhat generic, could probably put this elsewhere. But I don't think
|
|||
// it should part of the iOS App Delegate
|
|||
//
|
|||
class WindowingPlatformImpl : IWindowingPlatform |
|||
{ |
|||
private readonly IWindowImpl _window; |
|||
|
|||
public WindowingPlatformImpl(IWindowImpl window) |
|||
{ |
|||
_window = window; |
|||
} |
|||
|
|||
public IWindowImpl CreateWindow() |
|||
{ |
|||
return _window; |
|||
} |
|||
|
|||
public IWindowImpl CreateEmbeddableWindow() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IPopupImpl CreatePopup() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using Perspex.Input; |
|||
using Perspex.Input.Platform; |
|||
using Perspex.iOS; |
|||
using Perspex.Platform; |
|||
using Perspex.Shared.PlatformSupport; |
|||
using Perspex.Skia; |
|||
using UIKit; |
|||
|
|||
namespace Perspex |
|||
{ |
|||
public static class iOSApplicationExtensions |
|||
{ |
|||
public static AppT UseiOS<AppT>(this AppT app) where AppT : Application |
|||
{ |
|||
Perspex.iOS.iOSPlatform.Initialize(); |
|||
return app; |
|||
} |
|||
|
|||
// TODO: Can we merge this with UseSkia somehow once HW/platform cleanup is done?
|
|||
public static AppT UseSkiaViewHost<AppT>(this AppT app) where AppT : Application |
|||
{ |
|||
var window = new UIWindow(UIScreen.MainScreen.Bounds); |
|||
var controller = new PerspexViewController(window); |
|||
window.RootViewController = controller; |
|||
window.MakeKeyAndVisible(); |
|||
|
|||
PerspexLocator.CurrentMutable |
|||
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformImpl(controller.PerspexView)); |
|||
|
|||
SkiaPlatform.Initialize(); |
|||
|
|||
return app; |
|||
} |
|||
|
|||
public static AppT UseAssetAssembly<AppT>(this AppT app, Assembly assembly) where AppT : Application |
|||
{ |
|||
// Asset loading searches our own assembly?
|
|||
var loader = new AssetLoader(assembly); |
|||
PerspexLocator.CurrentMutable.Bind<IAssetLoader>().ToConstant(loader); |
|||
return app; |
|||
} |
|||
} |
|||
} |
|||
|
|||
namespace Perspex.iOS |
|||
{ |
|||
// TODO: Perhaps we should make this class handle all these interfaces directly, like we
|
|||
// do for Win32 and Gtk platforms
|
|||
//
|
|||
public class iOSPlatform //: IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform
|
|||
{ |
|||
internal static MouseDevice MouseDevice; |
|||
internal static KeyboardDevice KeyboardDevice; |
|||
|
|||
public static void Initialize() |
|||
{ |
|||
MouseDevice = new MouseDevice(); |
|||
KeyboardDevice = new KeyboardDevice(); |
|||
|
|||
PerspexLocator.CurrentMutable |
|||
.Bind<IPclPlatformWrapper>().ToSingleton<PclPlatformWrapper>() |
|||
.Bind<IClipboard>().ToTransient<Clipboard>() |
|||
// TODO: what does this look like for iOS??
|
|||
//.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
|
|||
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>() |
|||
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice) |
|||
.Bind<IMouseDevice>().ToConstant(MouseDevice) |
|||
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>() |
|||
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance); |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +1,38 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>CFBundleDisplayName</key> |
|||
<string>Perspex.iOSTestApplication</string> |
|||
<key>CFBundleIdentifier</key> |
|||
<string>com.your-company.Perspex.iOSTestApplication</string> |
|||
<key>CFBundleShortVersionString</key> |
|||
<string>1.0</string> |
|||
<key>CFBundleVersion</key> |
|||
<string>1.0</string> |
|||
<key>UIDeviceFamily</key> |
|||
<array> |
|||
<integer>1</integer> |
|||
<integer>2</integer> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations~ipad</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationPortraitUpsideDown</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>MinimumOSVersion</key> |
|||
<string>7.0</string> |
|||
</dict> |
|||
</plist> |
|||
<dict> |
|||
<key>CFBundleDisplayName</key> |
|||
<string>Perspex.iOSTestApplication</string> |
|||
<key>CFBundleIdentifier</key> |
|||
<string>com.your-company.Perspex.iOSTestApplication</string> |
|||
<key>CFBundleShortVersionString</key> |
|||
<string>1.0</string> |
|||
<key>CFBundleVersion</key> |
|||
<string>1.0</string> |
|||
<key>UIDeviceFamily</key> |
|||
<array> |
|||
<integer>1</integer> |
|||
<integer>2</integer> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>UISupportedInterfaceOrientations~ipad</key> |
|||
<array> |
|||
<string>UIInterfaceOrientationPortrait</string> |
|||
<string>UIInterfaceOrientationPortraitUpsideDown</string> |
|||
<string>UIInterfaceOrientationLandscapeLeft</string> |
|||
<string>UIInterfaceOrientationLandscapeRight</string> |
|||
</array> |
|||
<key>MinimumOSVersion</key> |
|||
<string>8.0</string> |
|||
<key>CFBundleIconFiles</key> |
|||
<array> |
|||
<string>Default-568h@2x.png</string> |
|||
</array> |
|||
</dict> |
|||
</plist> |
|||
|
|||
Loading…
Reference in new issue