Unverified Commit c22b0fcb authored by Ivan Matkov's avatar Ivan Matkov Committed by GitHub

Add a way to replace picture placeholders during drawing (#1009)

New `PictureFilterCanvas` class that allows override picture rendering
during drawing

Required for
https://github.com/JetBrains/compose-multiplatform-core/pull/1766
parent cbf3345b
......@@ -8,7 +8,7 @@ import org.jetbrains.skia.impl.Library.Companion.staticLoad
*/
abstract class PaintFilterCanvas(private val canvas: Canvas, unrollDrawable: Boolean) :
Canvas(makePaintFilterCanvas(canvas, unrollDrawable), true, canvas) {
companion object {
private companion object {
init {
staticLoad()
}
......@@ -23,7 +23,9 @@ abstract class PaintFilterCanvas(private val canvas: Canvas, unrollDrawable: Boo
*
* Note: The base implementation calls onFilter() for top-level/explicit paints only.
*/
abstract fun onFilter(paint: Paint): Boolean
protected abstract fun onFilter(paint: Paint): Boolean
// For JNI call
fun onFilter(paintPtr: NativePointer): Boolean {
val paint = Paint(paintPtr, false)
return onFilter(paint)
......@@ -56,13 +58,3 @@ internal expect fun PaintFilterCanvas.doInit(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nMake")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nMake")
private external fun PaintFilterCanvas_nMake(canvasPtr: NativePointer, unrollDrawable: Boolean): NativePointer
// Native/JS only
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nInit")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nInit")
internal external fun PaintFilterCanvas_nInit(ptr: NativePointer, onFilter: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
internal external fun PaintFilterCanvas_nGetOnFilterPaint(ptr: NativePointer): NativePointer
......@@ -3,7 +3,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
class Picture internal constructor(ptr: NativePointer) : RefCnt(ptr) {
class Picture internal constructor(ptr: NativePointer, managed: Boolean = true) : RefCnt(ptr, managed) {
companion object {
/**
* Recreates Picture that was serialized into data. Returns constructed Picture
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.reachabilityBarrier
abstract class PictureFilterCanvas(canvas: Canvas) :
Canvas(makePictureFilterCanvas(canvas), true, this) {
private companion object {
init {
staticLoad()
}
}
init {
Stats.onNativeCall()
try {
doInit(_ptr)
} finally {
reachabilityBarrier(this)
}
}
protected abstract fun onDrawPicture(picture: Picture, matrix: Matrix33? = null, paint: Paint? = null): Boolean
fun onDrawPicture(picturePtr: NativePointer, matrixPtr: NativePointer, paintPtr: NativePointer): Boolean {
val picture = Picture(picturePtr, managed = false)
// TODO: Provide mapping for matrix arg
val paint = if (paintPtr == NullPointer) null else Paint(paintPtr, managed = false)
return onDrawPicture(picture, null, paint)
}
}
private fun makePictureFilterCanvas(canvas: Canvas): NativePointer {
Stats.onNativeCall()
return try {
PictureFilterCanvas_nMake(getPtr(canvas))
} finally {
reachabilityBarrier(canvas)
}
}
internal expect fun PictureFilterCanvas.doInit(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_PictureFilterCanvas__1nMake")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PictureFilterCanvas__1nMake")
private external fun PictureFilterCanvas_nMake(canvasPtr: NativePointer): NativePointer
......@@ -5,14 +5,14 @@
#include "SkPaintFilterCanvas.h"
#include "interop.hh"
class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
class SkikoPaintFilterCanvas : public SkPaintFilterCanvas {
public:
SkijaPaintFilterCanvas(
SkikoPaintFilterCanvas(
SkCanvas* canvas,
bool unrollDrawable
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable) {}
virtual ~SkijaPaintFilterCanvas() {
virtual ~SkikoPaintFilterCanvas() {
skija::PaintFilterCanvas::detach(jobj);
}
......@@ -37,13 +37,13 @@ private:
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_PaintFilterCanvas_1jvmKt_PaintFilterCanvas_1nInit
(JNIEnv* env, jclass jclass, jobject jobj, jlong canvasPtr) {
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
canvas->jobj = skija::PaintFilterCanvas::attach(env, jobj);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PaintFilterCanvasKt_PaintFilterCanvas_1nMake
(JNIEnv* env, jclass jclass, jlong canvasPtr, jboolean unrollDrawable) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(canvasPtr));
SkijaPaintFilterCanvas* filterCanvas = new SkijaPaintFilterCanvas(canvas, unrollDrawable);
SkikoPaintFilterCanvas* filterCanvas = new SkikoPaintFilterCanvas(canvas, unrollDrawable);
return reinterpret_cast<jlong>(filterCanvas);
}
\ No newline at end of file
#include <jni.h>
#include "SkNWayCanvas.h"
#include "interop.hh"
class SkikoPictureFilterCanvas : public SkNWayCanvas {
public:
SkikoPictureFilterCanvas(SkCanvas* canvas) :
SkNWayCanvas(canvas->imageInfo().width(), canvas->imageInfo().height()),
_jobject(nullptr) {
this->addCanvas(canvas);
}
virtual ~SkikoPictureFilterCanvas() {
skija::PictureFilterCanvas::detach(_jobject);
}
jobject _jobject;
protected:
void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) override {
jboolean handled = skija::PictureFilterCanvas::onDrawPicture(_jobject, picture, matrix, paint);
if (!handled) {
SkCanvas::onDrawPicture(picture, matrix, paint);
}
}
};
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_PictureFilterCanvas_1jvmKt_PictureFilterCanvas_1nInit
(JNIEnv* env, jclass jclass, jobject pictureFilterCanvas, jlong canvasPtr) {
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
canvas->_jobject = skija::PictureFilterCanvas::attach(env, pictureFilterCanvas);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PictureFilterCanvasKt_PictureFilterCanvas_1nMake
(JNIEnv* env, jclass jclass, SkCanvas* canvas) {
SkikoPictureFilterCanvas* filterCanvas = new SkikoPictureFilterCanvas(canvas);
return reinterpret_cast<jlong>(filterCanvas);
}
......@@ -653,6 +653,39 @@ namespace skija {
}
}
namespace PictureFilterCanvas {
JavaVM* _vm;
jmethodID onDrawPictureId;
void onLoad(JNIEnv* env) {
env->GetJavaVM(&_vm);
jclass local = env->FindClass("org/jetbrains/skia/PictureFilterCanvas");
onDrawPictureId = env->GetMethodID(local, "onDrawPicture", "(JJJ)Z");
}
void onUnload(JNIEnv* env) {
}
bool onDrawPicture(jobject obj, const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
JNIEnv *env;
_vm->AttachCurrentThread(AS_JNI_ENV_PTR(&env), NULL);
jboolean result = env->CallBooleanMethod(obj, onDrawPictureId, reinterpret_cast<jlong>(picture), reinterpret_cast<jlong>(matrix), reinterpret_cast<jlong>(paint));
_vm->DetachCurrentThread();
return result;
}
jobject attach(JNIEnv* env, jobject obj) {
return env->NewGlobalRef(obj);
}
void detach(jobject obj) {
JNIEnv *env;
_vm->AttachCurrentThread(AS_JNI_ENV_PTR(&env), NULL);
env->DeleteGlobalRef(obj);
_vm->DetachCurrentThread();
}
}
namespace Rect {
jclass cls;
jmethodID makeLTRB;
......@@ -897,6 +930,7 @@ namespace skija {
PathSegment::onLoad(env);
Point::onLoad(env);
PaintFilterCanvas::onLoad(env);
PictureFilterCanvas::onLoad(env);
Rect::onLoad(env);
RRect::onLoad(env);
RSXform::onLoad(env);
......@@ -909,6 +943,7 @@ namespace skija {
RRect::onUnload(env);
Rect::onUnload(env);
PaintFilterCanvas::onUnload(env);
PictureFilterCanvas::onUnload(env);
Point::onUnload(env);
PathSegment::onUnload(env);
Path::onUnload(env);
......
......@@ -10,6 +10,7 @@
#include "SkMatrix.h"
#include "SkM44.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkRefCnt.h"
#include "SkRect.h"
#include "SkRRect.h"
......@@ -271,6 +272,15 @@ namespace skija {
void detach(jobject obj);
}
namespace PictureFilterCanvas {
extern jmethodID onDrawPictureId;
void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env);
bool onDrawPicture(jobject obj, const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint);
jobject attach(JNIEnv* env, jobject obj);
void detach(jobject obj);
}
namespace Rect {
extern jclass cls;
extern jmethodID makeLTRB;
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.NativePointer
internal actual fun PictureFilterCanvas.doInit(ptr: NativePointer) {
PictureFilterCanvas_nInit(this, ptr)
}
private external fun PictureFilterCanvas_nInit(thisPtr: PictureFilterCanvas, canvasPtr: NativePointer)
#include <iostream>
#include "SkCanvas.h"
#include "SkDrawable.h"
#include "SkPaintFilterCanvas.h"
#include "common.h"
class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
class SkikoPaintFilterCanvas : public SkPaintFilterCanvas {
public:
SkijaPaintFilterCanvas(
SkikoPaintFilterCanvas(
SkCanvas* canvas,
bool unrollDrawable
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable), _onFilter(nullptr), _onFilterPaint(nullptr) {}
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable), _onFilter(nullptr), _onFilter_paint(nullptr) {}
SkPaint* onFilterPaint() const {
return _onFilterPaint;
return _onFilter_paint;
}
void init(KInteropPointer onFilter) {
_onFilter = KBooleanCallback(onFilter);
}
protected:
bool onFilter(SkPaint& paint) const override {
_onFilterPaint = &paint;
_onFilter_paint = &paint;
KBoolean result = _onFilter();
_onFilterPaint = nullptr;
_onFilter_paint = nullptr;
return static_cast<bool>(result);
}
......@@ -36,26 +36,29 @@ protected:
private:
KBooleanCallback _onFilter;
mutable SkPaint* _onFilterPaint;
// TODO: Support callback with parameters properly
mutable SkPaint* _onFilter_paint;
bool unrollDrawable;
};
SKIKO_EXPORT void org_jetbrains_skia_PaintFilterCanvas__1nInit
(KNativePointer canvasPtr, KInteropPointer onFilter) {
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>((canvasPtr));
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>((canvasPtr));
canvas->init(onFilter);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_PaintFilterCanvas__1nMake
(KNativePointer canvasPtr, KBoolean unrollDrawable) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
SkijaPaintFilterCanvas* filterCanvas = new SkijaPaintFilterCanvas(canvas, unrollDrawable);
SkikoPaintFilterCanvas* filterCanvas = new SkikoPaintFilterCanvas(canvas, unrollDrawable);
return reinterpret_cast<KNativePointer>(filterCanvas);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint
(KNativePointer canvasPtr) {
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>((canvasPtr));
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>((canvasPtr));
return reinterpret_cast<KNativePointer>(canvas->onFilterPaint());
}
#include "SkNWayCanvas.h"
#include "common.h"
class SkikoPictureFilterCanvas : public SkNWayCanvas {
public:
SkikoPictureFilterCanvas(SkCanvas* canvas) :
SkNWayCanvas(canvas->imageInfo().width(), canvas->imageInfo().height()),
_onDrawPicture(nullptr),
_onDrawPicture_picture(nullptr),
_onDrawPicture_matrix(nullptr),
_onDrawPicture_paint(nullptr) {
this->addCanvas(canvas);
}
KBooleanCallback _onDrawPicture;
// TODO: Support callback with parameters properly
const SkPicture* _onDrawPicture_picture;
const SkMatrix* _onDrawPicture_matrix;
const SkPaint* _onDrawPicture_paint;
protected:
void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) override {
_onDrawPicture_picture = picture;
_onDrawPicture_matrix = matrix;
_onDrawPicture_paint = paint;
KBoolean handled = _onDrawPicture();
_onDrawPicture_picture = nullptr;
_onDrawPicture_matrix = nullptr;
_onDrawPicture_paint = nullptr;
if (!handled) {
SkCanvas::onDrawPicture(picture, matrix, paint);
}
}
};
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nMake
(SkCanvas* canvas) {
SkikoPictureFilterCanvas* filterCanvas = new SkikoPictureFilterCanvas(canvas);
return reinterpret_cast<KNativePointer>(filterCanvas);
}
SKIKO_EXPORT void org_jetbrains_skia_SkikoPictureFilterCanvas__1nInit
(KNativePointer canvasPtr, KInteropPointer onDrawPicture) {
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
canvas->_onDrawPicture = KBooleanCallback(onDrawPicture);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_picture
(KNativePointer canvasPtr) {
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
return reinterpret_cast<KNativePointer>(const_cast<SkPicture *>(canvas->_onDrawPicture_picture));
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_matrix
(KNativePointer canvasPtr) {
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
return reinterpret_cast<KNativePointer>(const_cast<SkMatrix *>(canvas->_onDrawPicture_matrix));
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_paint
(KNativePointer canvasPtr) {
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
return reinterpret_cast<KNativePointer>(const_cast<SkPaint *>(canvas->_onDrawPicture_paint));
}
package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.interopScope
internal actual fun PaintFilterCanvas.doInit(ptr: NativePointer) {
interopScope {
val onFilter = virtualBoolean {
val paint = Paint(PaintFilterCanvas_nGetOnFilterPaint(ptr), false)
onFilter(paint)
onFilter(PaintFilterCanvas_nGetOnFilterPaint(ptr))
}
PaintFilterCanvas_nInit(ptr, onFilter)
}
}
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nInit")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nInit")
internal external fun PaintFilterCanvas_nInit(ptr: NativePointer, onFilter: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
internal external fun PaintFilterCanvas_nGetOnFilterPaint(ptr: NativePointer): NativePointer
package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.interopScope
internal actual fun PictureFilterCanvas.doInit(ptr: NativePointer) {
interopScope {
val onFilter = virtualBoolean {
onDrawPicture(
picturePtr = PictureFilterCanvas_nGetOnDrawPicture_picture(ptr),
matrixPtr = PictureFilterCanvas_nGetOnDrawPicture_matrix(ptr),
paintPtr = PictureFilterCanvas_nGetOnDrawPicture_paint(ptr)
)
}
PictureFilterCanvas_nInit(ptr, onFilter)
}
}
@ExternalSymbolName("org_jetbrains_skia_PictureFilterCanvas__1nInit")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PictureFilterCanvas__1nInit")
internal external fun PictureFilterCanvas_nInit(ptr: NativePointer, onFilter: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_picture")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_picture")
internal external fun PictureFilterCanvas_nGetOnDrawPicture_picture(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_matrix")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_matrix")
internal external fun PictureFilterCanvas_nGetOnDrawPicture_matrix(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_paint")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_paint")
internal external fun PictureFilterCanvas_nGetOnDrawPicture_paint(ptr: NativePointer): NativePointer
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