Skip to content

Commit c22b0fc

Browse files
authored
Add a way to replace picture placeholders during drawing (#1009)
New `PictureFilterCanvas` class that allows override picture rendering during drawing Required for JetBrains/compose-multiplatform-core#1766
1 parent cbf3345 commit c22b0fc

File tree

12 files changed

+276
-31
lines changed

12 files changed

+276
-31
lines changed

skiko/src/commonMain/kotlin/org/jetbrains/skia/PaintFilterCanvas.kt

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import org.jetbrains.skia.impl.Library.Companion.staticLoad
88
*/
99
abstract class PaintFilterCanvas(private val canvas: Canvas, unrollDrawable: Boolean) :
1010
Canvas(makePaintFilterCanvas(canvas, unrollDrawable), true, canvas) {
11-
companion object {
11+
private companion object {
1212
init {
1313
staticLoad()
1414
}
@@ -23,7 +23,9 @@ abstract class PaintFilterCanvas(private val canvas: Canvas, unrollDrawable: Boo
2323
*
2424
* Note: The base implementation calls onFilter() for top-level/explicit paints only.
2525
*/
26-
abstract fun onFilter(paint: Paint): Boolean
26+
protected abstract fun onFilter(paint: Paint): Boolean
27+
28+
// For JNI call
2729
fun onFilter(paintPtr: NativePointer): Boolean {
2830
val paint = Paint(paintPtr, false)
2931
return onFilter(paint)
@@ -56,13 +58,3 @@ internal expect fun PaintFilterCanvas.doInit(ptr: NativePointer)
5658
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nMake")
5759
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nMake")
5860
private external fun PaintFilterCanvas_nMake(canvasPtr: NativePointer, unrollDrawable: Boolean): NativePointer
59-
60-
// Native/JS only
61-
62-
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nInit")
63-
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nInit")
64-
internal external fun PaintFilterCanvas_nInit(ptr: NativePointer, onFilter: InteropPointer)
65-
66-
@ExternalSymbolName("org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
67-
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint")
68-
internal external fun PaintFilterCanvas_nGetOnFilterPaint(ptr: NativePointer): NativePointer

skiko/src/commonMain/kotlin/org/jetbrains/skia/Picture.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.jetbrains.skia
33
import org.jetbrains.skia.impl.*
44
import org.jetbrains.skia.impl.Library.Companion.staticLoad
55

6-
class Picture internal constructor(ptr: NativePointer) : RefCnt(ptr) {
6+
class Picture internal constructor(ptr: NativePointer, managed: Boolean = true) : RefCnt(ptr, managed) {
77
companion object {
88
/**
99
* Recreates Picture that was serialized into data. Returns constructed Picture
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.jetbrains.skia
2+
3+
import org.jetbrains.skia.impl.Library.Companion.staticLoad
4+
import org.jetbrains.skia.impl.NativePointer
5+
import org.jetbrains.skia.impl.Stats
6+
import org.jetbrains.skia.impl.getPtr
7+
import org.jetbrains.skia.impl.reachabilityBarrier
8+
9+
abstract class PictureFilterCanvas(canvas: Canvas) :
10+
Canvas(makePictureFilterCanvas(canvas), true, this) {
11+
private companion object {
12+
init {
13+
staticLoad()
14+
}
15+
}
16+
init {
17+
Stats.onNativeCall()
18+
try {
19+
doInit(_ptr)
20+
} finally {
21+
reachabilityBarrier(this)
22+
}
23+
}
24+
25+
protected abstract fun onDrawPicture(picture: Picture, matrix: Matrix33? = null, paint: Paint? = null): Boolean
26+
27+
fun onDrawPicture(picturePtr: NativePointer, matrixPtr: NativePointer, paintPtr: NativePointer): Boolean {
28+
val picture = Picture(picturePtr, managed = false)
29+
// TODO: Provide mapping for matrix arg
30+
val paint = if (paintPtr == NullPointer) null else Paint(paintPtr, managed = false)
31+
return onDrawPicture(picture, null, paint)
32+
}
33+
}
34+
35+
private fun makePictureFilterCanvas(canvas: Canvas): NativePointer {
36+
Stats.onNativeCall()
37+
return try {
38+
PictureFilterCanvas_nMake(getPtr(canvas))
39+
} finally {
40+
reachabilityBarrier(canvas)
41+
}
42+
}
43+
44+
internal expect fun PictureFilterCanvas.doInit(ptr: NativePointer)
45+
46+
@ExternalSymbolName("org_jetbrains_skia_PictureFilterCanvas__1nMake")
47+
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_PictureFilterCanvas__1nMake")
48+
private external fun PictureFilterCanvas_nMake(canvasPtr: NativePointer): NativePointer

skiko/src/jvmMain/cpp/common/PaintFilterCanvas.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
#include "SkPaintFilterCanvas.h"
66
#include "interop.hh"
77

8-
class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
8+
class SkikoPaintFilterCanvas : public SkPaintFilterCanvas {
99
public:
10-
SkijaPaintFilterCanvas(
10+
SkikoPaintFilterCanvas(
1111
SkCanvas* canvas,
1212
bool unrollDrawable
1313
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable) {}
1414

15-
virtual ~SkijaPaintFilterCanvas() {
15+
virtual ~SkikoPaintFilterCanvas() {
1616
skija::PaintFilterCanvas::detach(jobj);
1717
}
1818

@@ -37,13 +37,13 @@ class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
3737

3838
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_PaintFilterCanvas_1jvmKt_PaintFilterCanvas_1nInit
3939
(JNIEnv* env, jclass jclass, jobject jobj, jlong canvasPtr) {
40-
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
40+
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
4141
canvas->jobj = skija::PaintFilterCanvas::attach(env, jobj);
4242
}
4343

4444
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PaintFilterCanvasKt_PaintFilterCanvas_1nMake
4545
(JNIEnv* env, jclass jclass, jlong canvasPtr, jboolean unrollDrawable) {
4646
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(canvasPtr));
47-
SkijaPaintFilterCanvas* filterCanvas = new SkijaPaintFilterCanvas(canvas, unrollDrawable);
47+
SkikoPaintFilterCanvas* filterCanvas = new SkikoPaintFilterCanvas(canvas, unrollDrawable);
4848
return reinterpret_cast<jlong>(filterCanvas);
4949
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <jni.h>
2+
#include "SkNWayCanvas.h"
3+
#include "interop.hh"
4+
5+
class SkikoPictureFilterCanvas : public SkNWayCanvas {
6+
public:
7+
SkikoPictureFilterCanvas(SkCanvas* canvas) :
8+
SkNWayCanvas(canvas->imageInfo().width(), canvas->imageInfo().height()),
9+
_jobject(nullptr) {
10+
this->addCanvas(canvas);
11+
}
12+
13+
virtual ~SkikoPictureFilterCanvas() {
14+
skija::PictureFilterCanvas::detach(_jobject);
15+
}
16+
17+
jobject _jobject;
18+
19+
protected:
20+
void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) override {
21+
jboolean handled = skija::PictureFilterCanvas::onDrawPicture(_jobject, picture, matrix, paint);
22+
if (!handled) {
23+
SkCanvas::onDrawPicture(picture, matrix, paint);
24+
}
25+
}
26+
};
27+
28+
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_PictureFilterCanvas_1jvmKt_PictureFilterCanvas_1nInit
29+
(JNIEnv* env, jclass jclass, jobject pictureFilterCanvas, jlong canvasPtr) {
30+
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(static_cast<uintptr_t>(canvasPtr));
31+
canvas->_jobject = skija::PictureFilterCanvas::attach(env, pictureFilterCanvas);
32+
}
33+
34+
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PictureFilterCanvasKt_PictureFilterCanvas_1nMake
35+
(JNIEnv* env, jclass jclass, SkCanvas* canvas) {
36+
SkikoPictureFilterCanvas* filterCanvas = new SkikoPictureFilterCanvas(canvas);
37+
return reinterpret_cast<jlong>(filterCanvas);
38+
}

skiko/src/jvmMain/cpp/common/interop.cc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,39 @@ namespace skija {
653653
}
654654
}
655655

656+
namespace PictureFilterCanvas {
657+
JavaVM* _vm;
658+
jmethodID onDrawPictureId;
659+
660+
void onLoad(JNIEnv* env) {
661+
env->GetJavaVM(&_vm);
662+
jclass local = env->FindClass("org/jetbrains/skia/PictureFilterCanvas");
663+
onDrawPictureId = env->GetMethodID(local, "onDrawPicture", "(JJJ)Z");
664+
}
665+
666+
void onUnload(JNIEnv* env) {
667+
}
668+
669+
bool onDrawPicture(jobject obj, const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
670+
JNIEnv *env;
671+
_vm->AttachCurrentThread(AS_JNI_ENV_PTR(&env), NULL);
672+
jboolean result = env->CallBooleanMethod(obj, onDrawPictureId, reinterpret_cast<jlong>(picture), reinterpret_cast<jlong>(matrix), reinterpret_cast<jlong>(paint));
673+
_vm->DetachCurrentThread();
674+
return result;
675+
}
676+
677+
jobject attach(JNIEnv* env, jobject obj) {
678+
return env->NewGlobalRef(obj);
679+
}
680+
681+
void detach(jobject obj) {
682+
JNIEnv *env;
683+
_vm->AttachCurrentThread(AS_JNI_ENV_PTR(&env), NULL);
684+
env->DeleteGlobalRef(obj);
685+
_vm->DetachCurrentThread();
686+
}
687+
}
688+
656689
namespace Rect {
657690
jclass cls;
658691
jmethodID makeLTRB;
@@ -897,6 +930,7 @@ namespace skija {
897930
PathSegment::onLoad(env);
898931
Point::onLoad(env);
899932
PaintFilterCanvas::onLoad(env);
933+
PictureFilterCanvas::onLoad(env);
900934
Rect::onLoad(env);
901935
RRect::onLoad(env);
902936
RSXform::onLoad(env);
@@ -909,6 +943,7 @@ namespace skija {
909943
RRect::onUnload(env);
910944
Rect::onUnload(env);
911945
PaintFilterCanvas::onUnload(env);
946+
PictureFilterCanvas::onUnload(env);
912947
Point::onUnload(env);
913948
PathSegment::onUnload(env);
914949
Path::onUnload(env);

skiko/src/jvmMain/cpp/common/interop.hh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "SkMatrix.h"
1111
#include "SkM44.h"
1212
#include "SkPaint.h"
13+
#include "SkPicture.h"
1314
#include "SkRefCnt.h"
1415
#include "SkRect.h"
1516
#include "SkRRect.h"
@@ -271,6 +272,15 @@ namespace skija {
271272
void detach(jobject obj);
272273
}
273274

275+
namespace PictureFilterCanvas {
276+
extern jmethodID onDrawPictureId;
277+
void onLoad(JNIEnv* env);
278+
void onUnload(JNIEnv* env);
279+
bool onDrawPicture(jobject obj, const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint);
280+
jobject attach(JNIEnv* env, jobject obj);
281+
void detach(jobject obj);
282+
}
283+
274284
namespace Rect {
275285
extern jclass cls;
276286
extern jmethodID makeLTRB;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.jetbrains.skia
2+
3+
import org.jetbrains.skia.impl.NativePointer
4+
5+
internal actual fun PictureFilterCanvas.doInit(ptr: NativePointer) {
6+
PictureFilterCanvas_nInit(this, ptr)
7+
}
8+
9+
private external fun PictureFilterCanvas_nInit(thisPtr: PictureFilterCanvas, canvasPtr: NativePointer)
Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1-
#include <iostream>
21
#include "SkCanvas.h"
32
#include "SkDrawable.h"
43
#include "SkPaintFilterCanvas.h"
54
#include "common.h"
65

7-
class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
6+
class SkikoPaintFilterCanvas : public SkPaintFilterCanvas {
87
public:
9-
SkijaPaintFilterCanvas(
8+
SkikoPaintFilterCanvas(
109
SkCanvas* canvas,
1110
bool unrollDrawable
12-
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable), _onFilter(nullptr), _onFilterPaint(nullptr) {}
11+
) : SkPaintFilterCanvas(canvas), unrollDrawable(unrollDrawable), _onFilter(nullptr), _onFilter_paint(nullptr) {}
1312

1413
SkPaint* onFilterPaint() const {
15-
return _onFilterPaint;
14+
return _onFilter_paint;
1615
}
1716

1817
void init(KInteropPointer onFilter) {
1918
_onFilter = KBooleanCallback(onFilter);
2019
}
20+
2121
protected:
2222
bool onFilter(SkPaint& paint) const override {
23-
_onFilterPaint = &paint;
23+
_onFilter_paint = &paint;
2424
KBoolean result = _onFilter();
25-
_onFilterPaint = nullptr;
25+
_onFilter_paint = nullptr;
2626
return static_cast<bool>(result);
2727
}
2828

@@ -36,26 +36,29 @@ class SkijaPaintFilterCanvas : public SkPaintFilterCanvas {
3636

3737
private:
3838
KBooleanCallback _onFilter;
39-
mutable SkPaint* _onFilterPaint;
39+
40+
// TODO: Support callback with parameters properly
41+
mutable SkPaint* _onFilter_paint;
42+
4043
bool unrollDrawable;
4144
};
4245

4346
SKIKO_EXPORT void org_jetbrains_skia_PaintFilterCanvas__1nInit
4447
(KNativePointer canvasPtr, KInteropPointer onFilter) {
45-
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>((canvasPtr));
48+
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>((canvasPtr));
4649
canvas->init(onFilter);
4750
}
4851

4952
SKIKO_EXPORT KNativePointer org_jetbrains_skia_PaintFilterCanvas__1nMake
5053
(KNativePointer canvasPtr, KBoolean unrollDrawable) {
5154
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
52-
SkijaPaintFilterCanvas* filterCanvas = new SkijaPaintFilterCanvas(canvas, unrollDrawable);
55+
SkikoPaintFilterCanvas* filterCanvas = new SkikoPaintFilterCanvas(canvas, unrollDrawable);
5356
return reinterpret_cast<KNativePointer>(filterCanvas);
5457
}
5558

5659
SKIKO_EXPORT KNativePointer org_jetbrains_skia_PaintFilterCanvas__1nGetOnFilterPaint
5760
(KNativePointer canvasPtr) {
58-
SkijaPaintFilterCanvas* canvas = reinterpret_cast<SkijaPaintFilterCanvas*>((canvasPtr));
61+
SkikoPaintFilterCanvas* canvas = reinterpret_cast<SkikoPaintFilterCanvas*>((canvasPtr));
5962
return reinterpret_cast<KNativePointer>(canvas->onFilterPaint());
6063
}
6164

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "SkNWayCanvas.h"
2+
#include "common.h"
3+
4+
class SkikoPictureFilterCanvas : public SkNWayCanvas {
5+
public:
6+
SkikoPictureFilterCanvas(SkCanvas* canvas) :
7+
SkNWayCanvas(canvas->imageInfo().width(), canvas->imageInfo().height()),
8+
_onDrawPicture(nullptr),
9+
_onDrawPicture_picture(nullptr),
10+
_onDrawPicture_matrix(nullptr),
11+
_onDrawPicture_paint(nullptr) {
12+
this->addCanvas(canvas);
13+
}
14+
15+
KBooleanCallback _onDrawPicture;
16+
17+
// TODO: Support callback with parameters properly
18+
const SkPicture* _onDrawPicture_picture;
19+
const SkMatrix* _onDrawPicture_matrix;
20+
const SkPaint* _onDrawPicture_paint;
21+
22+
protected:
23+
void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) override {
24+
_onDrawPicture_picture = picture;
25+
_onDrawPicture_matrix = matrix;
26+
_onDrawPicture_paint = paint;
27+
28+
KBoolean handled = _onDrawPicture();
29+
30+
_onDrawPicture_picture = nullptr;
31+
_onDrawPicture_matrix = nullptr;
32+
_onDrawPicture_paint = nullptr;
33+
34+
if (!handled) {
35+
SkCanvas::onDrawPicture(picture, matrix, paint);
36+
}
37+
}
38+
};
39+
40+
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nMake
41+
(SkCanvas* canvas) {
42+
SkikoPictureFilterCanvas* filterCanvas = new SkikoPictureFilterCanvas(canvas);
43+
return reinterpret_cast<KNativePointer>(filterCanvas);
44+
}
45+
46+
SKIKO_EXPORT void org_jetbrains_skia_SkikoPictureFilterCanvas__1nInit
47+
(KNativePointer canvasPtr, KInteropPointer onDrawPicture) {
48+
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
49+
canvas->_onDrawPicture = KBooleanCallback(onDrawPicture);
50+
}
51+
52+
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_picture
53+
(KNativePointer canvasPtr) {
54+
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
55+
return reinterpret_cast<KNativePointer>(const_cast<SkPicture *>(canvas->_onDrawPicture_picture));
56+
}
57+
58+
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_matrix
59+
(KNativePointer canvasPtr) {
60+
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
61+
return reinterpret_cast<KNativePointer>(const_cast<SkMatrix *>(canvas->_onDrawPicture_matrix));
62+
}
63+
64+
SKIKO_EXPORT KNativePointer org_jetbrains_skia_SkikoPictureFilterCanvas__1nGetOnDrawPicture_paint
65+
(KNativePointer canvasPtr) {
66+
SkikoPictureFilterCanvas* canvas = reinterpret_cast<SkikoPictureFilterCanvas*>(canvasPtr);
67+
return reinterpret_cast<KNativePointer>(const_cast<SkPaint *>(canvas->_onDrawPicture_paint));
68+
}

0 commit comments

Comments
 (0)