Unverified Commit c24886be authored by Aleksandr Veselov's avatar Aleksandr Veselov Committed by GitHub

Implement native methods of Codec (#346)

parent f1288f35
......@@ -26,10 +26,11 @@ config.webpack.output = Object.assign(config.webpack.output || {}, {
config.webpack.module.rules.push(
{
test: /\.(ttf|woff|woff2)$/,
type: 'asset/resource',
generator: {
filename: "assets/fonts/[name][ext]"
}
type: 'asset/resource'
},
{
test: /\.(png|jpg|gif)$/,
type: 'asset/resource'
},
{
test: /\.txt$/,
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.withResult
/**
* Information about individual frames in a multi-framed image.
*/
......@@ -66,6 +69,32 @@ class AnimationFrameInfo(
*/
internal var frameRect: IRect
) {
companion object {
private const val REPR_SIZE = 11
private fun fromIntArray(repr: IntArray, index: Int = 0): AnimationFrameInfo {
val offset = index * REPR_SIZE
return AnimationFrameInfo(
repr[offset + 0],
repr[offset + 1],
repr[offset + 2] != 0,
repr[offset + 3],
repr[offset + 4] != 0,
repr[offset + 5],
repr[offset + 6],
IRect(repr[offset + 7], repr[offset + 8], repr[offset + 9], repr[offset + 10])
)
}
internal fun fromInteropPointer(block: (InteropPointer) -> Unit): AnimationFrameInfo {
return fromIntArray(withResult(IntArray(REPR_SIZE), block))
}
internal fun fromInteropArrayPointer(size: Int, block: (InteropPointer) -> Unit): Array<AnimationFrameInfo> {
val repr = withResult(IntArray(REPR_SIZE * size), block)
return Array(size) { fromIntArray(repr, it) }
}
}
internal constructor(
requiredFrame: Int,
duration: Int,
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.Managed
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.getPtr
class Codec internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR), IHasImageInfo {
companion object {
......@@ -48,8 +44,10 @@ class Codec internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHo
override val imageInfo: ImageInfo
get() = try {
if (_imageInfo == null) {
Stats.onNativeCall()
_imageInfo = Codec_nGetImageInfo(_ptr)
_imageInfo = ImageInfo.createUsing(
_ptr = _ptr,
_nGetImageInfo = ::Codec_nGetImageInfo
)
}
_imageInfo!!
} finally {
......@@ -289,7 +287,7 @@ class Codec internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHo
fun getFrameInfo(frame: Int): AnimationFrameInfo {
return try {
Stats.onNativeCall()
_nGetFrameInfo(_ptr, frame)
AnimationFrameInfo.fromInteropPointer { _nGetFrameInfo(_ptr, frame, it) }
} finally {
reachabilityBarrier(this)
}
......@@ -312,7 +310,17 @@ class Codec internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHo
val framesInfo: Array<AnimationFrameInfo>
get() = try {
Stats.onNativeCall()
_nGetFramesInfo(_ptr)
val buffer = _nGetFramesInfo(_ptr)
val size = FramesInfo_nGetSize(buffer)
if (size > 0) {
AnimationFrameInfo.fromInteropArrayPointer(size) {
FramesInfo_nGetInfos(buffer, it)
}
} else {
arrayOf()
}.also {
FramesInfo_nDelete(buffer)
}
} finally {
reachabilityBarrier(this)
}
......@@ -353,7 +361,7 @@ class Codec internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHo
private external fun Codec_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Codec__1nGetImageInfo")
private external fun Codec_nGetImageInfo(ptr: NativePointer): ImageInfo?
private external fun Codec_nGetImageInfo(ptr: NativePointer, imageInfo: InteropPointer, colorSpacePtrs: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_Codec__1nReadPixels")
private external fun Codec_nReadPixels(ptr: NativePointer, bitmapPtr: NativePointer, frame: Int, priorFrame: Int): Int
......@@ -374,10 +382,19 @@ private external fun _nGetEncodedImageFormat(ptr: NativePointer): Int
private external fun _nGetFrameCount(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Codec__1nGetFrameInfo")
private external fun _nGetFrameInfo(ptr: NativePointer, frame: Int): AnimationFrameInfo
private external fun _nGetFrameInfo(ptr: NativePointer, frame: Int, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_Codec__1nGetFramesInfo")
private external fun _nGetFramesInfo(ptr: NativePointer): Array<AnimationFrameInfo>
private external fun _nGetFramesInfo(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Codec__1nGetRepetitionCount")
private external fun _nGetRepetitionCount(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Codec__1nFramesInfo_Delete")
private external fun FramesInfo_nDelete(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_Codec__1nFramesInfo_GetSize")
private external fun FramesInfo_nGetSize(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Codec__1nFramesInfo_GetInfos")
private external fun FramesInfo_nGetInfos(ptr: NativePointer, result: InteropPointer)
package org.jetbrains.skia
import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skia.util.assertContentSame
import org.jetbrains.skiko.tests.runTest
import org.jetbrains.skiko.util.IMAGE_COLORS_8X8
import org.jetbrains.skiko.util.makeSolidColor
import kotlin.test.Test
import kotlin.test.assertEquals
class CodecTest {
@Test
fun decodePNG() = runTest {
val codec = Codec.makeFromData(Data.makeFromResource("./colors_8x8.png"))
assertEquals(1, codec.frameCount)
assertEquals(8, codec.imageInfo.width)
assertEquals(8, codec.imageInfo.height)
assertEquals(EncodedImageFormat.PNG, codec.encodedImageFormat)
val pixels = codec.readPixels()
assertContentSame(IMAGE_COLORS_8X8, Image.makeFromBitmap(pixels), 0.01)
}
@Test
fun decodeGIF() = runTest {
val codec = Codec.makeFromData(Data.makeFromResource("./colored_square.gif"))
assertEquals(5, codec.frameCount)
assertEquals(8, codec.imageInfo.width)
assertEquals(8, codec.imageInfo.height)
assertEquals(EncodedImageFormat.GIF, codec.encodedImageFormat)
val palette = intArrayOf(Color.BLACK, Color.RED, Color.GREEN, Color.BLUE, Color.WHITE)
val pixels = Bitmap()
pixels.allocPixels(codec.imageInfo)
for (frame in 0 until codec.frameCount) {
codec.readPixels(pixels, frame)
val expected = Image.makeSolidColor(palette[frame], 8, 8)
assertContentSame(expected, Image.makeFromBitmap(pixels), 0.01)
}
assertEquals(200, codec.getFrameInfo(3).duration)
val framesInfo = codec.framesInfo
assertEquals(5, framesInfo.size)
for (frameInfo in framesInfo) {
assertEquals(200, frameInfo.duration)
}
}
}
\ No newline at end of file
package org.jetbrains.skiko.util
import org.jetbrains.skia.Image
import org.jetbrains.skia.util.imageFromIntArray
@Suppress("RemoveRedundantCallsOfConversionMethods")
val PIXELS_COLORS_8X8 by lazy { intArrayOf(
0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000.toInt(), 0x00000000.toInt(),
0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000.toInt(), 0x00000000.toInt(),
0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000.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(),
) }
val IMAGE_COLORS_8X8 by lazy { imageFromIntArray(PIXELS_COLORS_8X8, 8) }
fun Image.Companion.makeSolidColor(color: Int, width: Int, height: Int)
= imageFromIntArray(IntArray(width * height) { color }, width)
\ No newline at end of file
......@@ -20,10 +20,11 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_CodecKt__1nMakeFromDa
return reinterpret_cast<jlong>(instance.release());
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_CodecKt_Codec_1nGetImageInfo
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return skija::ImageInfo::toJava(env, instance->getInfo());
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CodecKt_Codec_1nGetImageInfo
(JNIEnv* env, jclass jclass, jlong ptr, jintArray imageInfoResult, jlongArray colorSpaceResultPtr) {
auto instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkImageInfo imageInfo = instance->getInfo();
skija::ImageInfo::writeImageInfoForInterop(env, imageInfo, imageInfoResult, colorSpaceResultPtr);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_CodecKt__1nGetSize
......@@ -61,27 +62,19 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CodecKt__1nGetFrameCou
return instance->getFrameCount();
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_CodecKt__1nGetFrameInfo
(JNIEnv* env, jclass jclass, jlong ptr, jint frame) {
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CodecKt__1nGetFrameInfo
(JNIEnv* env, jclass jclass, jlong ptr, jint frame, jintArray result) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkCodec::FrameInfo info;
instance->getFrameInfo(frame, &info);
return skija::AnimationFrameInfo::toJava(env, info);
skija::AnimationFrameInfo::copyToInterop(env, info, result);
}
extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_CodecKt__1nGetFramesInfo
(JNIEnv* env, jclass jclass, jlong ptr, jint frame) {
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_CodecKt__1nGetFramesInfo
(JNIEnv* env, jclass jclass, jlong ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
SkCodec::FrameInfo info;
std::vector<SkCodec::FrameInfo> frames = instance->getFrameInfo();
jobjectArray res = env->NewObjectArray(frames.size(), skija::AnimationFrameInfo::cls, nullptr);
if (java::lang::Throwable::exceptionThrown(env))
return nullptr;
for (int i = 0; i < frames.size(); ++i) {
skija::AutoLocal<jobject> infoObj(env, skija::AnimationFrameInfo::toJava(env, frames[i]));
env->SetObjectArrayElement(res, i, infoObj.get());
}
return res;
auto* infos = new std::vector<SkCodec::FrameInfo> { instance->getFrameInfo() };
return reinterpret_cast<jlong>(infos);
}
extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CodecKt__1nGetRepetitionCount
......@@ -89,3 +82,20 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CodecKt__1nGetRepetiti
SkCodec* instance = reinterpret_cast<SkCodec*>(static_cast<uintptr_t>(ptr));
return instance->getRepetitionCount();
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CodecKt_FramesInfo_1nDelete
(JNIEnv* env, jclass jclass, jlong ptr) {
delete reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(static_cast<uintptr_t>(ptr));
}
extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_CodecKt_FramesInfo_1nGetSize
(JNIEnv* env, jclass jclass, jlong ptr) {
auto* infos = reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(static_cast<uintptr_t>(ptr));
return infos->size();
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CodecKt_FramesInfo_1nGetInfos
(JNIEnv* env, jclass jclass, jlong ptr, jintArray result) {
auto* infos = reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(static_cast<uintptr_t>(ptr));
skija::AnimationFrameInfo::copyToInterop(env, *infos, result);
}
\ No newline at end of file
......@@ -177,6 +177,34 @@ namespace skija {
IRect::fromSkIRect(env, i.fFrameRect));
return java::lang::Throwable::exceptionThrown(env) ? nullptr : res;
}
void copyToInteropAtIndex(JNIEnv* env, const SkCodec::FrameInfo& info, jintArray dst, jsize index) {
jint repr[11] {
info.fRequiredFrame,
info.fDuration,
static_cast<jint>(info.fFullyReceived),
static_cast<jint>(info.fAlphaType),
static_cast<jint>(info.fHasAlphaWithinBounds),
static_cast<jint>(info.fDisposalMethod),
static_cast<jint>(info.fBlend),
info.fFrameRect.left(),
info.fFrameRect.top(),
info.fFrameRect.right(),
info.fFrameRect.bottom()
};
env->SetIntArrayRegion(dst, index * 11, 11, repr);
}
void copyToInterop(JNIEnv* env, const SkCodec::FrameInfo& info, jintArray dst) {
copyToInteropAtIndex(env, info, dst, 0);
}
void copyToInterop(JNIEnv* env, const std::vector<SkCodec::FrameInfo>& infos, jintArray dst) {
jsize i = 0;
for (const auto& info : infos) {
copyToInteropAtIndex(env, info, dst, i++);
}
}
}
namespace Color4f {
......@@ -495,11 +523,11 @@ namespace skija {
if (rectInts == nullptr)
return std::unique_ptr<SkIRect>(nullptr);
else {
jint *ints = env->GetIntArrayElements(rectInts, NULL);
jint *ints = env->GetIntArrayElements(rectInts, nullptr);
auto result = std::unique_ptr<SkIRect>(new SkIRect{
ints[0], ints[1], ints[2], ints[3]
});
env->ReleaseIntArrayElements(rectInts, ints, NULL);
env->ReleaseIntArrayElements(rectInts, ints, 0);
return result;
}
}
......
......@@ -85,6 +85,8 @@ namespace skija {
void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env);
jobject toJava(JNIEnv* env, const SkCodec::FrameInfo& i);
void copyToInterop(JNIEnv* env, const SkCodec::FrameInfo& info, jintArray dst);
void copyToInterop(JNIEnv* env, const std::vector<SkCodec::FrameInfo>& infos, jintArray dst);
}
template <typename T>
......
......@@ -22,21 +22,13 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_Codec__1nMakeFromData
return reinterpret_cast<KNativePointer>(instance.release());
}
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetImageInfo
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_Codec__1nGetImageInfo");
SKIKO_EXPORT void org_jetbrains_skia_Codec__1nGetImageInfo
(KNativePointer ptr, KInt* imageInfoResult, KNativePointer* colorSpacePtrsArray) {
auto instance = reinterpret_cast<SkCodec*>(ptr);
SkImageInfo imageInfo = instance->getInfo();
skija::ImageInfo::writeImageInfoForInterop(imageInfo, imageInfoResult, colorSpacePtrsArray);
}
#if 0
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetImageInfo
(KNativePointer ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>((ptr));
return skija::ImageInfo::toJava(env, instance->getInfo());
}
#endif
SKIKO_EXPORT KInt org_jetbrains_skia_Codec__1nGetSize
(KNativePointer ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>((ptr));
......@@ -73,45 +65,38 @@ SKIKO_EXPORT KInt org_jetbrains_skia_Codec__1nGetFrameCount
}
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetFrameInfo
(KNativePointer ptr, KInt frame) {
TODO("implement org_jetbrains_skia_Codec__1nGetFrameInfo");
}
#if 0
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetFrameInfo
(KNativePointer ptr, KInt frame) {
SkCodec* instance = reinterpret_cast<SkCodec*>((ptr));
SkCodec::FrameInfo info;
SKIKO_EXPORT void org_jetbrains_skia_Codec__1nGetFrameInfo
(KNativePointer ptr, KInt frame, KInteropPointer result) {
auto* instance = reinterpret_cast<SkCodec*>((ptr));
SkCodec::FrameInfo info{};
instance->getFrameInfo(frame, &info);
return skija::AnimationFrameInfo::toJava(env, info);
skija::AnimationFrameInfo::copyToInterop(info, result);
}
#endif
SKIKO_EXPORT KNativePointer org_jetbrains_skia_Codec__1nGetFramesInfo
(KNativePointer ptr) {
SkCodec* instance = reinterpret_cast<SkCodec*>((ptr));
auto* infos = new std::vector<SkCodec::FrameInfo> { instance->getFrameInfo() };
return reinterpret_cast<KNativePointer>(infos);
}
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetFramesInfo
(KNativePointer ptr, KInt frame) {
TODO("implement org_jetbrains_skia_Codec__1nGetFramesInfo");
SKIKO_EXPORT void org_jetbrains_skia_Codec__1nFramesInfo_Delete
(KNativePointer ptr) {
delete reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(ptr);
}
#if 0
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_Codec__1nGetFramesInfo
(KNativePointer ptr, KInt frame) {
SkCodec* instance = reinterpret_cast<SkCodec*>((ptr));
SkCodec::FrameInfo info;
std::vector<SkCodec::FrameInfo> frames = instance->getFrameInfo();
KInteropPointerArray res = env->NewObjectArray(frames.size(), skija::AnimationFrameInfo::cls, nullptr);
if (java::lang::Throwable::exceptionThrown(env))
return nullptr;
for (int i = 0; i < frames.size(); ++i) {
skija::AutoLocal<KInteropPointer> infoObj(env, skija::AnimationFrameInfo::toJava(env, frames[i]));
env->SetObjectArrayElement(res, i, infoObj.get());
}
return res;
SKIKO_EXPORT KInt org_jetbrains_skia_Codec__1nFramesInfo_GetSize
(KNativePointer ptr) {
auto* infos = reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(ptr);
return static_cast<KInt>(infos->size());
}
#endif
SKIKO_EXPORT void org_jetbrains_skia_Codec__1nFramesInfo_GetInfos
(KNativePointer ptr, KInteropPointer result) {
auto* infos = reinterpret_cast<std::vector<SkCodec::FrameInfo>*>(ptr);
skija::AnimationFrameInfo::copyToInterop(*infos, result);
}
SKIKO_EXPORT KInt org_jetbrains_skia_Codec__1nGetRepetitionCount
(KNativePointer ptr) {
......
......@@ -95,6 +95,11 @@ namespace skija {
}
}
namespace AnimationFrameInfo {
void copyToInterop(const SkCodec::FrameInfo& info, KInteropPointer dst);
void copyToInterop(const std::vector<SkCodec::FrameInfo>& infos, KInteropPointer dst);
}
namespace svg {
namespace SVGLength {
void copyToInterop(const SkSVGLength& length, KInteropPointer dst);
......
......@@ -286,6 +286,37 @@ namespace skija {
}
}
namespace AnimationFrameInfo {
static void copyToInteropAtIndex(const SkCodec::FrameInfo& info, KInt* repr, size_t index) {
repr += (index * 11);
repr[0] = info.fRequiredFrame;
repr[1] = info.fDuration;
repr[2] = static_cast<KInt>(info.fFullyReceived);
repr[3] = static_cast<KInt>(info.fAlphaType);
repr[4] = static_cast<KInt>(info.fHasAlphaWithinBounds);
repr[5] = static_cast<KInt>(info.fDisposalMethod);
repr[6] = static_cast<KInt>(info.fBlend);
repr[7] = info.fFrameRect.left();
repr[8] = info.fFrameRect.top();
repr[9] = info.fFrameRect.right();
repr[10] = info.fFrameRect.bottom();
}
void copyToInterop(const SkCodec::FrameInfo& info, KInteropPointer dst) {
KInt* repr = reinterpret_cast<KInt*>(dst);
copyToInteropAtIndex(info, repr, 0);
}
void copyToInterop(const std::vector<SkCodec::FrameInfo>& infos, KInteropPointer dst) {
KInt* repr = reinterpret_cast<KInt*>(dst);
size_t i = 0;
for (const auto& info : infos) {
copyToInteropAtIndex(info, repr, i++);
}
}
}
namespace svg {
namespace SVGLength {
void copyToInterop(const SkSVGLength& length, KInteropPointer dst) {
......
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