From ceb5df9d39edea92d5864eb1022aebae5024a6e6 Mon Sep 17 00:00:00 2001 From: Julian Winkler Date: Fri, 1 Sep 2023 13:09:04 +0200 Subject: [PATCH] implement BitmapFactory.decodeStream() using gdk_pixbuf_new_from_stream The java InputStream is wrapped into a custom GInputStream implementation --- meson.build | 1 + src/api-impl-jni/android_graphics_Bitmap.c | 12 ++++ .../android_graphics_Bitmap.h | 12 +++- .../android_graphics_BitmapFactory.h | 4 +- .../graphics/android_graphics_BitmapFactory.c | 60 +++++++++++++++++++ src/api-impl/android/graphics/Bitmap.java | 10 +++- .../android/graphics/BitmapFactory.java | 4 +- 7 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 src/api-impl-jni/graphics/android_graphics_BitmapFactory.c diff --git a/meson.build b/meson.build index 4ed5c372..034ab871 100644 --- a/meson.build +++ b/meson.build @@ -67,6 +67,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/database/android_database_SQLiteCommon.c', 'src/api-impl-jni/database/android_database_SQLiteConnection.c', 'src/api-impl-jni/drawables/ninepatch.c', + 'src/api-impl-jni/graphics/android_graphics_BitmapFactory.c', 'src/api-impl-jni/android_content_res_AssetManager.c', 'src/api-impl-jni/audio/android_media_AudioTrack.c', 'src/api-impl-jni/audio/android_media_SoundPool.c', diff --git a/src/api-impl-jni/android_graphics_Bitmap.c b/src/api-impl-jni/android_graphics_Bitmap.c index d540abe4..5a90773e 100644 --- a/src/api-impl-jni/android_graphics_Bitmap.c +++ b/src/api-impl-jni/android_graphics_Bitmap.c @@ -52,3 +52,15 @@ JNIEXPORT jboolean JNICALL Java_android_graphics_Bitmap_nativeRecycle(JNIEnv *en g_object_unref(pixbuf); return true; } + +JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1copy(JNIEnv *env, jclass, jlong src_ptr) { + GdkPixbuf *src = _PTR(src_ptr); + GdkPixbuf *copy = gdk_pixbuf_copy(src); + return _INTPTR(copy); +} + +JNIEXPORT jint JNICALL Java_android_graphics_Bitmap_nativeRowBytes(JNIEnv *env, jclass, jlong pixbuf_ptr) { + GdkPixbuf *pixbuf = _PTR(pixbuf_ptr); + + return gdk_pixbuf_get_rowstride(pixbuf); +} diff --git a/src/api-impl-jni/generated_headers/android_graphics_Bitmap.h b/src/api-impl-jni/generated_headers/android_graphics_Bitmap.h index f64e690f..75bed938 100644 --- a/src/api-impl-jni/generated_headers/android_graphics_Bitmap.h +++ b/src/api-impl-jni/generated_headers/android_graphics_Bitmap.h @@ -19,6 +19,14 @@ extern "C" { JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1bitmap_1from_1path (JNIEnv *, jobject, jobject); +/* + * Class: android_graphics_Bitmap + * Method: native_copy + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1copy + (JNIEnv *, jclass, jlong); + /* * Class: android_graphics_Bitmap * Method: getWidth @@ -86,10 +94,10 @@ JNIEXPORT void JNICALL Java_android_graphics_Bitmap_nativeErase /* * Class: android_graphics_Bitmap * Method: nativeRowBytes - * Signature: (I)I + * Signature: (J)I */ JNIEXPORT jint JNICALL Java_android_graphics_Bitmap_nativeRowBytes - (JNIEnv *, jclass, jint); + (JNIEnv *, jclass, jlong); /* * Class: android_graphics_Bitmap diff --git a/src/api-impl-jni/generated_headers/android_graphics_BitmapFactory.h b/src/api-impl-jni/generated_headers/android_graphics_BitmapFactory.h index c7d95182..7d593e30 100644 --- a/src/api-impl-jni/generated_headers/android_graphics_BitmapFactory.h +++ b/src/api-impl-jni/generated_headers/android_graphics_BitmapFactory.h @@ -12,9 +12,9 @@ extern "C" { /* * Class: android_graphics_BitmapFactory * Method: nativeDecodeStream - * Signature: (Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory/Options;)Landroid/graphics/Bitmap; + * Signature: (Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory/Options;)J */ -JNIEXPORT jobject JNICALL Java_android_graphics_BitmapFactory_nativeDecodeStream +JNIEXPORT jlong JNICALL Java_android_graphics_BitmapFactory_nativeDecodeStream (JNIEnv *, jclass, jobject, jbyteArray, jobject, jobject); /* diff --git a/src/api-impl-jni/graphics/android_graphics_BitmapFactory.c b/src/api-impl-jni/graphics/android_graphics_BitmapFactory.c new file mode 100644 index 00000000..17c10313 --- /dev/null +++ b/src/api-impl-jni/graphics/android_graphics_BitmapFactory.c @@ -0,0 +1,60 @@ +#include +#include + +#include "../defines.h" +#include "../util.h" + +#include "../generated_headers/android_graphics_BitmapFactory.h" + +struct _JavaInputStream +{ + GInputStream parent_instance; + + jobject is; + jbyteArray storage; + int storage_size; +}; + +G_DECLARE_FINAL_TYPE(JavaInputStream, java_input_stream, ATL, JAVA_INPUT_STREAM, GInputStream); + +static gssize java_input_stream_read(GInputStream *gstream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { + JavaInputStream *stream = ATL_JAVA_INPUT_STREAM(gstream); + JNIEnv *env = get_jni_env(); + + count = MIN(count, stream->storage_size); + + count = (*env)->CallIntMethod(env, stream->is, _METHOD(_CLASS(stream->is), "read", "([BII)I"), stream->storage, 0, count); + if (count == -1) { // end of stream + return 0; + } + jbyte *storage_buf = (*env)->GetByteArrayElements(env, stream->storage, NULL); + memcpy(buffer, storage_buf, count); + (*env)->ReleaseByteArrayElements(env, stream->storage, storage_buf, 0); + + return count; +} + +static void java_input_stream_class_init(JavaInputStreamClass *klass) { + klass->parent_class.read_fn = java_input_stream_read; +} + +static void java_input_stream_init(JavaInputStream *self) { +} + +G_DEFINE_TYPE(JavaInputStream, java_input_stream, G_TYPE_INPUT_STREAM) + +static GInputStream *java_input_stream_new(JNIEnv *env, jobject is, jbyteArray storage) { + JavaInputStream *stream = g_object_new(java_input_stream_get_type(), NULL); + stream->is = is; + stream->storage = storage; + stream->storage_size = (*env)->GetArrayLength(env, storage); + return &stream->parent_instance; +} + +JNIEXPORT jlong JNICALL Java_android_graphics_BitmapFactory_nativeDecodeStream(JNIEnv *env, jclass, jobject is, jbyteArray storage, jobject outPadding, jobject opts) { + GInputStream *stream = java_input_stream_new(env, is, storage); + + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream(stream, NULL, NULL); + g_object_unref(stream); + return _INTPTR(pixbuf); +} diff --git a/src/api-impl/android/graphics/Bitmap.java b/src/api-impl/android/graphics/Bitmap.java index 2c23f374..e062db27 100644 --- a/src/api-impl/android/graphics/Bitmap.java +++ b/src/api-impl/android/graphics/Bitmap.java @@ -136,7 +136,13 @@ public final class Bitmap { mLayoutBounds = null; } + Bitmap(long pixbuf) { + this(); + this.pixbuf = pixbuf; + } + private native long native_bitmap_from_path(CharSequence path); + static native long native_copy(long src); /** * Private constructor that must received an already allocated native bitmap @@ -784,7 +790,7 @@ public final class Bitmap { return bitmap; */ - return new Bitmap(); + return new Bitmap(native_copy(source.pixbuf)); } /** @@ -1606,7 +1612,7 @@ public final class Bitmap { int quality, OutputStream stream, byte[] tempStorage); private static native void nativeErase(int nativeBitmap, int color); - private static native int nativeRowBytes(int nativeBitmap); + private static native int nativeRowBytes(long nativeBitmap); private static native int nativeConfig(int nativeBitmap); private static native int nativeGetPixel(int nativeBitmap, int x, int y, diff --git a/src/api-impl/android/graphics/BitmapFactory.java b/src/api-impl/android/graphics/BitmapFactory.java index edbb24c9..8c31c86b 100644 --- a/src/api-impl/android/graphics/BitmapFactory.java +++ b/src/api-impl/android/graphics/BitmapFactory.java @@ -596,7 +596,7 @@ public class BitmapFactory { tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; - return nativeDecodeStream(is, tempStorage, outPadding, opts); + return new Bitmap(nativeDecodeStream(is, tempStorage, outPadding, opts)); } /** @@ -669,7 +669,7 @@ public class BitmapFactory { return decodeFileDescriptor(fd, null, null); } - private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, + private static native long nativeDecodeStream(InputStream is, byte[] storage, Rect padding, Options opts); private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd, Rect padding, Options opts);