Unverified Commit a55a6b09 authored by Oleksandr Karpovich's avatar Oleksandr Karpovich Committed by GitHub

add Canvas methods implementations for nativeJsMain (#323)

* CanvasTest.kt: drawString

* CanvasTest.kt: localToDevice

* CanvasTest.kt: setMatrix

* CanvasTest.kt: drawPatch

* add Matrix44.fromInteropPointer to make it reusable
Co-authored-by: 's avatarOleksandr Karpovich <oleksandr.karpovich@jetbrains.com>
parent d90660e8
...@@ -1126,8 +1126,7 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int ...@@ -1126,8 +1126,7 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
val localToDevice: Matrix44 val localToDevice: Matrix44
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
val mat = _nGetLocalToDevice(_ptr) Matrix44.fromInteropPointer { interopPointer -> _nGetLocalToDevice(_ptr, interopPointer) }
Matrix44(*mat)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
...@@ -1627,7 +1626,7 @@ private external fun _nDrawPaint(ptr: NativePointer, paintPtr: NativePointer) ...@@ -1627,7 +1626,7 @@ private external fun _nDrawPaint(ptr: NativePointer, paintPtr: NativePointer)
private external fun _nSetMatrix(ptr: NativePointer, matrix: InteropPointer) private external fun _nSetMatrix(ptr: NativePointer, matrix: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_Canvas__1nGetLocalToDevice") @ExternalSymbolName("org_jetbrains_skia_Canvas__1nGetLocalToDevice")
private external fun _nGetLocalToDevice(ptr: NativePointer): FloatArray private external fun _nGetLocalToDevice(ptr: NativePointer, resultFloats: InteropPointer): FloatArray
@ExternalSymbolName("org_jetbrains_skia_Canvas__1nResetMatrix") @ExternalSymbolName("org_jetbrains_skia_Canvas__1nResetMatrix")
private external fun _nResetMatrix(ptr: NativePointer) private external fun _nResetMatrix(ptr: NativePointer)
......
package org.jetbrains.skia package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.withResult
/** /**
* *
* 4x4 matrix used by SkCanvas and other parts of Skia. * 4x4 matrix used by SkCanvas and other parts of Skia.
...@@ -53,6 +56,11 @@ class Matrix44(vararg mat: Float) { ...@@ -53,6 +56,11 @@ class Matrix44(vararg mat: Float) {
companion object { companion object {
val IDENTITY = Matrix44(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f) val IDENTITY = Matrix44(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f)
internal fun fromInteropPointer(block: (InteropPointer) -> Unit): Matrix44 {
val result = withResult(FloatArray(16), block)
return Matrix44(*result)
}
} }
/** /**
......
package org.jetbrains.skia package org.jetbrains.skia
import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skia.util.assertContentSame import org.jetbrains.skia.util.assertContentSame
import org.jetbrains.skia.util.imageFromIntArray import org.jetbrains.skia.util.imageFromIntArray
import org.jetbrains.skiko.tests.SkipJsTarget
import org.jetbrains.skiko.tests.SkipNativeTarget
import org.jetbrains.skiko.tests.runTest
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertTrue
class CanvasTest { class CanvasTest {
@Test @Test
...@@ -83,4 +89,105 @@ class CanvasTest { ...@@ -83,4 +89,105 @@ class CanvasTest {
assertContentSame(expected, surface.makeImageSnapshot(), 0.25) assertContentSame(expected, surface.makeImageSnapshot(), 0.25)
} }
private suspend fun fontInter36() =
Font(Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf"), 36f)
// TODO(karpovich): enable for all platforms
// native and js don't work: resulting image has no changed pixels (typeface implementations required)
@Test @SkipNativeTarget @SkipJsTarget
fun drawString() = runTest {
val surface = Surface.makeRasterN32Premul(100, 100)
val bytes = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
assertTrue {
bytes.isNotEmpty() && bytes.all { it == 0.toByte() }
}
surface.canvas.drawString(
s = "Hello world!",
x = 10f, y = 10f,
font = fontInter36(),
paint = Paint().apply {
color = Color.RED
setStroke(false)
}
)
val bytes2 = Bitmap.makeFromImage(surface.makeImageSnapshot()).readPixels()!!
assertTrue {
bytes2.isNotEmpty() && bytes2.any { it != 0.toByte() }
}
}
@Test
fun testLocalToDevice() = runTest {
val surface = Surface.makeRasterN32Premul(100, 100)
val expectedArray = FloatArray(16) { if (it % 5 == 0) 1f else 0f }
assertContentEquals(expectedArray, surface.canvas.localToDevice.mat)
surface.canvas.scale(2f, 2f)
val expectedAfterScale = floatArrayOf(
2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
)
assertContentEquals(expectedAfterScale, surface.canvas.localToDevice.mat)
surface.canvas.resetMatrix()
assertContentEquals(expectedArray, surface.canvas.localToDevice.mat)
}
@Test
fun testSetMatrix() = runTest {
val surface = Surface.makeRasterN32Premul(100, 100)
val expectedArray = FloatArray(16) { if (it % 5 == 0) 1f else 0f }
assertContentEquals(expectedArray, surface.canvas.localToDevice.mat)
surface.canvas.setMatrix(Matrix33.makeScale(2f, 2f))
val expectedAfterScale = floatArrayOf(
2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
)
assertContentEquals(expectedAfterScale, surface.canvas.localToDevice.mat)
}
@Test
fun drawPatch() = runTest {
// source: https://fiddle.skia.org/c/e96c5f9aa21fb97c25058d9e7b9be3a9
val surface = Surface.makeRasterN32Premul(8, 8)
val points = arrayOf(
3, 1, 4, 2, 5, 1, 7, 3, 6, 4, 7, 5, 5, 7, 4, 6, 3, 7, 1, 5, 2, 4, 1, 3
).toList().chunked(2) {
Point(it[0].toFloat(), it[1].toFloat())
}
surface.canvas.drawPatch(
cubics = points.toTypedArray(),
colors = intArrayOf(Color.RED, Color.BLUE, Color.YELLOW, Color.CYAN),
texCoords = null,
paint = Paint()
)
val expected = imageFromIntArray(
pixArray = intArrayOf(
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffcb0634.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xffb63b49.toInt(), 0xff9b3064.toInt(), 0xff762b8a.toInt(), 0xff4b1cb4.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xff8d6e73.toInt(), 0xff866479.toInt(), 0xff7a6386.toInt(), 0xff675698.toInt(), 0xff3632c9.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0xff36cdc9.toInt(), 0xff67a998.toInt(), 0xff7a9c86.toInt(), 0xff869b79.toInt(), 0xff8d9173.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xff4be3b4.toInt(), 0xff76d48a.toInt(), 0xff9bcf64.toInt(), 0xffb6c449.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffcbf934.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
),
imageWidth = 8
)
assertContentSame(expected = expected, got = surface.makeImageSnapshot(), sensitivity = 0.25)
}
} }
...@@ -229,13 +229,13 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nResetMatri ...@@ -229,13 +229,13 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nResetMatri
canvas->resetMatrix(); canvas->resetMatrix();
} }
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_CanvasKt__1nGetLocalToDevice extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nGetLocalToDevice
(JNIEnv* env, jclass jclass, jlong canvasPtr) { (JNIEnv* env, jclass jclass, jlong canvasPtr, jfloatArray resultingMatrix) {
jfloat *floats = env->GetFloatArrayElements(resultingMatrix, NULL);
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(canvasPtr)); SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(canvasPtr));
SkM44 matrix = canvas->getLocalToDevice(); SkM44 matrix = canvas->getLocalToDevice();
std::vector<float> floats(16); matrix.getRowMajor(floats);
matrix.getRowMajor(floats.data()); env->ReleaseFloatArrayElements(resultingMatrix, floats, 0);
return javaFloatArray(env, floats);
} }
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nClipRect extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nClipRect
......
...@@ -124,23 +124,15 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawRegion ...@@ -124,23 +124,15 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawRegion
canvas->drawRegion(*region, *paint); canvas->drawRegion(*region, *paint);
} }
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawString SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawString
(KNativePointer canvasPtr, KInteropPointer stringObj, KFloat x, KFloat y, KNativePointer skFontPtr, KNativePointer paintPtr) { (KNativePointer canvasPtr, KInteropPointer stringObj, KFloat x, KFloat y, KNativePointer skFontPtr, KNativePointer paintPtr) {
TODO("implement org_jetbrains_skia_Canvas__1nDrawString"); SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasPtr);
} SkString string = skString(stringObj);
SkFont* font = reinterpret_cast<SkFont*>(skFontPtr);
#if 0 SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawString
(KNativePointer canvasPtr, KInteropPointer stringObj, KFloat x, KFloat y, KNativePointer skFontPtr, KNativePointer paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*> ((canvasPtr));
SkString string = skString(env, stringObj);
SkFont* font = reinterpret_cast<SkFont*> ((skFontPtr));
SkPaint* paint = reinterpret_cast<SkPaint*> ((paintPtr));
canvas->drawString(string, x, y, *font, *paint); canvas->drawString(string, x, y, *font, *paint);
} }
#endif
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawTextBlob SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawTextBlob
...@@ -183,30 +175,18 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawVertices ...@@ -183,30 +175,18 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawVertices
canvas->drawVertices(vertices, static_cast<SkBlendMode>(blendMode), *paint); canvas->drawVertices(vertices, static_cast<SkBlendMode>(blendMode), *paint);
} }
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPatch SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPatch
(KNativePointer ptr, KFloat* cubicsArr, KInt* colorsArr, KFloat* texCoordsArr, KInt blendMode, KNativePointer paintPtr) { (KNativePointer ptr, KFloat* cubicsArr, KInt* colorsArr, KFloat* texCoordsArr, KInt blendMode, KNativePointer paintPtr) {
TODO("implement org_jetbrains_skia_Canvas__1nDrawPatch"); SkCanvas* canvas = reinterpret_cast<SkCanvas*>(ptr);
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPatch
(KNativePointer ptr, KFloat* cubicsArr, KInt* colorsArr, KFloat* texCoordsArr, KInt blendMode, KNativePointer paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*> ((ptr));
KFloat* cubics = env->GetFloatArrayElements(cubicsArr, 0);
KInt* colors = env->GetIntArrayElements(colorsArr, 0);
KFloat* texCoords = texCoordsArr == nullptr ? nullptr : env->GetFloatArrayElements(texCoordsArr, 0);
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr)); SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
canvas->drawPatch(
canvas->drawPatch(reinterpret_cast<SkPoint*>(cubics), reinterpret_cast<SkColor*>(colors), reinterpret_cast<SkPoint*>(texCoords), static_cast<SkBlendMode>(blendMode), *paint); reinterpret_cast<SkPoint*>(cubicsArr),
reinterpret_cast<SkColor*>(colorsArr),
if (texCoords != nullptr) reinterpret_cast<SkPoint*>(texCoordsArr),
env->ReleaseFloatArrayElements(texCoordsArr, texCoords, 0); static_cast<SkBlendMode>(blendMode),
env->ReleaseIntArrayElements(colorsArr, colors, 0); *paint
env->ReleaseFloatArrayElements(cubicsArr, cubics, 0); );
} }
#endif
...@@ -238,21 +218,12 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPaint ...@@ -238,21 +218,12 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPaint
canvas->drawPaint(*paint); canvas->drawPaint(*paint);
} }
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nSetMatrix
(KNativePointer canvasPtr, KFloat* matrixArr) {
TODO("implement org_jetbrains_skia_Canvas__1nSetMatrix");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nSetMatrix SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nSetMatrix
(KNativePointer canvasPtr, KFloat* matrixArr) { (KNativePointer canvasPtr, KFloat* matrixArr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr)); SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
std::unique_ptr<SkMatrix> matrix = skMatrix(env, matrixArr); std::unique_ptr<SkMatrix> matrix = skMatrix(matrixArr);
canvas->setMatrix(*matrix); canvas->setMatrix(*matrix);
} }
#endif
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nResetMatrix SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nResetMatrix
(KNativePointer canvasPtr) { (KNativePointer canvasPtr) {
...@@ -261,22 +232,12 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nResetMatrix ...@@ -261,22 +232,12 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nResetMatrix
} }
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Canvas__1nGetLocalToDevice SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nGetLocalToDevice
(KNativePointer canvasPtr) { (KNativePointer canvasPtr, KFloat* resultingFloats) {
TODO("implement org_jetbrains_skia_Canvas__1nGetLocalToDevice"); SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasPtr);
}
#if 0
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Canvas__1nGetLocalToDevice
(KNativePointer canvasPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
SkM44 matrix = canvas->getLocalToDevice(); SkM44 matrix = canvas->getLocalToDevice();
std::vector<float> floats(16); matrix.getRowMajor(resultingFloats);
matrix.getRowMajor(floats.data());
return javaFloatArray(env, floats);
} }
#endif
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nClipRect SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nClipRect
(KNativePointer canvasPtr, KFloat left, KFloat top, KFloat right, KFloat bottom, KInt mode, KBoolean antiAlias) { (KNativePointer canvasPtr, KFloat left, KFloat top, KFloat right, KFloat bottom, KInt mode, KBoolean antiAlias) {
......
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