Unverified Commit 54fb2034 authored by Aleksandr Veselov's avatar Aleksandr Veselov Committed by GitHub

Implement Drawable for native (#373)

parent 82342b34
......@@ -81,7 +81,9 @@ val compileWasm = tasks.register<CompileSkikoCppTask>("compileWasm") {
flags.set(listOf(
*skiaPreprocessorFlags(),
"-DSKIKO_WASM"
"-DSKIKO_WASM",
"-fno-rtti",
"-fno-exceptions"
))
}
......
......@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skiko.maybeSynchronized
/**
*
......@@ -12,14 +13,15 @@ import org.jetbrains.skia.impl.Library.Companion.staticLoad
* allow for clients of the drawable that may want to cache the results, the drawable must
* change its generation id whenever its internal state changes such that it will draw differently.
*/
abstract class Drawable : RefCnt(Drawable_nMake()) {
abstract class Drawable : Managed(Drawable_nMake(), _FinalizerHolder.PTR) {
companion object {
init {
staticLoad()
}
}
internal var _bounds: Rect? = null
private var _bounds: Rect? = null
private val boundsLock = Unit
/**
* Draws into the specified content. The drawing sequence will be balanced upon return
......@@ -93,8 +95,10 @@ abstract class Drawable : RefCnt(Drawable_nMake()) {
*/
val bounds: Rect
get() {
if (_bounds == null) _bounds = onGetBounds()
return _bounds!!
maybeSynchronized(boundsLock) {
if (_bounds == null) _bounds = Rect.fromInteropPointer { Drawable_nGetBounds(_ptr, it) }
return _bounds!!
}
}
/**
......@@ -103,27 +107,34 @@ abstract class Drawable : RefCnt(Drawable_nMake()) {
* in response to its internal state changing.
*/
fun notifyDrawingChanged(): Drawable {
Stats.onNativeCall()
_nNotifyDrawingChanged(_ptr)
_bounds = null
maybeSynchronized(boundsLock) {
Stats.onNativeCall()
_nNotifyDrawingChanged(_ptr)
_bounds = null
}
return this
}
abstract fun onDraw(canvas: Canvas?)
abstract fun onGetBounds(): Rect
fun _onDraw(canvasPtr: NativePointer) {
private fun _onDraw(canvasPtr: NativePointer) {
onDraw(Canvas(canvasPtr, false, this))
}
private object _FinalizerHolder {
val PTR = Drawable_nGetFinalizer()
}
init {
Stats.onNativeCall()
Stats.onNativeCall()
Drawable_nInit(this, _ptr)
doInit(_ptr)
}
}
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nInit")
private external fun Drawable_nInit(drawable: Drawable, ptr: NativePointer)
internal expect fun Drawable.doInit(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nGetFinalizer")
private external fun Drawable_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nMake")
private external fun Drawable_nMake(): NativePointer
......@@ -138,4 +149,18 @@ private external fun _nDraw(ptr: NativePointer, canvasPtr: NativePointer, matrix
private external fun _nMakePictureSnapshot(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nNotifyDrawingChanged")
private external fun _nNotifyDrawingChanged(ptr: NativePointer)
\ No newline at end of file
private external fun _nNotifyDrawingChanged(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nGetBounds")
private external fun Drawable_nGetBounds(ptr: NativePointer, result: InteropPointer)
// For Native/JS usage only
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nInit")
internal external fun Drawable_nInit(ptr: NativePointer, onGetBounds: InteropPointer, onDraw: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nGetOnDrawCanvas")
internal external fun _nGetOnDrawCanvas(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Drawable__1nSetBounds")
internal external fun _nSetBounds(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float)
\ No newline at end of file
......@@ -77,7 +77,7 @@ class Picture internal constructor(ptr: NativePointer) : RefCnt(ptr) {
return try {
Stats.onNativeCall()
interopScope {
_nPlayback(_ptr, getPtr(canvas), toInterop(abort))
_nPlayback(_ptr, getPtr(canvas), booleanCallback(abort))
}
this
} finally {
......
......@@ -42,7 +42,15 @@ expect class InteropScope() {
fun InteropPointer.fromInteropNativePointerArray(): NativePointerArray
inline fun <reified T> InteropPointer.fromInterop(decoder: ArrayInteropDecoder<T>): Array<T>
fun toInteropForArraysOfPointers(interopPointers: Array<InteropPointer>): InteropPointer
fun toInterop(callback: (() -> Boolean)?): InteropPointer
// Callbacks
fun callback(callback: (() -> Unit)?): InteropPointer
fun booleanCallback(callback: (() -> Boolean)?): InteropPointer
// Virtual methods
fun virtual(method: () -> Unit): InteropPointer
fun virtualBoolean(method: () -> Boolean): InteropPointer
fun release()
}
......
package org.jetbrains.skia
import org.jetbrains.skia.tests.assertCloseEnough
import kotlin.test.Test
import kotlin.test.assertEquals
class DrawableTest {
@Test
fun canDrawDrawable() {
val cross = object : Drawable() {
override fun onDraw(canvas: Canvas?) {
val paint = Paint().apply { color = Color.RED }
canvas!!.drawRect(Rect(4f, 0f, 12f, 16f), paint)
canvas.drawRect(Rect(0f, 4f, 16f, 12f), paint)
}
override fun onGetBounds() = Rect(0f, 0f, 16f, 16f)
}
val surface = Surface.makeRasterN32Premul(16, 16)
surface.canvas.drawDrawable(cross)
val pixels = Bitmap.makeFromImage(surface.makeImageSnapshot())
assertEquals(Color.RED, pixels.getColor(8, 8))
assertEquals(Color.TRANSPARENT, pixels.getColor(2, 2))
assertCloseEnough(Rect(0f, 0f, 16f, 16f), cross.bounds)
}
}
\ No newline at end of file
......@@ -195,12 +195,28 @@ actual class InteropScope actual constructor() {
return toInterop(interopPointers.toIntArray())
}
actual fun toInterop(callback: (() -> Boolean)?): InteropPointer {
actual fun booleanCallback(callback: (() -> Boolean)?): InteropPointer {
if (callback == null) { return 0 }
initCallbacks()
val data = CallbackData<Boolean>(null)
return _registerCallback({ data.value = callback() }, data)
return _registerCallback({ data.value = callback() }, data, global = false)
}
actual fun callback(callback: (() -> Unit)?): InteropPointer {
if (callback == null) { return 0 }
initCallbacks()
return _registerCallback({ callback() }, null, global = false)
}
actual fun virtual(method: () -> Unit): InteropPointer {
return _registerCallback({ method() }, null, global = true)
}
actual fun virtualBoolean(method: () -> Boolean): InteropPointer {
val data = CallbackData<Boolean>(null)
return _registerCallback({ data.value = method() }, data, global = true);
}
actual fun release() {
......@@ -229,7 +245,7 @@ actual class InteropScope actual constructor() {
private class CallbackData<T>(@JsName("value") var value: T?)
// See `setup.js`
private external fun _registerCallback(cb: () -> Unit, data: Any?): Int
private external fun _registerCallback(cb: () -> Unit, data: Any?, global: Boolean): Int
private external fun _createLocalCallbackScope()
private external fun _releaseLocalCallbackScope()
......
......@@ -11,20 +11,23 @@ var {_callCallback, _registerCallback, _releaseCallback, _createLocalCallbackSco
class Scope {
constructor() {
this.callbackMap = [CB_NULL];
this.nextId = 1;
this.callbackMap = new Map();
this.callbackMap.set(0, CB_NULL);
}
addCallback(callback, data) {
this.callbackMap.push({callback, data});
return this.callbackMap.length - 1;
let id = this.nextId++;
this.callbackMap.set(id, {callback, data});
return id;
}
getCallback(id) {
return this.callbackMap[id] || CB_UNDEFINED
return this.callbackMap.get(id) || CB_UNDEFINED;
}
deleteCallback(id) {
this.callbackMap[id] = CB_UNDEFINED
this.callbackMap.delete(id);
}
release() {
......@@ -36,8 +39,8 @@ var {_callCallback, _registerCallback, _releaseCallback, _createLocalCallbackSco
let scope = GLOBAL_SCOPE;
return {
_callCallback(callbackId) {
let callback = scope.getCallback(callbackId);
_callCallback(callbackId, global = false) {
let callback = (global ? GLOBAL_SCOPE : scope).getCallback(callbackId);
try {
callback.callback();
return callback.data;
......@@ -45,11 +48,11 @@ var {_callCallback, _registerCallback, _releaseCallback, _createLocalCallbackSco
console.error(e)
}
},
_registerCallback(callback, data = null) {
return scope.addCallback(callback, data);
_registerCallback(callback, data = null, global = false) {
return (global ? GLOBAL_SCOPE : scope).addCallback(callback, data);
},
_releaseCallback(callbackId) {
scope.deleteCallback(callbackId);
_releaseCallback(callbackId, global = false) {
(global ? GLOBAL_SCOPE : scope).deleteCallback(callbackId);
},
_createLocalCallbackScope() {
if (scope !== GLOBAL_SCOPE) {
......
......@@ -10,14 +10,14 @@ public:
~SkijaDrawableImpl() {
JNIEnv* env;
if (fJavaVM->GetEnv(reinterpret_cast<void**>(&env), SKIKO_JNI_VERSION) == JNI_OK)
env->DeleteWeakGlobalRef(fObject);
if (fJavaVM->GetEnv(AS_JNI_ENV_PTR(&env), SKIKO_JNI_VERSION) == JNI_OK)
env->DeleteGlobalRef(fObject);
}
void init(JNIEnv* e, jobject o) {
fEnv = e;
fEnv->GetJavaVM(&fJavaVM);
fObject = fEnv->NewWeakGlobalRef(o);
fObject = fEnv->NewGlobalRef(o);
}
protected:
......@@ -38,13 +38,21 @@ private:
jobject fObject;
};
static void deleteDrawable(SkijaDrawableImpl* drawable) {
delete drawable;
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_DrawableKt_Drawable_1nGetFinalizer() {
return reinterpret_cast<jlong>(&deleteDrawable);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_DrawableKt_Drawable_1nMake
(JNIEnv* env, jclass jclass) {
SkijaDrawableImpl* instance = new SkijaDrawableImpl();
return reinterpret_cast<jlong>(instance);
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_DrawableKt_Drawable__1nInit
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_Drawable_1jvmKt_Drawable_1nInit
(JNIEnv* env, jclass jclass, jobject jthis, jlong ptr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>(static_cast<uintptr_t>(ptr));
instance->init(env, jthis);
......@@ -75,3 +83,9 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_DrawableKt__1nNotifyDr
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>(static_cast<uintptr_t>(ptr));
return instance->notifyDrawingChanged();
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_DrawableKt_Drawable_1nGetBounds
(JNIEnv* env, jclass jclass, jlong ptr, jfloatArray result) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>(static_cast<uintptr_t>(ptr));
skija::Rect::copyToInterop(env, instance->getBounds(), result);
}
......@@ -446,8 +446,8 @@ public:
}
T operator()() {
jobject res = env->CallObjectMethod(callback, kotlin::jvm::functions::Function0::invoke);
return jObjectConvert<T>(env, res);
skija::AutoLocal<jobject> res(env, env->CallObjectMethod(callback, kotlin::jvm::functions::Function0::invoke));
return jObjectConvert<T>(env, res.get());
}
bool isExceptionThrown() {
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
internal actual fun Drawable.doInit(ptr: NativePointer) {
Stats.onNativeCall()
Drawable_nInit(this, ptr)
}
private external fun Drawable_nInit(thisPtr: Drawable, ptr: NativePointer)
\ No newline at end of file
......@@ -78,7 +78,13 @@ actual open class InteropScope actual constructor() {
actual inline fun <reified T> InteropPointer.fromInterop(decoder: ArrayInteropDecoder<T>): Array<T> =
this@fromInterop as Array<T>
actual fun toInteropForArraysOfPointers(interopPointers: Array<InteropPointer>): InteropPointer = interopPointers
actual fun toInterop(callback: (() -> Boolean)?) = callback as Any?
actual fun callback(callback: (() -> Unit)?) = callback as Any?
actual fun booleanCallback(callback: (() -> Boolean)?) = callback as Any?
actual fun virtual(method: () -> Unit) = callback(method)
actual fun virtualBoolean(method: () -> Boolean) = booleanCallback(method)
actual fun release() {}
}
......
......@@ -188,23 +188,13 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPatch
);
}
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawDrawable
(KNativePointer ptr, KNativePointer drawablePtr, KFloat* matrixArr) {
TODO("implement org_jetbrains_skia_Canvas__1nDrawDrawable");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawDrawable
(KNativePointer ptr, KNativePointer drawablePtr, KFloat* matrixArr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((ptr));
SkDrawable* drawable = reinterpret_cast<SkDrawable*>((drawablePtr));
std::unique_ptr<SkMatrix> matrix = skMatrix(env, matrixArr);
std::unique_ptr<SkMatrix> matrix = skMatrix(matrixArr);
canvas->drawDrawable(drawable, matrix.get());
}
#endif
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nClear(KNativePointer ptr, KInt color) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((ptr));
......
......@@ -5,94 +5,96 @@
#include "SkDrawable.h"
#include "common.h"
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nMake
(){
TODO("implement org_jetbrains_skia_Drawable__1nMake");
class SkikoDrawable : public SkDrawable {
public:
SkikoDrawable(KInteropPointer onDraw, KInteropPointer onGetBounds) : _onDraw(onDraw), _onGetBounds(onGetBounds) {};
void init(KInteropPointer onDraw, KInteropPointer onGetBounds) {
_onDraw = KVoidCallback(onDraw);
_onGetBounds = KVoidCallback(onGetBounds);
}
SkRect& bounds() { return _bounds; }
SkCanvas* onDrawCanvas() { return _onDrawCanvas; }
protected:
void onDraw(SkCanvas* canvas) override {
_onDrawCanvas = canvas;
_onDraw();
_onDrawCanvas = nullptr;
};
SkRect onGetBounds() override {
_onGetBounds();
return _bounds;
};
private:
SkRect _bounds;
SkCanvas* _onDrawCanvas;
KVoidCallback _onDraw;
KVoidCallback _onGetBounds;
};
static void deleteSkikoDrawable(SkikoDrawable* drawable) {
delete drawable;
}
#if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nMake
(){
SkijaDrawableImpl* instance = new SkijaDrawableImpl();
return reinterpret_cast<KNativePointer>(instance);
}
#endif
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nGetFinalizer
() {
return reinterpret_cast<KNativePointer>(&deleteSkikoDrawable);
}
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nSetBounds
(KNativePointer ptr, KFloat left, KFloat top, KFloat right, KFloat bottom) {
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>(ptr);
instance->bounds().setLTRB(left, top, right, bottom);
}
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nInit
(KInteropPointer kthis, KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Drawable__1nInit");
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nGetBounds
(KNativePointer ptr, KInteropPointer result) {
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>(ptr);
skija::Rect::copyToInterop(instance->getBounds(), result);
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nInit
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Drawable__1nGetOnDrawCanvas
(KNativePointer ptr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>((ptr));
instance->init(env, jthis);
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>(ptr);
return reinterpret_cast<KInteropPointer>(instance->onDrawCanvas());
}
#endif
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nMake
(){
SkikoDrawable* instance = new SkikoDrawable(nullptr, nullptr);
return reinterpret_cast<KNativePointer>(instance);
}
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nDraw
(KNativePointer ptr, KNativePointer canvasPtr, KFloat* matrixArr) {
TODO("implement org_jetbrains_skia_Drawable__1nDraw");
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nInit
(KNativePointer ptr, KInteropPointer onGetBounds, KInteropPointer onDraw) {
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>((ptr));
instance->init(onDraw, onGetBounds);
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nDraw
(KNativePointer ptr, KNativePointer canvasPtr, KFloat* matrixArr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>((ptr));
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>((ptr));
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
std::unique_ptr<SkMatrix> matrix = skMatrix(env, matrixArr);
std::unique_ptr<SkMatrix> matrix = skMatrix(matrixArr);
instance->draw(canvas, matrix.get());
}
#endif
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nMakePictureSnapshot
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Drawable__1nMakePictureSnapshot");
}
#if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Drawable__1nMakePictureSnapshot
(KNativePointer ptr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>((ptr));
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>((ptr));
return reinterpret_cast<KNativePointer>(instance->newPictureSnapshot());
}
#endif
SKIKO_EXPORT KInt org_jetbrains_skia_Drawable__1nGetGenerationId
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Drawable__1nGetGenerationId");
}
#if 0
SKIKO_EXPORT KInt org_jetbrains_skia_Drawable__1nGetGenerationId
(KNativePointer ptr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>((ptr));
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>((ptr));
return instance->getGenerationID();
}
#endif
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nNotifyDrawingChanged
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Drawable__1nNotifyDrawingChanged");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Drawable__1nNotifyDrawingChanged
(KNativePointer ptr) {
SkijaDrawableImpl* instance = reinterpret_cast<SkijaDrawableImpl*>((ptr));
SkikoDrawable* instance = reinterpret_cast<SkikoDrawable*>((ptr));
return instance->notifyDrawingChanged();
}
#endif
......@@ -160,10 +160,12 @@ static inline KFloat fromBits(KInt i) {
// Callback support
typedef void (*SkikoDisposeCallback)(KInteropPointer);
typedef void (*SkikoCallVoidCallback)(KInteropPointer);
typedef KBoolean (*SkikoCallBooleanCallback)(KInteropPointer);
typedef void* KOpaquePointer;
void disposeCallback(KInteropPointer cb);
void callVoidCallback(KInteropPointer cb);
KBoolean callBooleanCallback(KInteropPointer cb);
template <typename T, T(*Apply)(KInteropPointer)>
......@@ -171,19 +173,35 @@ class KCallback {
public:
explicit KCallback(KInteropPointer data) : data(data) {}
virtual ~KCallback() { disposeCallback(data); }
virtual ~KCallback() {
if (data != nullptr) {
disposeCallback(data);
}
}
KCallback(const KCallback&) = delete;
KCallback(KCallback&&) = delete;
KCallback& operator=(const KCallback&) = delete;
KCallback& operator=(KCallback&&) = delete;
T operator()() const { return Apply(data); }
KCallback(KCallback&& other) noexcept {
KInteropPointer t = data;
data = other.data;
other.data = t;
}
KCallback& operator=(KCallback&& other) {
std::swap(data, other.data);
return *this;
}
T operator()() const {
return Apply(data);
}
private:
KInteropPointer data;
};
typedef KCallback<KBoolean, callBooleanCallback> KBooleanCallback;
typedef KCallback<void, callVoidCallback> KVoidCallback;
#endif /* SKIKO_COMMON_H */
......@@ -342,13 +342,21 @@ KBoolean callBooleanCallback(KInteropPointer cb) {
return static_cast<KBoolean>(value);
}
void callVoidCallback(KInteropPointer cb) {
EM_ASM({
_callCallback($0);
}, cb);
}
#else // __EMSCRIPTEN__
static SkikoDisposeCallback disposeCallbackImpl = nullptr;
static SkikoCallBooleanCallback callBooleanCallbackImpl = nullptr;
static SkikoCallVoidCallback callVoidCallbackImpl = nullptr;
SKIKO_EXPORT void skiko_initCallbacks(KOpaquePointer callBoolean, KOpaquePointer dispose) {
SKIKO_EXPORT void skiko_initCallbacks(KOpaquePointer callBoolean, KOpaquePointer callVoid, KOpaquePointer dispose) {
callBooleanCallbackImpl = reinterpret_cast<SkikoCallBooleanCallback>(callBoolean);
callVoidCallbackImpl = reinterpret_cast<SkikoCallVoidCallback>(callVoid);
disposeCallbackImpl = reinterpret_cast<SkikoDisposeCallback>(dispose);
}
......@@ -360,5 +368,9 @@ KBoolean callBooleanCallback(KInteropPointer cb) {
return callBooleanCallbackImpl(cb);
}
void callVoidCallback(KInteropPointer cb) {
return callVoidCallbackImpl(cb);
}
#endif // __EMSCRIPTEN__
package org.jetbrains.skia
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.interopScope
internal actual fun Drawable.doInit(ptr: NativePointer) {
Stats.onNativeCall()
interopScope {
val onDrawCallback = virtual {
onDraw(Canvas(_nGetOnDrawCanvas(_ptr), false, this))
}
val onGetBoundsCallback = virtual {
val bounds = onGetBounds()
_nSetBounds(_ptr, bounds.left, bounds.top, bounds.right, bounds.bottom)
}
Drawable_nInit(_ptr, onGetBoundsCallback, onDrawCallback)
}
}
......@@ -26,6 +26,7 @@ actual abstract class Native actual constructor(ptr: NativePointer) {
init {
initCallbacks(
staticCFunction(::callBooleanCallback),
staticCFunction(::callVoidCallback),
staticCFunction(::disposeCallback),
)
}
......@@ -200,12 +201,21 @@ actual class InteropScope actual constructor() {
return toInterop(interopPointers.map { it.toLong() }.toLongArray())
}
actual fun toInterop(callback: (() -> Boolean)?): InteropPointer
actual fun booleanCallback(callback: (() -> Boolean)?): InteropPointer
= callback?.let {
val ptr = StableRef.create(it).asCPointer()
NativePtr.NULL.plus(ptr.toLong())
} ?: NativePtr.NULL
actual fun callback(callback: (() -> Unit)?): InteropPointer
= callback?.let {
val ptr = StableRef.create(it).asCPointer()
NativePtr.NULL.plus(ptr.toLong())
} ?: NativePtr.NULL
actual fun virtual(method: () -> Unit) = callback(method)
actual fun virtualBoolean(method: () -> Boolean) = booleanCallback(method)
actual fun release() {
elements.forEach {
it.unpin()
......@@ -232,6 +242,10 @@ actual class NativePointerArray actual constructor(size: Int) {
// Callbacks support
private fun callVoidCallback(ptr: COpaquePointer) {
ptr.asStableRef<() -> Unit>().get().invoke()
}
private fun callBooleanCallback(ptr: COpaquePointer): Boolean {
return ptr.asStableRef<() -> Boolean>().get().invoke()
}
......@@ -241,4 +255,4 @@ private fun disposeCallback(ptr: COpaquePointer) {
}
@ExternalSymbolName("skiko_initCallbacks")
private external fun initCallbacks(callBoolean: COpaquePointer, dispose: COpaquePointer)
\ No newline at end of file
private external fun initCallbacks(callBoolean: COpaquePointer, callVoid: COpaquePointer, dispose: COpaquePointer)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment