You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
Bitmap: implement pixel buffer access
For GPU textures, the GdkTextureDownloader will take care of format conversions, so the application never sees the actual format. If the application calls AndroidBitmap_unlockPixels(), the texture is converted into a GdkMemoryTexture and can be accessed zero copy.
This commit is contained in:
@@ -71,6 +71,14 @@ JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1ref_1texture
|
|||||||
JNIEXPORT void JNICALL Java_android_graphics_Bitmap_native_1get_1pixels
|
JNIEXPORT void JNICALL Java_android_graphics_Bitmap_native_1get_1pixels
|
||||||
(JNIEnv *, jclass, jlong, jintArray, jint, jint, jint, jint, jint, jint);
|
(JNIEnv *, jclass, jlong, jintArray, jint, jint, jint, jint, jint, jint);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: android_graphics_Bitmap
|
||||||
|
* Method: native_copy_to_buffer
|
||||||
|
* Signature: (JLjava/nio/Buffer;II)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_android_graphics_Bitmap_native_1copy_1to_1buffer
|
||||||
|
(JNIEnv *, jclass, jlong, jobject, jint, jint);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
#include "../defines.h"
|
#include "../defines.h"
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
#include "../generated_headers/android_graphics_Bitmap.h"
|
#include "../generated_headers/android_graphics_Bitmap.h"
|
||||||
|
|
||||||
@@ -90,3 +91,16 @@ JNIEXPORT void JNICALL Java_android_graphics_Bitmap_native_1get_1pixels(JNIEnv *
|
|||||||
gdk_texture_download(texture, (guchar *)(array + offset), stride*4);
|
gdk_texture_download(texture, (guchar *)(array + offset), stride*4);
|
||||||
(*env)->ReleaseIntArrayElements(env, pixels, array, 0);
|
(*env)->ReleaseIntArrayElements(env, pixels, array, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_android_graphics_Bitmap_native_1copy_1to_1buffer(JNIEnv *env, jclass class, jlong texture_ptr, jobject buffer, jint memory_format, jint stride)
|
||||||
|
{
|
||||||
|
GdkTexture *texture = GDK_TEXTURE(_PTR(texture_ptr));
|
||||||
|
GdkTextureDownloader *downloader = gdk_texture_downloader_new(texture);
|
||||||
|
gdk_texture_downloader_set_format(downloader, memory_format);
|
||||||
|
jarray array_ref;
|
||||||
|
jbyte *array;
|
||||||
|
guchar *data = get_nio_buffer(env, buffer, &array_ref, &array);
|
||||||
|
gdk_texture_downloader_download_into(downloader, data, stride);
|
||||||
|
release_nio_buffer(env, array_ref, array);
|
||||||
|
gdk_texture_downloader_free(downloader);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package android.graphics;
|
package android.graphics;
|
||||||
|
|
||||||
|
import java.nio.Buffer;
|
||||||
|
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -9,28 +11,42 @@ import android.util.DisplayMetrics;
|
|||||||
public final class Bitmap {
|
public final class Bitmap {
|
||||||
|
|
||||||
public enum Config {
|
public enum Config {
|
||||||
RGB_565,
|
RGB_565(2, -1, /*ANDROID_BITMAP_FORMAT_RGB_565*/4),
|
||||||
ARGB_8888,
|
ARGB_8888(4, /*GDK_MEMORY_R8G8B8A8*/5, /**ANDROID_BITMAP_FORMAT_RGBA_8888*/1),
|
||||||
ARGB_4444,
|
ARGB_4444(2, -1, /*ANDROID_BITMAP_FORMAT_RGBA_4444*/7),
|
||||||
ALPHA_8,
|
ALPHA_8(1, /*GDK_MEMORY_A8*/ 24, /*ANDROID_BITMAP_FORMAT_A_8*/8);
|
||||||
|
|
||||||
|
private int bytes_per_pixel;
|
||||||
|
private int gdk_memory_format;
|
||||||
|
int android_memory_format; // used by native function AndroidBitmap_getInfo()
|
||||||
|
|
||||||
|
private Config(int bytes_per_pixel, int gdk_memory_format, int android_memory_format) {
|
||||||
|
this.bytes_per_pixel = bytes_per_pixel;
|
||||||
|
this.gdk_memory_format = gdk_memory_format;
|
||||||
|
this.android_memory_format = android_memory_format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
|
private int stride;
|
||||||
private long texture;
|
private long texture;
|
||||||
private long snapshot;
|
private long snapshot;
|
||||||
private Config config = Config.ARGB_8888;
|
private Config config = Config.ARGB_8888;
|
||||||
|
private boolean hasAlpha = true;
|
||||||
|
long bytes = 0; // used by native function AndroidBitmap_lockPixels()
|
||||||
|
|
||||||
Bitmap(long texture) {
|
Bitmap(long texture) {
|
||||||
|
this(native_get_width(texture), native_get_height(texture), Config.ARGB_8888);
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
this.width = native_get_width(texture);
|
|
||||||
this.height = native_get_height(texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bitmap(int width, int height, Config config) {
|
private Bitmap(int width, int height, Config config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
int stride = width * config.bytes_per_pixel;
|
||||||
|
this.stride = (stride + 3) & ~3; // 4-byte alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createBitmap(int width, int height, Config config) {
|
public static Bitmap createBitmap(int width, int height, Config config) {
|
||||||
@@ -42,7 +58,9 @@ public final class Bitmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createBitmap(DisplayMetrics metrics, int width, int height, Config config, boolean hasAlpha, ColorSpace colorSpace) {
|
public static Bitmap createBitmap(DisplayMetrics metrics, int width, int height, Config config, boolean hasAlpha, ColorSpace colorSpace) {
|
||||||
return new Bitmap(width, height, config);
|
Bitmap bitmap = new Bitmap(width, height, config);
|
||||||
|
bitmap.hasAlpha = hasAlpha;
|
||||||
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createBitmap(Bitmap src, int x, int y, int width, int height) {
|
public static Bitmap createBitmap(Bitmap src, int x, int y, int width, int height) {
|
||||||
@@ -109,8 +127,12 @@ public final class Bitmap {
|
|||||||
snapshot = 0;
|
snapshot = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRowBytes() {
|
||||||
|
return stride;
|
||||||
|
}
|
||||||
|
|
||||||
public int getAllocationByteCount() {
|
public int getAllocationByteCount() {
|
||||||
return width * height * 4;
|
return height * getRowBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepareToDraw() {
|
public void prepareToDraw() {
|
||||||
@@ -131,9 +153,15 @@ public final class Bitmap {
|
|||||||
return texture == 0 && snapshot == 0;
|
return texture == 0 && snapshot == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHasAlpha(boolean hasAlpha) {}
|
public void setHasAlpha(boolean hasAlpha) {
|
||||||
|
this.hasAlpha = hasAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
public Bitmap copy(Bitmap.Config config, boolean hasAlpha) {
|
public boolean hasAlpha() {
|
||||||
|
return hasAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap copy(Bitmap.Config config, boolean isMutable) {
|
||||||
Bitmap bitmap = new Bitmap(width, height, config);
|
Bitmap bitmap = new Bitmap(width, height, config);
|
||||||
bitmap.texture = native_ref_texture(getTexture());
|
bitmap.texture = native_ref_texture(getTexture());
|
||||||
return bitmap;
|
return bitmap;
|
||||||
@@ -143,6 +171,15 @@ public final class Bitmap {
|
|||||||
native_get_pixels(getTexture(), pixels, offset, stride, x, y, width, height);
|
native_get_pixels(getTexture(), pixels, offset, stride, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyPixelsToBuffer(Buffer buffer) {
|
||||||
|
if (config.gdk_memory_format == -1) {
|
||||||
|
System.out.println("copyPixelsToBuffer: format " + config.name() + " not implemented");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
native_copy_to_buffer(getTexture(), buffer, config.gdk_memory_format, getRowBytes());
|
||||||
|
buffer.position(buffer.position() + getAllocationByteCount());
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
@@ -161,4 +198,5 @@ public final class Bitmap {
|
|||||||
private static native void native_recycle(long texture, long snapshot);
|
private static native void native_recycle(long texture, long snapshot);
|
||||||
private static native long native_ref_texture(long texture);
|
private static native long native_ref_texture(long texture);
|
||||||
private static native void native_get_pixels(long texture, int[] pixels, int offset, int stride, int x, int y, int width, int height);
|
private static native void native_get_pixels(long texture, int[] pixels, int offset, int stride, int x, int y, int width, int height);
|
||||||
|
private static native void native_copy_to_buffer(long texture, Buffer buffer, int memory_format, int stride);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gdk/gdk.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
// FIXME: put the header in a common place
|
// FIXME: put the header in a common place
|
||||||
#include "../api-impl-jni/defines.h"
|
#include "../api-impl-jni/defines.h"
|
||||||
@@ -14,19 +15,67 @@ struct AndroidBitmapInfo {
|
|||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
int AndroidBitmap_getInfo(JNIEnv* env, jobject bitmap, struct AndroidBitmapInfo *info) {
|
int AndroidBitmap_getInfo(JNIEnv* env, jobject bitmap, struct AndroidBitmapInfo *info)
|
||||||
GdkPixbuf *pixbuf = _PTR(_GET_LONG_FIELD(bitmap, "pixbuf"));
|
{
|
||||||
info->width = gdk_pixbuf_get_width(pixbuf);
|
info->width = _GET_INT_FIELD(bitmap, "width");
|
||||||
info->height = gdk_pixbuf_get_height(pixbuf);
|
info->height = _GET_INT_FIELD(bitmap, "height");
|
||||||
info->stride = gdk_pixbuf_get_rowstride(pixbuf);
|
info->stride = _GET_INT_FIELD(bitmap, "stride");
|
||||||
info->format = 1; // ANDROID_BITMAP_FORMAT_RGBA_8888
|
info->format = _GET_INT_FIELD(_GET_OBJ_FIELD(bitmap, "config", "Landroid/graphics/Bitmap$Config;"), "android_memory_format");
|
||||||
return ANDROID_BITMAP_RESULT_SUCCESS;
|
return ANDROID_BITMAP_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
int AndroidBitmap_lockPixels(JNIEnv* env, jobject bitmap, void** pixels) {
|
|
||||||
GdkPixbuf *pixbuf = _PTR(_GET_LONG_FIELD(bitmap, "pixbuf"));
|
int AndroidBitmap_lockPixels(JNIEnv* env, jobject bitmap, void** pixels)
|
||||||
*pixels = gdk_pixbuf_get_pixels(pixbuf);
|
{
|
||||||
|
printf("AndroidBitmap_lockPixels\n");
|
||||||
|
GdkTexture *texture = _PTR((*env)->CallLongMethod(env, bitmap, _METHOD(_CLASS(bitmap), "getTexture", "()J")));
|
||||||
|
int stride = _GET_INT_FIELD(bitmap, "stride");
|
||||||
|
int format = _GET_INT_FIELD(_GET_OBJ_FIELD(bitmap, "config", "Landroid/graphics/Bitmap$Config;"), "gdk_memory_format");
|
||||||
|
if (format == -1) {
|
||||||
|
printf("AndroidBitmap_lockPixels: format not implemented\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
GdkTextureDownloader *downloader = gdk_texture_downloader_new(texture);
|
||||||
|
gdk_texture_downloader_set_format(downloader, format);
|
||||||
|
GBytes *bytes = NULL;
|
||||||
|
if (GDK_IS_MEMORY_TEXTURE(texture)) { // try to get the bytes non-copying
|
||||||
|
gsize texture_stride;
|
||||||
|
bytes = gdk_texture_downloader_download_bytes(downloader, &texture_stride);
|
||||||
|
if (texture_stride != stride) { // texture was not created by us, fall back to copy
|
||||||
|
g_bytes_unref(bytes);
|
||||||
|
bytes = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bytes == NULL) {
|
||||||
|
guchar *data = g_malloc(stride * gdk_texture_get_height(texture));
|
||||||
|
gdk_texture_downloader_download_into(downloader, data, stride);
|
||||||
|
bytes = g_bytes_new_take(data, stride * gdk_texture_get_height(texture));
|
||||||
|
}
|
||||||
|
gdk_texture_downloader_free(downloader);
|
||||||
|
_SET_LONG_FIELD(bitmap, "bytes", _INTPTR(bytes));
|
||||||
|
*pixels = (void *)g_bytes_get_data(bytes, NULL);
|
||||||
return ANDROID_BITMAP_RESULT_SUCCESS;
|
return ANDROID_BITMAP_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject bitmap) {
|
|
||||||
|
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject bitmap)
|
||||||
|
{
|
||||||
|
printf("AndroidBitmap_unlockPixels\n");
|
||||||
|
GBytes *bytes = _PTR(_GET_LONG_FIELD(bitmap, "bytes"));
|
||||||
|
if (!bytes) {
|
||||||
|
printf("AndroidBitmap_unlockPixels: no bytes! Was AndroidBitmap_lockPixels called?\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int width = _GET_INT_FIELD(bitmap, "width");
|
||||||
|
int height = _GET_INT_FIELD(bitmap, "height");
|
||||||
|
int stride = _GET_INT_FIELD(bitmap, "stride");
|
||||||
|
int format = _GET_INT_FIELD(_GET_OBJ_FIELD(bitmap, "config", "Landroid/graphics/Bitmap$Config;"), "gdk_memory_format");
|
||||||
|
if (format == -1) {
|
||||||
|
printf("AndroidBitmap_lockPixels: format not implemented\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
GdkTexture *texture = gdk_memory_texture_new(width, height, format, bytes, stride);
|
||||||
|
g_bytes_unref(bytes);
|
||||||
|
(*env)->CallVoidMethod(env, bitmap, _METHOD(_CLASS(bitmap), "recycle", "()V"));
|
||||||
|
_SET_LONG_FIELD(bitmap, "texture", _INTPTR(texture));
|
||||||
|
_SET_LONG_FIELD(bitmap, "bytes", 0);
|
||||||
return ANDROID_BITMAP_RESULT_SUCCESS;
|
return ANDROID_BITMAP_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user