Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new saveLayer method to Canvas (already exists in Skia) #995

Merged
merged 2 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions skiko/src/commonMain/kotlin/org/jetbrains/skia/Canvas.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,36 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
}
}

fun saveLayer(layerRec: SaveLayerRec): Int {
eymar marked this conversation as resolved.
Show resolved Hide resolved
return try {
Stats.onNativeCall()
if (layerRec.bounds != null) {
_nSaveLayerSaveLayerRecRect(
_ptr,
layerRec.bounds.left,
layerRec.bounds.top,
layerRec.bounds.right,
layerRec.bounds.bottom,
getPtr(layerRec.paint),
getPtr(layerRec.backdrop),
getPtr(layerRec.colorSpace),
layerRec.saveLayerFlags.mask
)
} else {
_nSaveLayerSaveLayerRec(
_ptr,
getPtr(layerRec.paint),
getPtr(layerRec.backdrop),
getPtr(layerRec.colorSpace),
layerRec.saveLayerFlags.mask
)
}
} finally {
reachabilityBarrier(this)
reachabilityBarrier(layerRec)
}
}

val saveCount: Int
get() = try {
Stats.onNativeCall()
Expand All @@ -1369,6 +1399,39 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
return this
}

class SaveLayerRec(
val bounds: Rect? = null,
val paint: Paint? = null,
val backdrop: ImageFilter? = null,
val colorSpace: ColorSpace? = null,
val saveLayerFlags: SaveLayerFlags = SaveLayerFlags()
)

enum class SaveLayerFlagsSet(val mask: Int) {
PreserveLCDText(1 shl 1),
InitWithPrevious(1 shl 2),
F16ColorType(1 shl 4)
}

class SaveLayerFlags internal constructor(internal val mask: Int) {
constructor(vararg flagsSet: SaveLayerFlagsSet) : this(flagsSet.fold(0) { acc, flag -> acc or flag.mask })

operator fun contains(flag: SaveLayerFlagsSet): Boolean = (mask and flag.mask) != 0

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as SaveLayerFlags

return mask == other.mask
}

override fun hashCode(): Int {
return mask
}
}

private object _FinalizerHolder {
val PTR = Canvas_nGetFinalizer()
}
Expand Down Expand Up @@ -1645,6 +1708,29 @@ private external fun _nSaveLayerRect(
paintPtr: NativePointer
): Int

@ExternalSymbolName("org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRec")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRec")
private external fun _nSaveLayerSaveLayerRec(
ptr: NativePointer,
paintPtr: NativePointer,
backdropImageFilterPtr: NativePointer,
colorSpacePtr: NativePointer,
saveLayerFlags: Int
): Int

@ExternalSymbolName("org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRecRect")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRecRect")
private external fun _nSaveLayerSaveLayerRecRect(
ptr: NativePointer,
left: Float,
top: Float,
right: Float,
bottom: Float,
paintPtr: NativePointer,
backdropImageFilterPtr: NativePointer,
colorSpacePtr: NativePointer,
saveLayerFlags: Int
): Int

