2016-02-02 01:19:33 +03:00
|
|
|
//
|
|
|
|
// Bindings for SKBitmap
|
|
|
|
//
|
|
|
|
// Author:
|
|
|
|
// Matthew Leibowitz
|
|
|
|
//
|
|
|
|
// Copyright 2016 Xamarin Inc
|
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics;
|
2016-04-15 01:21:56 +03:00
|
|
|
using System.Linq;
|
2016-02-02 01:19:33 +03:00
|
|
|
using System.Reflection;
|
2017-02-12 03:37:27 +03:00
|
|
|
using System.Runtime.InteropServices;
|
2017-02-26 01:48:05 +03:00
|
|
|
using System.Threading;
|
2016-02-02 01:19:33 +03:00
|
|
|
|
|
|
|
namespace SkiaSharp
|
|
|
|
{
|
2016-09-14 02:51:17 +03:00
|
|
|
public abstract class SKObject : SKNativeObject
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
|
|
|
private static readonly Dictionary<IntPtr, WeakReference> instances = new Dictionary<IntPtr, WeakReference>();
|
|
|
|
|
2016-06-08 03:58:12 +03:00
|
|
|
private readonly List<SKObject> ownedObjects = new List<SKObject>();
|
2017-02-26 01:48:05 +03:00
|
|
|
private int referenceCount = 0;
|
|
|
|
private bool ownsHandle = false;
|
2016-02-02 01:19:33 +03:00
|
|
|
|
2016-06-03 15:55:39 +03:00
|
|
|
[Preserve]
|
|
|
|
internal SKObject(IntPtr handle, bool owns)
|
2017-02-26 01:48:05 +03:00
|
|
|
: base(handle, false)
|
2016-06-03 15:55:39 +03:00
|
|
|
{
|
|
|
|
OwnsHandle = owns;
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
|
|
|
|
2017-02-26 01:48:05 +03:00
|
|
|
protected bool OwnsHandle
|
|
|
|
{
|
|
|
|
get { return ownsHandle; }
|
|
|
|
private set
|
|
|
|
{
|
|
|
|
if (ownsHandle != value)
|
|
|
|
{
|
|
|
|
ownsHandle = value;
|
|
|
|
if (value)
|
|
|
|
Interlocked.Increment(ref referenceCount);
|
|
|
|
else
|
|
|
|
Interlocked.Decrement(ref referenceCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-03 15:55:39 +03:00
|
|
|
|
2016-09-14 02:51:17 +03:00
|
|
|
public override IntPtr Handle
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
2016-09-14 02:51:17 +03:00
|
|
|
get { return base.Handle; }
|
2016-02-02 01:19:33 +03:00
|
|
|
protected set
|
|
|
|
{
|
2016-09-14 02:51:17 +03:00
|
|
|
base.Handle = value;
|
2016-02-02 01:19:33 +03:00
|
|
|
|
2016-09-14 02:51:17 +03:00
|
|
|
RegisterHandle(Handle, this);
|
|
|
|
}
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
|
|
|
|
2016-09-14 02:51:17 +03:00
|
|
|
protected override void Dispose(bool disposing)
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
2016-06-08 03:58:12 +03:00
|
|
|
lock (ownedObjects)
|
|
|
|
{
|
|
|
|
foreach (var child in ownedObjects)
|
|
|
|
{
|
|
|
|
child.Dispose();
|
|
|
|
}
|
|
|
|
ownedObjects.Clear();
|
|
|
|
}
|
|
|
|
|
2017-02-26 01:48:05 +03:00
|
|
|
var zero = DeregisterHandle(Handle, this);
|
2016-09-14 02:51:17 +03:00
|
|
|
|
|
|
|
base.Dispose(disposing);
|
2017-02-26 01:48:05 +03:00
|
|
|
|
|
|
|
if (zero)
|
|
|
|
Handle = IntPtr.Zero;
|
2016-09-14 02:51:17 +03:00
|
|
|
}
|
2016-06-03 15:55:39 +03:00
|
|
|
|
2016-06-17 23:54:08 +03:00
|
|
|
internal static TSkiaObject GetObject<TSkiaObject>(IntPtr handle, bool owns = true)
|
2016-06-03 15:55:39 +03:00
|
|
|
where TSkiaObject : SKObject
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
|
|
|
if (handle == IntPtr.Zero)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock (instances)
|
|
|
|
{
|
|
|
|
// find any existing managed werappers
|
|
|
|
WeakReference reference;
|
|
|
|
if (instances.TryGetValue(handle, out reference))
|
|
|
|
{
|
|
|
|
var instance = reference.Target as TSkiaObject;
|
2016-09-14 02:51:17 +03:00
|
|
|
if (instance != null && instance.Handle != IntPtr.Zero)
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
2017-02-26 01:48:05 +03:00
|
|
|
if (owns)
|
|
|
|
Interlocked.Increment(ref instance.referenceCount);
|
2016-02-02 01:19:33 +03:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new wrapper
|
|
|
|
// TODO: we could probably cache this
|
|
|
|
var type = typeof(TSkiaObject);
|
2016-04-15 01:21:56 +03:00
|
|
|
var constructor = type.GetTypeInfo().DeclaredConstructors.FirstOrDefault(c =>
|
2016-06-17 23:54:08 +03:00
|
|
|
c.GetParameters().Length == 2 && c.GetParameters()[0].ParameterType == typeof(IntPtr) && c.GetParameters()[1].ParameterType == typeof(bool));
|
2016-02-02 01:19:33 +03:00
|
|
|
if (constructor == null)
|
|
|
|
{
|
2016-06-17 23:54:08 +03:00
|
|
|
throw new MissingMethodException($"No constructor found for {type.FullName}.ctor(System.IntPtr, System.Boolean)");
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
2016-06-17 23:54:08 +03:00
|
|
|
return (TSkiaObject)constructor.Invoke(new object[] { handle, owns });
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
internal static void RegisterHandle(IntPtr handle, SKObject instance)
|
|
|
|
{
|
|
|
|
if (handle == IntPtr.Zero)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock (instances)
|
|
|
|
{
|
|
|
|
// find old references
|
|
|
|
WeakReference reference;
|
|
|
|
if (instances.TryGetValue(handle, out reference))
|
|
|
|
{
|
|
|
|
var shouldReplace =
|
|
|
|
reference == null ||
|
2016-03-05 22:27:54 +03:00
|
|
|
reference.Target == null ||
|
2016-02-02 01:19:33 +03:00
|
|
|
((SKObject)reference.Target).Handle == IntPtr.Zero;
|
|
|
|
|
|
|
|
Debug.WriteLineIf(!shouldReplace, "Not replacing existing, living, managed instance with new object.");
|
|
|
|
|
|
|
|
// replace the old one if it is dead
|
|
|
|
instances[handle] = new WeakReference(instance);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// add a new reference
|
|
|
|
instances.Add(handle, new WeakReference(instance));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-26 01:48:05 +03:00
|
|
|
internal static bool DeregisterHandle(IntPtr handle, SKObject instance)
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
|
|
|
if (handle == IntPtr.Zero)
|
|
|
|
{
|
2017-02-26 01:48:05 +03:00
|
|
|
return false;
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
lock (instances)
|
|
|
|
{
|
|
|
|
// find any references
|
|
|
|
WeakReference reference;
|
2017-02-26 01:48:05 +03:00
|
|
|
if (Interlocked.Decrement(ref instance.referenceCount) <= 0 && instances.TryGetValue(handle, out reference))
|
2016-02-02 01:19:33 +03:00
|
|
|
{
|
|
|
|
// remove it if it is dead or the correct object
|
|
|
|
instances.Remove(handle);
|
2017-02-26 01:48:05 +03:00
|
|
|
return true;
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
|
|
|
}
|
2017-02-26 01:48:05 +03:00
|
|
|
return false;
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
2016-06-08 03:58:12 +03:00
|
|
|
|
2018-02-02 18:46:25 +03:00
|
|
|
/// <summary>
|
|
|
|
/// This object will dispose the specific child object which disposed.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="child">The child object to dispose when the parent is disposed.</param>
|
|
|
|
internal void SetDisposeChild (SKObject child)
|
|
|
|
{
|
|
|
|
lock (ownedObjects) {
|
|
|
|
ownedObjects.Add (child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-11 12:42:56 +03:00
|
|
|
/// <summary>
|
|
|
|
/// This object will take ownership of the specified object.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="obj">The object to own.</param>
|
2018-02-02 18:46:25 +03:00
|
|
|
private void TakeOwnership (SKObject obj)
|
2016-06-08 03:58:12 +03:00
|
|
|
{
|
2018-02-02 18:46:25 +03:00
|
|
|
SetDisposeChild (obj);
|
2016-08-11 12:42:56 +03:00
|
|
|
obj.RevokeOwnership ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// This object will hand ownership over to the specified object.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="owner">The object to give ownership to.</param>
|
2018-02-02 18:46:25 +03:00
|
|
|
internal void RevokeOwnership (SKObject owner = null)
|
2016-08-11 12:42:56 +03:00
|
|
|
{
|
|
|
|
if (owner != null) {
|
|
|
|
owner.TakeOwnership (this);
|
|
|
|
} else {
|
2017-10-25 17:29:51 +03:00
|
|
|
OwnsHandle = false;
|
2016-08-11 12:42:56 +03:00
|
|
|
}
|
2016-06-08 03:58:12 +03:00
|
|
|
}
|
2017-02-12 03:37:27 +03:00
|
|
|
|
|
|
|
internal static int SizeOf <T> ()
|
|
|
|
{
|
|
|
|
#if WINDOWS_UWP || NET_STANDARD
|
2017-02-17 06:36:20 +03:00
|
|
|
return Marshal.SizeOf <T> ();
|
2017-02-12 03:37:27 +03:00
|
|
|
#else
|
2017-02-20 22:25:20 +03:00
|
|
|
return Marshal.SizeOf (typeof (T));
|
2017-02-12 03:37:27 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
internal T PtrToStructure <T> (IntPtr intPtr)
|
|
|
|
{
|
|
|
|
#if WINDOWS_UWP || NET_STANDARD
|
|
|
|
return Marshal.PtrToStructure <T> (intPtr);
|
|
|
|
#else
|
|
|
|
return (T) Marshal.PtrToStructure (intPtr, typeof (T));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-02-17 06:36:20 +03:00
|
|
|
internal 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 T PtrToStructure <T> (IntPtr intPtr, int index)
|
|
|
|
{
|
|
|
|
var size = SizeOf <T> ();
|
|
|
|
var newPtr = new IntPtr (intPtr.ToInt64 () + (index * size));
|
|
|
|
return PtrToStructure <T> (newPtr);
|
|
|
|
}
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|
2016-09-14 02:51:17 +03:00
|
|
|
|
|
|
|
public class SKNativeObject : IDisposable
|
|
|
|
{
|
2017-02-26 01:48:05 +03:00
|
|
|
private readonly bool zero;
|
|
|
|
|
2016-09-14 02:51:17 +03:00
|
|
|
internal SKNativeObject(IntPtr handle)
|
|
|
|
{
|
|
|
|
Handle = handle;
|
2017-02-26 01:48:05 +03:00
|
|
|
zero = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal SKNativeObject(IntPtr handle, bool zero)
|
|
|
|
{
|
|
|
|
Handle = handle;
|
|
|
|
this.zero = zero;
|
2016-09-14 02:51:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
~SKNativeObject()
|
|
|
|
{
|
|
|
|
Dispose(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual IntPtr Handle { get; protected set; }
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
{
|
2017-02-26 01:48:05 +03:00
|
|
|
if (zero)
|
|
|
|
Handle = IntPtr.Zero;
|
2016-09-14 02:51:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
Dispose(true);
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
}
|
|
|
|
}
|
2016-02-02 01:19:33 +03:00
|
|
|
}
|