Unverified Commit 926c7c69 authored by Aleksandr Veselov's avatar Aleksandr Veselov Committed by GitHub

Implement Canvas::nDrawPoints and Canvas::nDrawVertices, commonize CanvasTest (#286)

parent 3c284175
......@@ -34,23 +34,12 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPoint
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPoints
(KNativePointer canvasPtr, int mode, KFloat* coords, KNativePointer paintPtr) {
TODO("implement org_jetbrains_skia_Canvas__1nDrawPoints");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPoints
(KNativePointer canvasPtr, int mode, KFloat* coords, KNativePointer paintPtr) {
(KNativePointer canvasPtr, int mode, KInt coordsCount, KFloat* coords, KNativePointer paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode);
jsize len = env->GetArrayLength(coords);
KFloat* arr = static_cast<KFloat*>(env->GetPrimitiveArrayCritical(coords, 0));
canvas->drawPoints(skMode, len / 2, reinterpret_cast<SkPoint*>(arr), *paint);
env->ReleasePrimitiveArrayCritical(coords, arr, 0);
canvas->drawPoints(skMode, coordsCount / 2, reinterpret_cast<SkPoint*>(coords), *paint);
}
#endif
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawLine
(KNativePointer canvasPtr, KFloat x0, KFloat y0, KFloat x1, KFloat y1, KNativePointer paintPtr) {
......@@ -175,22 +164,15 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPicture
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawVertices
(KNativePointer ptr, KInt verticesMode, KFloat* positionsArr, KInt* colorsArr, KFloat* texCoordsArr, KShort* indexArr, KInt blendMode, KNativePointer paintPtr) {
TODO("implement org_jetbrains_skia_Canvas__1nDrawVertices");
}
#if 0
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawVertices
(KNativePointer ptr, KInt verticesMode, KFloat* positionsArr, KInt* colorsArr, KFloat* texCoordsArr, KShort* indexArr, KInt blendMode, KNativePointer paintPtr) {
(KNativePointer ptr, KInt verticesMode, KInt vertexCount, KFloat* positionsArr, KInt* colorsArr, KFloat* texCoordsArr, KInt indexCount, KShort* indexArr, KInt blendMode, KNativePointer paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*> ((ptr));
int indexCount = indexArr == nullptr ? 0 : env->GetArrayLength(indexArr);
KFloat* positions = env->GetFloatArrayElements(positionsArr, 0);
KInt* colors = colorsArr == nullptr ? nullptr : env->GetIntArrayElements(colorsArr, 0);
KFloat* texCoords = texCoordsArr == nullptr ? nullptr : env->GetFloatArrayElements(texCoordsArr, 0);
const KShort* indices = indexArr == nullptr ? nullptr : env->GetShortArrayElements(indexArr, 0);
KFloat* positions = positionsArr;
KInt* colors = colorsArr;
KFloat* texCoords = texCoordsArr;
KShort* indices = indexArr;
sk_sp<SkVertices> vertices = SkVertices::MakeCopy(
static_cast<SkVertices::VertexMode>(verticesMode),
env->GetArrayLength(positionsArr) / 2,
vertexCount,
reinterpret_cast<SkPoint*>(positions),
reinterpret_cast<SkPoint*>(texCoords),
reinterpret_cast<SkColor*>(colors),
......@@ -199,14 +181,7 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawVertices
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
canvas->drawVertices(vertices, static_cast<SkBlendMode>(blendMode), *paint);
if (texCoords != nullptr)
env->ReleaseFloatArrayElements(texCoordsArr, texCoords, 0);
if (colors != nullptr)
env->ReleaseIntArrayElements(colorsArr, colors, 0);
env->ReleaseFloatArrayElements(positionsArr, positions, 0);
}
#endif
......
......@@ -119,7 +119,7 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
fun drawPoints(coords: FloatArray, paint: Paint): Canvas {
Stats.onNativeCall()
interopScope {
_nDrawPoints(_ptr, 0 /* SkCanvas::PointMode::kPoints_PointMode */, toInterop(coords), getPtr(paint))
_nDrawPoints(_ptr, 0 /* SkCanvas::PointMode::kPoints_PointMode */, coords.size, toInterop(coords), getPtr(paint))
}
reachabilityBarrier(paint)
return this
......@@ -178,7 +178,7 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
fun drawLines(coords: FloatArray, paint: Paint): Canvas {
Stats.onNativeCall()
interopScope {
_nDrawPoints(_ptr, 1 /* SkCanvas::PointMode::kLines_PointMode */, toInterop(coords), getPtr(paint))
_nDrawPoints(_ptr, 1 /* SkCanvas::PointMode::kLines_PointMode */, coords.size, toInterop(coords),getPtr(paint))
}
reachabilityBarrier(paint)
return this
......@@ -235,7 +235,7 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
fun drawPolygon(coords: FloatArray, paint: Paint): Canvas {
Stats.onNativeCall()
interopScope {
_nDrawPoints(_ptr, 2 /* SkCanvas::PointMode::kPolygon_PointMode */, toInterop(coords), getPtr(paint))
_nDrawPoints(_ptr, 2 /* SkCanvas::PointMode::kPolygon_PointMode */, coords.size, toInterop(coords), getPtr(paint))
}
reachabilityBarrier(paint)
return this
......@@ -595,9 +595,11 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
_nDrawVertices(
_ptr,
0 /* kTriangles_VertexMode */,
positions.size,
toInterop(Point.flattenArray(positions)),
toInterop(colors),
toInterop(Point.flattenArray(texCoords)),
indices?.size ?: 0,
toInterop(indices),
mode.ordinal,
getPtr(paint)
......@@ -700,9 +702,11 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
_nDrawVertices(
_ptr,
1 /* kTriangleStrip_VertexMode */,
positions.size,
toInterop(Point.flattenArray(positions)),
toInterop(colors),
toInterop(Point.flattenArray(texCoords)),
indices?.size ?: 0,
toInterop(indices),
mode.ordinal,
getPtr(paint)
......@@ -805,9 +809,11 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
_nDrawVertices(
_ptr,
2 /* kTriangleFan_VertexMode */,
positions.size,
toInterop(Point.flattenArray(positions)),
toInterop(colors),
toInterop(Point.flattenArray(texCoords)),
indices?.size ?: 0,
toInterop(indices),
mode.ordinal,
getPtr(paint)
......@@ -866,9 +872,11 @@ open class Canvas internal constructor(ptr: NativePointer, managed: Boolean, int
_nDrawVertices(
_ptr,
vertexMode.ordinal,
points,
toInterop(positions),
toInterop(colors),
toInterop(texCoords),
indices?.size ?: 0,
toInterop(indices),
mode.ordinal,
getPtr(paint)
......@@ -1470,7 +1478,7 @@ private external fun _nMakeFromBitmap(bitmapPtr: NativePointer, flags: Int, pixe
private external fun _nDrawPoint(ptr: NativePointer, x: Float, y: Float, paintPtr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_Canvas__1nDrawPoints")
private external fun _nDrawPoints(ptr: NativePointer, mode: Int, coords: InteropPointer, paintPtr: NativePointer)
private external fun _nDrawPoints(ptr: NativePointer, mode: Int, coordsCount: Int, coords: InteropPointer, paintPtr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_Canvas__1nDrawLine")
private external fun _nDrawLine(ptr: NativePointer, x0: Float, y0: Float, x1: Float, y1: Float, paintPtr: NativePointer)
......@@ -1582,9 +1590,11 @@ private external fun _nDrawPicture(ptr: NativePointer, picturePtr: NativePointer
private external fun _nDrawVertices(
ptr: NativePointer,
verticesMode: Int,
vertexCount: Int,
cubics: InteropPointer,
colors: InteropPointer,
texCoords: InteropPointer,
indexCount: Int,
indices: InteropPointer,
blendMode: Int,
paintPtr: NativePointer
......
package org.jetbrains.skia
import org.jetbrains.skia.util.assertContentSame
import org.jetbrains.skia.util.imageFromIntArray
import kotlin.test.Test
class CanvasTest {
@Test
fun drawVertices() {
val surface = Surface.makeRasterN32Premul(8, 8)
val positions = Point.flattenArray(
arrayOf(
Point(0.0f, 0.0f),
Point(4.0f, 4.0f),
Point(8.0f, 0.0f),
Point(4.0f, 4.0f),
Point(0.0f, 8.0f),
Point(8.0f, 8.0f),
)
)!!
val colors = listOf(
Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW
).toIntArray()
val indices = shortArrayOf(0, 1, 2, 3, 4, 5)
surface.canvas.drawVertices(
VertexMode.TRIANGLES,
positions,
colors,
positions,
indices,
BlendMode.SRC_OVER,
Paint()
)
// Hint: use surface.makeImageSnapshot().printBitmap() to print image as kotlin array
val expected = imageFromIntArray(intArrayOf(
0x00000000.toInt(), 0xffbf2020.toInt(), 0xff9f2040.toInt(), 0xff802060.toInt(), 0xff602080.toInt(), 0xff40209f.toInt(), 0xff2020bf.toInt(), 0xff0020df.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xff806020.toInt(), 0xff606040.toInt(), 0xff406060.toInt(), 0xff206080.toInt(), 0xff00609f.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff409f20.toInt(), 0xff209f40.toInt(), 0xff009f60.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff00df20.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff20ffdf.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff60bfdf.toInt(), 0xff60dfbf.toInt(), 0xff60ff9f.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xff9f80df.toInt(), 0xff9f9fbf.toInt(), 0xff9fbf9f.toInt(), 0xff9fdf80.toInt(), 0xff9fff60.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0xffdf40df.toInt(), 0xffdf60bf.toInt(), 0xffdf809f.toInt(), 0xffdf9f80.toInt(), 0xffdfbf60.toInt(), 0xffdfdf40.toInt(), 0xffdfff20.toInt(),
), surface.width)
assertContentSame(expected, surface.makeImageSnapshot(), 0.25)
}
@Test
fun drawPoints() {
val surface = Surface.makeRasterN32Premul(8, 8)
surface.canvas.drawPoints(
paint = Paint().apply {
setStroke(true)
setARGB(0xFF, 0xFF, 0x00, 0x00)
strokeWidth = 2.0f
},
coords = arrayOf(
Point(1.0f, 1.0f),
Point(4.0f, 4.0f),
Point(3.0f, 5.0f),
Point(2.0f, 6.0f),
Point(8.0f, 8.0f),
)
)
val expected = imageFromIntArray(intArrayOf(
0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(),
), surface.width)
assertContentSame(expected, surface.makeImageSnapshot(), 0.25)
}
}
package org.jetbrains.skia.util
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Color
import org.jetbrains.skia.Image
import kotlin.math.abs
fun assertContentSame(expected: Image, got: Image, sensitivity: Double) {
require(sensitivity in 0.0..1.0)
val sensitivity255 = (sensitivity * 255).toInt()
if (expected.width == got.width && expected.height == got.height) {
val expectedPixels = Bitmap.makeFromImage(expected)
val gotPixels = Bitmap.makeFromImage(got)
for (y in 0 until expected.height) {
for (x in 0 until expected.width) {
val color1 = expectedPixels.getColor(x, y)
val color2 = gotPixels.getColor(x, y)
val pixelsAreSame = run {
if (abs(Color.getR(color1) - Color.getR(color2)) > sensitivity255) {
return@run false
}
if (abs(Color.getG(color1) - Color.getG(color2)) > sensitivity255) {
return@run false
}
if (abs(Color.getB(color1) - Color.getB(color2)) > sensitivity255) {
return@run false
}
if (abs(Color.getA(color1) - Color.getA(color2)) > sensitivity255) {
return@run false
}
true
}
if (!pixelsAreSame) {
throw AssertionError("Image mismatch at pixel [$x, $y]: expected $color1, got $color2")
}
}
}
} else {
throw AssertionError("Image size mismatch")
}
}
\ No newline at end of file
package org.jetbrains.skia.util
import org.jetbrains.skia.*
fun makeByteArrayFromRGBArray(pixArray: IntArray): ByteArray {
var result = ByteArray(pixArray.size * 4)
var off = 0
for (pix in pixArray) {
result[off++] = Color.getR(pix).toByte()
result[off++] = Color.getG(pix).toByte()
result[off++] = Color.getB(pix).toByte()
result[off++] = Color.getA(pix).toByte()
}
return result
}
fun imageFromIntArray(pixArray: IntArray, imageWidth: Int) = Image.makeRaster(
imageInfo = ImageInfo(imageWidth, pixArray.size / imageWidth, ColorType.RGBA_8888, ColorAlphaType.UNPREMUL),
rowBytes = imageWidth * 4 /* Four bytes per pixel */,
bytes = makeByteArrayFromRGBArray(pixArray)
)
\ No newline at end of file
package org.jetbrains.skia.util
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Image
fun Image.printBitmap() {
val pixels = Bitmap.makeFromImage(this)
for (y in 0 until height) {
for (x in 0 until width) {
val pix = pixels.getColor(x, y)
val pixStr = pix.toUInt().toString(16).padStart(8, '0')
print("0x$pixStr.toInt(), ")
}
println()
}
}
\ No newline at end of file
......@@ -31,7 +31,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPoint
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPoints
(JNIEnv* env, jclass jclass, jlong canvasPtr, int mode, jfloatArray coords, jlong paintPtr) {
(JNIEnv* env, jclass jclass, jlong canvasPtr, int mode, jint _coordsCount, jfloatArray coords, jlong paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(static_cast<uintptr_t>(canvasPtr));
SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr));
SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode);
......@@ -154,7 +154,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPictur
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawVertices
(JNIEnv* env, jclass jclass, jlong ptr, jint verticesMode, jfloatArray positionsArr, jintArray colorsArr, jfloatArray texCoordsArr, jshortArray indexArr, jint blendMode, jlong paintPtr) {
(JNIEnv* env, jclass jclass, jlong ptr, jint verticesMode, jint _vertexCount, jfloatArray positionsArr, jintArray colorsArr, jfloatArray texCoordsArr, jint _indexCount, jshortArray indexArr, jint blendMode, jlong paintPtr) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*> (static_cast<uintptr_t>(ptr));
int indexCount = indexArr == nullptr ? 0 : env->GetArrayLength(indexArr);
jfloat* positions = env->GetFloatArrayElements(positionsArr, 0);
......
package org.jetbrains.skiko
import org.jetbrains.skia.*
import org.jetbrains.skiko.util.ScreenshotTestRule
import org.junit.Rule
import org.junit.Test
class CanvasTest {
@get:Rule
val screenshots = ScreenshotTestRule()
@Test
fun drawVertices() {
val surface = Surface.makeRasterN32Premul(16, 16)
val positions = Point.flattenArray(
arrayOf(
Point(0f, 0f),
Point(16f, 0f),
Point(8f, 8f),
Point(16f, 8f),
Point(0f, 16f),
Point(16f, 16f),
)
)!!
val colors = listOf(
Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW
).toIntArray()
val indices = shortArrayOf(0, 1, 2, 3, 4, 5)
surface.canvas.drawVertices(
VertexMode.TRIANGLES,
positions,
colors,
positions,
indices,
BlendMode.SRC_OVER,
Paint()
)
screenshots.assert(surface.makeImageSnapshot())
}
}
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