@ExternalSymbolName("org_jetbrains_skia_Canvas__1nGetSaveCount")
@ModuleImport("./skiko.mjs", "org_jetbrains_skia_Canvas__1nGetSaveCount")
Expand Down
64 changes: 57 additions & 7 deletions skiko/src/commonTest/kotlin/org/jetbrains/skia/CanvasTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class CanvasTest {
fun drawString() = runTest {
val surface = Surface.makeRasterN32Premul(100, 100)

val bytes = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
val bytes = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
assertTrue {
bytes.isNotEmpty() && bytes.all { it == 0.toByte() }
}
Expand All @@ -114,7 +114,7 @@ class CanvasTest {
}
)

val bytes2 = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
val bytes2 = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
assertTrue {
bytes2.isNotEmpty() && bytes2.any { it != 0.toByte() }
}
Expand Down Expand Up @@ -210,7 +210,7 @@ class CanvasTest {
surface.canvas.drawBlackPixel(1, 1)

surface.assertPixelsMatch(
IntArray(16){ index ->
IntArray(16) { index ->
when (index) {
10, 11, 14, 15 -> 0xff000000.toInt()
else -> 0xffffffff.toInt()
Expand All @@ -233,7 +233,7 @@ class CanvasTest {
@Test
fun testRotateXY() {
val surface = whiteSurface(4, 4)
surface.canvas.rotate(deg = 90f, x = 2f, y=2f)
surface.canvas.rotate(deg = 90f, x = 2f, y = 2f)
surface.canvas.drawBlackPixel(0, 0)

surface.assertSingleBlackPixelAt(3, 0)
Expand All @@ -247,7 +247,7 @@ class CanvasTest {
surface.canvas.drawBlackPixel(0, 2)

surface.assertPixelsMatch(
IntArray(16){ index ->
IntArray(16) { index ->
when (index) {
// Skewing skews the shape of the pixel itself, so it becomes a parallelogram
9 -> 0xff3f3f3f.toInt()
Expand All @@ -258,6 +258,56 @@ class CanvasTest {
)
}

@Test
fun testSaveLayerRecRect() {
val surface = whiteSurface(5, 5)

surface.canvas.saveLayer(
Canvas.SaveLayerRec(
bounds = Rect(1f, 1f, 4f, 4f),
saveLayerFlags = Canvas.SaveLayerFlags(Canvas.SaveLayerFlagsSet.InitWithPrevious)
)
)

val black = Paint().also { it.setARGB(255, 0, 0, 0) }
surface.canvas.drawRect(Rect(1f, 1f, 4f, 4f), black)

surface.canvas.restore()

surface.assertPixelsMatch(
intArrayOf(
Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE,
)
)
}


@Test
fun testSaveLayerRec() {
val surface = whiteSurface(5, 5)

surface.canvas.saveLayer(Canvas.SaveLayerRec(saveLayerFlags = Canvas.SaveLayerFlags(Canvas.SaveLayerFlagsSet.InitWithPrevious)))

val black = Paint().also { it.setARGB(255, 0, 0, 0) }
surface.canvas.drawRect(Rect(1f, 1f, 4f, 4f), black)

surface.canvas.restore()

surface.assertPixelsMatch(
intArrayOf(
Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.BLACK, Color.BLACK, Color.BLACK, Color.WHITE,
Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE,
)
)
}


private fun whiteSurface(width: Int, height: Int): Surface {
val surface = Surface.makeRasterN32Premul(width, height)
Expand Down Expand Up @@ -286,8 +336,8 @@ class CanvasTest {


private fun Surface.assertSingleBlackPixelAt(x: Int, y: Int) {
val pixArray = IntArray(width * height){ 0xffffffff.toInt() }
pixArray[y*width + x] = 0xff000000.toInt()
val pixArray = IntArray(width * height) { 0xffffffff.toInt() }
pixArray[y * width + x] = 0xff000000.toInt()

assertPixelsMatch(pixArray)
}
Expand Down
21 changes: 21 additions & 0 deletions skiko/src/jvmMain/cpp/common/Canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,27 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CanvasKt__1nSaveLayerR
return canvas->saveLayer(&bounds, paint);
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CanvasKt__1nSaveLayerSaveLayerRec
(JNIEnv* env, jclass jclass, jlong ptr, jlong paintPtr, jlong backdropImageFilterPtr, jlong colorSpacePtr, jint saveLayerFlags) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(ptr));
SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr));
SkImageFilter* backdrop = reinterpret_cast<SkImageFilter*>(backdropImageFilterPtr);
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(colorSpacePtr);

return canvas->saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, colorSpace, saveLayerFlags));
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CanvasKt__1nSaveLayerSaveLayerRecRect
(JNIEnv* env, jclass jclass, jlong ptr, jfloat left, jfloat top, jfloat right, jfloat bottom, jlong paintPtr, jlong backdropImageFilterPtr, jlong colorSpacePtr, jint saveLayerFlags) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(ptr));
SkRect bounds {left, top, right, bottom};
SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr));
SkImageFilter* backdrop = reinterpret_cast<SkImageFilter*>(backdropImageFilterPtr);
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(colorSpacePtr);

return canvas->saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, colorSpace, saveLayerFlags));
}

extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CanvasKt__1nGetSaveCount(JNIEnv* env, jclass jclass, jlong ptr) {
return reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(ptr))->getSaveCount();
}
Expand Down
21 changes: 21 additions & 0 deletions skiko/src/nativeJsMain/cpp/Canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,27 @@ SKIKO_EXPORT KInt org_jetbrains_skia_Canvas__1nSaveLayerRect
return canvas->saveLayer(&bounds, paint);
}

SKIKO_EXPORT KInt org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRec
(KNativePointer ptr, KNativePointer paintPtr, KNativePointer backdropImageFilterPtr, KNativePointer colorSpacePtr, KInt saveLayerFlags) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((ptr));
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
SkImageFilter* backdrop = reinterpret_cast<SkImageFilter*>(backdropImageFilterPtr);
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(colorSpacePtr);

return canvas->saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, colorSpace, saveLayerFlags));
}

SKIKO_EXPORT KInt org_jetbrains_skia_Canvas__1nSaveLayerSaveLayerRecRect
(KNativePointer ptr, KFloat left, KFloat top, KFloat right, KFloat bottom, KNativePointer paintPtr, KNativePointer backdropImageFilterPtr, KNativePointer colorSpacePtr, KInt saveLayerFlags) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((ptr));
SkRect bounds {left, top, right, bottom};
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
SkImageFilter* backdrop = reinterpret_cast<SkImageFilter*>(backdropImageFilterPtr);
SkColorSpace* colorSpace = reinterpret_cast<SkColorSpace*>(colorSpacePtr);

return canvas->saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, colorSpace, saveLayerFlags));
}

SKIKO_EXPORT KInt org_jetbrains_skia_Canvas__1nGetSaveCount(KNativePointer ptr) {
return reinterpret_cast<SkCanvas*>((ptr))->getSaveCount();
}
Expand Down
Loading