1 module skia.SKObject;
2 
3 import skia.Definitions;
4 import skia.Exceptions;
5 import skia.IDisposable;
6 import skia.SkiaApi;
7 import std.experimental.logger;
8 
9 import core.atomic;
10 import core.sync.condition;
11 import core.sync.mutex;
12 import core.sync.rwmutex;
13 
14 
15 /**
16  * 
17  */
18 class SKObject : SKNativeObject, ISKReferenceCounted
19 {
20     // private const object locker = new object ();
21 
22 // FIXME: Needing refactor or cleanup -@putao at 2020-12-27T16:44:22+08:00
23 // 
24     // private ConcurrentDictionary<void*, SKObject> ownedObjects;
25     // private ConcurrentDictionary<void*, SKObject> keepAliveObjects;
26     private SKObject[void*] ownedObjects;
27     private SKObject[void*] keepAliveObjects;
28     
29     private Mutex _ownedObjectsMutex;
30     private Mutex _keepAliveObjectsMutex;
31 
32     // shared static this()
33     // {
34     //     // SkiaSharpVersion.CheckNativeLibraryCompatible (true);
35 
36     //     // SKColorSpace.EnsureStaticInstanceAreInitialized ();
37     //     // SKData.EnsureStaticInstanceAreInitialized ();
38     //     // SKFontManager.EnsureStaticInstanceAreInitialized ();
39     //     // SKTypeface.EnsureStaticInstanceAreInitialized ();
40     // }
41 
42     this (void* handle, bool owns)
43     {
44         _ownedObjectsMutex = new Mutex();
45         _keepAliveObjectsMutex = new Mutex();
46         super (handle, owns);
47     }
48 
49     // protected override void Dispose (bool disposing) {
50     //     return super.Dispose (disposing);
51     // }
52 
53     // override void Dispose ()
54     // {
55     //     if (IgnorePublicDispose)
56     //         return;
57 
58     //     DisposeInternal ();
59     // }
60 
61     override void* Handle() {
62         return super.Handle();
63     }
64 
65     override void Handle(void* value) {
66         if (value is null) {
67             DeregisterHandle (Handle, this);
68             super.Handle = value;
69         } else {
70             super.Handle = value;
71             RegisterHandle (Handle, this);
72         }
73     }
74 
75 
76     // SKObject[void*] OwnedObjects() {
77     //     return ownedObjects;
78     // }
79 
80     // SKObject[void*] KeepAliveObjects() {
81     //     return keepAliveObjects;
82     // }
83 
84     void registerOwnedObject(void* handle, SKObject obj) {
85         _ownedObjectsMutex.lock();
86         scope(exit) {
87             _ownedObjectsMutex.unlock();
88         }
89 
90         ownedObjects[handle] = obj;
91     }
92 
93     void registerKeepAliveObject(void* handle, SKObject obj) {
94         _ownedObjectsMutex.lock();
95         scope(exit) {
96             _ownedObjectsMutex.unlock();
97         }
98 
99         keepAliveObjects[handle] = obj;
100     }
101 
102     protected override void DisposeUnownedManaged ()
103     {
104         if (ownedObjects !is null) {
105             foreach (SKObject c; ownedObjects.byValue()) {
106                 if(c is null) continue;
107                 
108                 version(SKIA_DEBUG) {
109                     tracef("SKObject: %s, OwnsHandle: %s", typeid(c), c.OwnsHandle);
110                 }
111 
112                 if (!c.OwnsHandle)
113                     c.DisposeInternal ();
114             }
115         }
116     }
117 
118     protected override void DisposeManaged ()
119     {
120         if (ownedObjects !is null) {
121             foreach (SKObject c; ownedObjects.byValue()) {
122                 if (c !is null && c.OwnsHandle)
123                     c.DisposeInternal ();
124             }
125             ownedObjects.clear ();
126         }
127         keepAliveObjects.clear ();
128     }
129 
130     protected override void DisposeNative ()
131     {
132         ISKReferenceCounted refcnt = cast(ISKReferenceCounted)this;
133         if (refcnt !is null)
134             refcnt.SafeUnRef ();
135     }
136 
137     static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, Func!(void*, bool, TSkiaObject) objectFactory)
138         if(is(TSkiaObject : SKObject))
139     {
140         if (handle is null)
141             return null;
142 
143         return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, true, true, objectFactory);
144     }
145 
146     static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, Func!(void*, bool, TSkiaObject) objectFactory)
147         if(is(TSkiaObject : SKObject))
148     {
149         trace("xxxx");
150 
151         if (handle is null)
152             return null;
153 
154         return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, owns, true, objectFactory);
155     }
156 
157     static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, bool unrefExisting, Func!(void*, bool, TSkiaObject) objectFactory)
158         if(is(TSkiaObject : SKObject))
159     {
160         if (handle is null)
161             return null;
162 
163         return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, owns, unrefExisting, objectFactory);
164     }
165 
166     static void RegisterHandle (void* handle, SKObject instance)
167     {
168         if (handle is null || instance is null)
169             return;
170         HandleDictionary.RegisterHandle (handle, instance);
171     }
172 
173     static void DeregisterHandle (void* handle, SKObject instance)
174     {
175         if (handle is null)
176             return;
177 
178         HandleDictionary.DeregisterHandle (handle, instance);
179     }
180 
181     static bool GetInstance(TSkiaObject) (void* handle, ref TSkiaObject instance)
182         if(is(TSkiaObject : SKObject))
183     {
184         if (handle is null) {
185             instance = null;
186             return false;
187         }
188 
189         return HandleDictionary.GetInstance!(TSkiaObject) (handle, instance);
190     }
191 
192     // indicate that the user cannot dispose the object
193     void PreventPublicDisposal ()
194     {
195         IgnorePublicDispose = true;
196     }
197 
198     // indicate that the ownership of this object is now in the hands of
199     // the native object
200     void RevokeOwnership (SKObject newOwner)
201     {
202         OwnsHandle = false;
203         IgnorePublicDispose = true;
204 
205         if (newOwner is null)
206             DisposeInternal ();
207         else {
208             newOwner.registerOwnedObject(Handle, this);
209         }
210     }
211 
212     // indicate that the child is controlled by the native code and
213     // the managed wrapper should be disposed when the owner is
214     static T OwnedBy(T) (T child, SKObject owner)
215         if(is(T : SKObject))
216     {
217         if (child !is null) {
218             owner.registerOwnedObject(child.Handle, child);
219             // owner.OwnedObjects[child.Handle] = child;
220         }
221 
222         return child;
223     }
224 
225     // indicate that the child was created by the managed code and
226     // should be disposed when the owner is
227     static T Owned(T) (T owner, SKObject child)
228         if(is(T : SKObject))
229     {
230         if (child !is null) {
231             if (owner !is null){
232                 owner.registerOwnedObject(child.Handle, child);
233                 // owner.OwnedObjects[child.Handle] = child;
234             }
235             else
236                 child.Dispose ();
237         }
238 
239         return owner;
240     }
241 
242     // indicate that the chile should not be garbage collected while
243     // the owner still lives
244     static T Referenced(T) (T owner, SKObject child)
245         if(is(T : SKObject))
246     {
247         if (child !is null && owner !is null) {
248             // owner.KeepAliveObjects[child.Handle] = child;
249             owner.registerKeepAliveObject(child.Handle, child);
250 
251         }
252 
253         return owner;
254     }
255 
256     static T[] PtrToStructureArray(T) (void* intPtr, int count)
257     {
258         warning("TODO");
259         auto items = new T[count];
260         // var size = Marshal.SizeOf!(T) ();
261         // for (var i = 0; i < count; i++) {
262         //     var newPtr = new void* (intPtr.ToInt64 () + (i * size));
263         //     items[i] = Marshal.PtrToStructure<T> (newPtr);
264         // }
265         return items;
266     }
267 
268     // static T PtrToStructure<T> (void* intPtr, int index)
269     // {
270     //     var size = Marshal.SizeOf<T> ();
271     //     var newPtr = new void* (intPtr.ToInt64 () + (index * size));
272     //     return Marshal.PtrToStructure<T> (newPtr);
273     // }
274 }
275 
276 
277 /**
278  * 
279  */
280 class SKNativeObject : IDisposable
281 {
282     bool fromFinalizer = false;
283 
284     private shared int isDisposed = 0;
285 
286     this (void* handle)
287     {
288         this (handle, true);
289     }
290 
291     this (void* handle, bool ownsHandle)
292     {
293         version(SKIA_DEBUG) {
294             tracef("SkObject: %s, ownsHandle: %s", typeid(this), ownsHandle);
295         }
296         Handle = handle;
297         OwnsHandle = ownsHandle;
298     }
299 
300     ~this ()
301     {
302         fromFinalizer = true;
303         Dispose (false);
304     }
305 
306     private void* _handle = null;
307     void* Handle() { 
308         return _handle;
309     }
310 
311     protected void Handle(void* value) {
312         _handle = value;
313     }
314 
315     private bool _ownsHandle = false;
316     protected bool OwnsHandle() { 
317         return _ownsHandle;
318     }
319 
320     protected void OwnsHandle(bool value) {
321         _ownsHandle = value;
322     }
323 
324     private bool _ignorePublicDispose = false;
325     protected bool IgnorePublicDispose() { 
326         return _ignorePublicDispose;
327     }
328 
329     protected void IgnorePublicDispose(bool value) {
330         _ignorePublicDispose = value;
331     }
332 
333     protected bool IsDisposed() {
334         return isDisposed == 1;
335     }
336 
337     protected void DisposeUnownedManaged ()
338     {
339         // dispose of any managed resources that are not actually owned
340     }
341 
342     protected void DisposeManaged ()
343     {
344         // dispose of any managed resources
345     }
346 
347     protected void DisposeNative ()
348     {
349         // dispose of any unmanaged resources
350     }
351 
352     protected void Dispose (bool disposing)
353     {
354         // if (Interlocked.CompareExchange (ref isDisposed, 1, 0) != 0)
355         //     return;
356         int v = cas(&isDisposed, 1, 0);
357         if(v != 0) {
358             return;
359         }
360 
361         // dispose any objects that are owned/created by native code
362         if (disposing)
363             DisposeUnownedManaged ();
364 
365         // destroy the native object
366         if (Handle !is null && OwnsHandle)
367             DisposeNative ();
368 
369         // dispose any remaining managed-created objects
370         if (disposing) 
371             DisposeManaged ();
372 
373         Handle = null;
374     }
375 
376     void Dispose ()
377     {
378         if (IgnorePublicDispose)
379             return;
380 
381         DisposeInternal ();
382     }
383 
384     protected void DisposeInternal ()
385     {
386         Dispose (true);
387         // FIXME: Needing refactor or cleanup -@putao at 2020-12-27T16:08:53+08:00
388         // 
389         // GC.SuppressFinalize (this);
390     }
391 }
392 
393 
394 /**
395  * SKObjectExtensions
396  */
397 bool IsUnique (void* handle, bool isVirtual)
398 {
399     if (isVirtual)
400         return SkiaApi.sk_refcnt_unique (cast(sk_refcnt_t*)handle);
401     else
402         return SkiaApi.sk_nvrefcnt_unique (cast(sk_nvrefcnt_t*)handle);
403 }
404 
405 int GetReferenceCount (void* handle, bool isVirtual)
406 {
407     if (isVirtual)
408         return SkiaApi.sk_refcnt_get_ref_count (cast(sk_refcnt_t*)handle);
409     else
410         return SkiaApi.sk_nvrefcnt_get_ref_count (cast(sk_nvrefcnt_t* )handle);
411 }
412 
413 void SafeRef (ISKReferenceCounted obj)
414 {
415     ISKNonVirtualReferenceCounted nvrefcnt = cast(ISKNonVirtualReferenceCounted)obj;
416     if (nvrefcnt !is nvrefcnt)
417         nvrefcnt.ReferenceNative ();
418     else
419         SkiaApi.sk_refcnt_safe_unref (cast(sk_refcnt_t*)obj.Handle);
420 }
421 
422 void SafeUnRef (ISKReferenceCounted obj)
423 {
424     ISKNonVirtualReferenceCounted nvrefcnt = cast(ISKNonVirtualReferenceCounted)obj;
425     if (nvrefcnt !is nvrefcnt)
426         nvrefcnt.UnreferenceNative ();
427     else
428         SkiaApi.sk_refcnt_safe_unref (cast(sk_refcnt_t*)obj.Handle);
429 }
430 
431 int GetReferenceCount (ISKReferenceCounted obj)
432 {
433     ISKNonVirtualReferenceCounted skObj = cast(ISKNonVirtualReferenceCounted)obj;
434     if (skObj !is null)
435         return SkiaApi.sk_nvrefcnt_get_ref_count (cast(sk_nvrefcnt_t*)obj.Handle);
436     else
437         return SkiaApi.sk_refcnt_get_ref_count (cast(sk_refcnt_t*)obj.Handle);
438 }
439 
440 
441 /**
442  * 
443  */
444 interface ISKReferenceCounted
445 {
446     void* Handle();
447 }
448 
449 
450 /**
451  * 
452  */
453 interface ISKNonVirtualReferenceCounted : ISKReferenceCounted
454 {
455     void ReferenceNative ();
456 
457     void UnreferenceNative ();
458 }
459 
460 
461 /**
462  * 
463  */
464 interface ISKSkipObjectRegistration
465 {
466 }
467 
468 
469 alias Func(T, R) = R delegate(T);
470 alias Func(T1, T2, R) = R delegate(T1, T2);
471 alias Func(T1, T2, T3, R) = R delegate(T1, T2, T3);
472 alias Func(T1, T2, T3, T4, R) = R delegate(T1, T2, T3, T4);
473 alias Func(T1, T2, T3, T4, T5, R) = R delegate(T1, T2, T3, T4, T5);
474 
475 package struct HandleDictionary {
476     private __gshared Object[void*] instances;
477     private __gshared Mutex instancesLock;
478 
479     shared static this() {
480         instancesLock = new Mutex();
481     }
482 
483     /// <summary>
484     /// Retrieve the living instance if there is one, or null if not.
485     /// </summary>
486     /// <returns>The instance if it is alive, or null if there is none.</returns>
487     static bool GetInstance(TSkiaObject) (void* handle, ref TSkiaObject instance)
488         if(is(TSkiaObject : SKObject))
489     {
490         if (handle is null) {
491             instance = null;
492             return false;
493         }
494 
495         // FIXME: Needing refactor or cleanup -@putao at 2021-01-01T12:20:15+08:00
496         // 
497         // if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) {
498         //     instance = null;
499         //     return false;
500         // }
501 
502         instancesLock.lock();
503         try {
504             return GetInstanceNoLocks (handle, instance);
505         } finally {
506             instancesLock.unlock();
507         }
508     }
509 
510     /// <summary>
511     /// Retrieve or create an instance for the native handle.
512     /// </summary>
513     /// <returns>The instance, or null if the handle was null.</returns>
514     static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, bool unrefExisting, Func!(void*, bool, TSkiaObject) objectFactory)
515         if(is(TSkiaObject : SKObject))
516     {
517         if (handle is null)
518             return null;
519 
520 //         if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) {
521 // #if THROW_OBJECT_EXCEPTIONS
522 //             throw new InvalidOperationException (
523 //                 "For some reason, the object was constructed using a factory function instead of the constructor. " +
524 //                 "H: {handle.ToString ("x")} Type: {typeof (TSkiaObject)}");
525 // #else
526 //             return objectFactory.Invoke (handle, owns);
527 // #endif
528 //         }
529 
530         instancesLock.lock();
531 
532         try {
533             TSkiaObject instance;
534             if (GetInstanceNoLocks!(TSkiaObject)(handle, instance)) {
535                 // some object get automatically referenced on the native side,
536                 // but managed code just has the same reference
537                 ISKReferenceCounted refcnt = cast(ISKReferenceCounted)instance;
538                 if (unrefExisting && refcnt !is null) {
539                     if (refcnt.GetReferenceCount () == 1) {
540                         throw new InvalidOperationException ( "About to unreference an object that has no references. ");
541                         // throw new InvalidOperationException (
542                         //     "About to unreference an object that has no references. " ~
543                         //     format("H: {handle.ToString (\"%02X\")} Type: {instance.GetType ()}"));
544                     }
545                     refcnt.SafeUnRef ();
546                 }
547 
548                 return instance;
549             }
550 
551             TSkiaObject obj = objectFactory(handle, owns);
552 
553             return obj;
554         } catch(Exception ex) {
555             warning(ex);
556             return TSkiaObject.init;
557         } finally {
558             instancesLock.unlock();
559         }
560     }
561 
562     /// <summary>
563     /// Retrieve the living instance if there is one, or null if not. This does not use locks.
564     /// </summary>
565     /// <returns>The instance if it is alive, or null if there is none.</returns>
566     private static bool GetInstanceNoLocks(TSkiaObject) (void* handle, TSkiaObject instance)
567         if(is(TSkiaObject : SKObject))
568     {
569         auto itemPtr = handle in instances;
570         if(itemPtr !is null) {
571             TSkiaObject match = cast(TSkiaObject)*itemPtr;
572             if(match is null) {
573                 warning("A managed object exists for the handle, but is not the expected type.");
574             } else {
575                 instance = match;
576                 return true;
577             }
578         } 
579 
580         instance = null;
581         return false;
582     }
583 
584     /// <summary>
585     /// Registers the specified instance with the dictionary.
586     /// </summary>
587     static void RegisterHandle (void* handle, SKObject instance)
588     {
589         if (handle is null || instance is null)
590             return;
591 
592         ISKSkipObjectRegistration objectRegistration = cast(ISKSkipObjectRegistration)instance;
593         if (objectRegistration !is null)
594             return;
595 
596         SKObject objectToDispose = null;
597         instancesLock.lock();
598 
599         try {
600             Object oldValue;
601             auto itemPtr = handle in instances;
602 
603             if(itemPtr !is null) {
604                 oldValue = *itemPtr;
605                 SKObject obj = cast(SKObject)oldValue;
606                 if(obj is null) {
607                     warning("No a SKObject");
608                 } else {
609                     if (obj.OwnsHandle) {
610                         // a mostly recoverable error
611                         // if there is a managed object, then maybe something happened and the native object is dead
612                         // throw new InvalidOperationException (
613                         //     "A managed object already exists for the specified native object. " +
614                         //     "H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {instance.GetType ()})");
615                         throw new Exception("A managed object already exists for the specified native object. ");
616                     }
617 
618                     objectToDispose = obj;
619                 }
620             }
621 
622             instances[handle] = instance;
623         } finally {
624             instancesLock.unlock();
625             // instancesLock.ExitWriteLock ();
626         }
627 
628         // dispose the object we just replaced
629         if(objectToDispose !is null) {
630             objectToDispose.DisposeInternal ();
631         }
632     }
633 
634     /// <summary>
635     /// Removes the registered instance from the dictionary.
636     /// </summary>
637     static void DeregisterHandle (void* handle, SKObject instance)
638     {
639         if (handle is null)
640             return;
641 
642         ISKSkipObjectRegistration objectRegistration = cast(ISKSkipObjectRegistration)instance;
643         if (objectRegistration !is null)
644             return;
645 
646         instancesLock.lock();
647 
648         try {
649 
650             void* weak;
651             auto itemPtr = handle in instances;
652             if(itemPtr !is null) {
653                 SKObject obj = cast(SKObject)*itemPtr;
654                 if(*itemPtr is null || obj is instance) {
655                     instances.remove(handle);
656                 } else {
657                     warning("A managed object did not exist for the specified native object. " );
658                 }
659             } else {
660                 warning("A managed object did not exist for the specified native object. " );
661             }
662 
663         } finally {
664             instancesLock.unlock();
665         }
666     }
667 }