diff options
author | Patrick Dubroy <dubroy@google.com> | 2010-12-01 11:23:13 -0800 |
---|---|---|
committer | Patrick Dubroy <dubroy@google.com> | 2010-12-14 16:50:50 -0800 |
commit | e4ac2d6b5723c95e648c489b187ddde449452c13 (patch) | |
tree | a783470220e1840761dc6696f3e5f4cc7870054b /core/jni | |
parent | 89f8d63a991f2dc4a961ad92ab5bb6b6c1ecd60f (diff) | |
download | frameworks_base-e4ac2d6b5723c95e648c489b187ddde449452c13.zip frameworks_base-e4ac2d6b5723c95e648c489b187ddde449452c13.tar.gz frameworks_base-e4ac2d6b5723c95e648c489b187ddde449452c13.tar.bz2 |
Allocate bitmap backing buffers in the Java heap.
Change-Id: I60f6ccff13357c1c518e9d56b02fe0171637edd1
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/android/graphics/Bitmap.cpp | 28 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 4 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 11 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 224 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 80 | ||||
-rw-r--r-- | core/jni/android_emoji_EmojiFactory.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 38 |
7 files changed, 269 insertions, 120 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 29c6ba2..1d961ec 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1,4 +1,5 @@ #include "SkBitmap.h"
+#include "SkPixelRef.h"
#include "SkImageEncoder.h"
#include "SkColorPriv.h"
#include "GraphicsJNI.h"
@@ -210,11 +211,6 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) { static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
int offset, int stride, int width, int height,
SkBitmap::Config config, jboolean isMutable) {
- if (width <= 0 || height <= 0) {
- doThrowIAE(env, "width and height must be > 0");
- return NULL;
- }
-
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
@@ -226,7 +222,9 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, SkBitmap bitmap;
bitmap.setConfig(config, width, height);
- if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {
+
+ jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+ if (NULL == buff) {
return NULL;
}
@@ -235,21 +233,19 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap);
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,
- NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);
}
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
SkBitmap::Config dstConfig, jboolean isMutable) {
SkBitmap result;
- JavaPixelAllocator allocator(env, true);
+ JavaPixelAllocator allocator(env);
if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL;
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,
- NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);
}
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
@@ -380,7 +376,8 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { }
}
- if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {
+ jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+ if (NULL == buffer) {
ctable->safeUnref();
delete bitmap;
return NULL;
@@ -393,7 +390,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { memcpy(bitmap->getPixels(), p->readInplace(size), size);
bitmap->unlockPixels();
- return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL, density);
+ return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
@@ -447,8 +444,9 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jintArray offsetXY) {
SkIPoint offset;
SkBitmap* dst = new SkBitmap;
+ JavaPixelAllocator allocator(env);
- src->extractAlpha(dst, paint, &offset);
+ src->extractAlpha(dst, paint, &allocator, &offset);
if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
int* array = env->GetIntArrayElements(offsetXY, NULL);
array[0] = offset.fX;
@@ -456,7 +454,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0);
}
- return GraphicsJNI::createBitmap(env, dst, true, NULL);
+ return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 164b357..73c9a50 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -291,7 +291,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } - // detach bitmap from its autotdeleter, since we want to own it now + // detach bitmap from its autodeleter, since we want to own it now adb.detach(); if (padding) { @@ -322,7 +322,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, return javaBitmap; } // now create the java bitmap - return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); + return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), false, ninePatchChunk); } static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 91a8202..1e00b71 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -79,7 +79,7 @@ static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } - JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); + JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env); decoder->setAllocator(javaAllocator); JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); decoder->setReporter(javaMemoryReporter); @@ -241,15 +241,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b getMimeTypeString(env, decoder->getFormat())); } - // detach bitmap from its autotdeleter, since we want to own it now + // detach bitmap from its autodeleter, since we want to own it now adb.detach(); - SkPixelRef* pr; - pr = bitmap->pixelRef(); + SkPixelRef* pr = bitmap->pixelRef(); // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); + // now create the java bitmap - return GraphicsJNI::createBitmap(env, bitmap, false, NULL); + jbyteArray buff = ((AndroidPixelRef*) pr)->getStorageObj(); + return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1); } static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 9467be8..fc358a1 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -2,6 +2,9 @@ #include "jni.h" #include "GraphicsJNI.h" + +#include "SkCanvas.h" +#include "SkDevice.h" #include "SkPicture.h" #include "SkRegion.h" #include <android_runtime/AndroidRuntime.h> @@ -163,7 +166,6 @@ static jfieldID gPointF_yFieldID; static jclass gBitmap_class; static jfieldID gBitmap_nativeInstanceID; static jmethodID gBitmap_constructorMethodID; -static jmethodID gBitmap_allocBufferMethodID; static jclass gBitmapConfig_class; static jfieldID gBitmapConfig_nativeInstanceID; @@ -360,16 +362,16 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// -jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, - jbyteArray ninepatch, int density) +jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, + bool isMutable, jbyteArray ninepatch, int density) { - SkASSERT(bitmap != NULL); - SkASSERT(NULL != bitmap->pixelRef()); + SkASSERT(bitmap); + SkASSERT(bitmap->pixelRef()); jobject obj = env->AllocObject(gBitmap_class); if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, - (jint)bitmap, isMutable, ninepatch, density); + (jint)bitmap, buffer, isMutable, ninepatch, density); if (hasException(env)) { obj = NULL; } @@ -377,6 +379,13 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, return obj; } +jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, + jbyteArray ninepatch, int density) +{ + return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density); +} + + jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) { SkASSERT(bitmap != NULL); @@ -408,8 +417,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) return obj; } -#include "SkPixelRef.h" - static JNIEnv* vm2env(JavaVM* vm) { JNIEnv* env = NULL; @@ -427,87 +434,125 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// -#include "SkMallocPixelRef.h" - -/* Extend SkMallocPixelRef to inform the VM when we free up the storage -*/ -class AndroidPixelRef : public SkMallocPixelRef { -public: - /** Allocate the specified buffer for pixels. The memory is freed when the - last owner of this pixelref is gone. Our caller has already informed - the VM of our allocation. - */ - AndroidPixelRef(JNIEnv* env, void* storage, size_t size, - SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { - SkASSERT(storage); - SkASSERT(env); - - if (env->GetJavaVM(&fVM) != JNI_OK) { - SkDebugf("------ [%p] env->GetJavaVM failed\n", env); - sk_throw(); - } +AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, + SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { + SkASSERT(storage); + SkASSERT(env); + + if (env->GetJavaVM(&fVM) != JNI_OK) { + SkDebugf("------ [%p] env->GetJavaVM failed\n", env); + sk_throw(); } + fStorageObj = storageObj; + fHasGlobalRef = false; + fGlobalRefCnt = 0; + + // If storageObj is NULL, the memory was NOT allocated on the Java heap + fOnJavaHeap = (storageObj != NULL); + +} - virtual ~AndroidPixelRef() { +AndroidPixelRef::~AndroidPixelRef() { + if (fOnJavaHeap) { JNIEnv* env = vm2env(fVM); -// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); - jlong jsize = this->getSize(); // the VM wants longs for the size - env->CallVoidMethod(gVMRuntime_singleton, - gVMRuntime_trackExternalFreeMethodID, - jsize); - if (GraphicsJNI::hasException(env)) { - env->ExceptionClear(); + + if (fStorageObj && fHasGlobalRef) { + env->DeleteGlobalRef(fStorageObj); + } + fStorageObj = NULL; + + // Set this to NULL to prevent the SkMallocPixelRef destructor + // from freeing the memory. + fStorage = NULL; + } +} + +void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) { + if (!fHasGlobalRef) { + fStorageObj = arr; + } +} + +void AndroidPixelRef::globalRef() { + if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) { + JNIEnv *env = vm2env(fVM); + if (fStorageObj == NULL) { + SkDebugf("Cannot create a global ref, fStorage obj is NULL"); + sk_throw(); + } + if (fHasGlobalRef) { + // This should never happen + SkDebugf("Already holding a global ref"); + sk_throw(); + } + + fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj); + // TODO: Check for failure here + fHasGlobalRef = true; + } + ref(); +} + +void AndroidPixelRef::globalUnref() { + if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) { + JNIEnv *env = vm2env(fVM); + if (!fHasGlobalRef) { + SkDebugf("We don't have a global ref!"); + sk_throw(); } + env->DeleteGlobalRef(fStorageObj); + fStorageObj = NULL; + fHasGlobalRef = false; } + unref(); +} -private: - JavaVM* fVM; -}; +/////////////////////////////////////////////////////////////////////////////// -bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, - SkColorTable* ctable, bool reportSizeToVM) { +jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, + SkColorTable* ctable) { Sk64 size64 = bitmap->getSize64(); if (size64.isNeg() || !size64.is32()) { doThrow(env, "java/lang/IllegalArgumentException", "bitmap size exceeds 32bits"); - return false; + return NULL; } size_t size = size64.get32(); - jlong jsize = size; // the VM wants longs for the size - if (reportSizeToVM) { - // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); - bool r = env->CallBooleanMethod(gVMRuntime_singleton, - gVMRuntime_trackExternalAllocationMethodID, - jsize); - if (GraphicsJNI::hasException(env)) { - return false; - } - if (!r) { - LOGE("VM won't let us allocate %zd bytes\n", size); - doThrowOOME(env, "bitmap size exceeds VM budget"); - return false; + jbyteArray arrayObj = env->NewByteArray(size); + if (arrayObj) { + jbyte *addr = env->GetByteArrayElements(arrayObj, NULL); + env->ReleaseByteArrayElements(arrayObj, addr, 0); + if (addr) { + SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable); + bitmap->setPixelRef(pr)->unref(); + // since we're already allocated, we lockPixels right away + // HeapAllocator behaves this way too + bitmap->lockPixels(); } } + + return arrayObj; +} + +bool GraphicsJNI::mallocPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { + Sk64 size64 = bitmap->getSize64(); + if (size64.isNeg() || !size64.is32()) { + doThrow(env, "java/lang/IllegalArgumentException", + "bitmap size exceeds 32bits"); + return false; + } + + size_t size = size64.get32(); + // call the version of malloc that returns null on failure void* addr = sk_malloc_flags(size, 0); + if (NULL == addr) { - if (reportSizeToVM) { - // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); - // we didn't actually allocate it, so inform the VM - env->CallVoidMethod(gVMRuntime_singleton, - gVMRuntime_trackExternalFreeMethodID, - jsize); - if (!GraphicsJNI::hasException(env)) { - doThrowOOME(env, "bitmap size too large for malloc"); - } - } return false; } - - SkPixelRef* pr = reportSizeToVM ? - new AndroidPixelRef(env, addr, size, ctable) : - new SkMallocPixelRef(addr, size, ctable); + + SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable); bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too @@ -517,8 +562,9 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// -JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) - : fReportSizeToVM(reportSizeToVM) { +JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap) + : fAllocateInJavaHeap(allocateInJavaHeap), + fStorageObj(NULL) { if (env->GetJavaVM(&fVM) != JNI_OK) { SkDebugf("------ [%p] env->GetJavaVM failed\n", env); sk_throw(); @@ -527,7 +573,19 @@ JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { JNIEnv* env = vm2env(fVM); - return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM); + + // If allocating in the Java heap, only allow a single object to be + // allocated for the lifetime of this object. + if (fStorageObj != NULL) { + SkDebugf("ERROR: One-shot allocator has already allocated\n"); + sk_throw(); + } + + if (fAllocateInJavaHeap) { + fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); + return fStorageObj != NULL; + } + return GraphicsJNI::mallocPixelRef(env, bitmap, ctable); } //////////////////////////////////////////////////////////////////////////////// @@ -568,6 +626,25 @@ bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) { //////////////////////////////////////////////////////////////////////////////// +JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) { + fEnv = env; + fNativeBitmap = nativeBitmap; + fBuffer = buffer; + + // If the buffer is NULL, the backing memory wasn't allocated on the Java heap + if (fBuffer) { + ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer); + } +} + +JavaHeapBitmapRef::~JavaHeapBitmapRef() { + if (fBuffer) { + ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL); + } +} + +//////////////////////////////////////////////////////////////////////////////// + static jclass make_globalref(JNIEnv* env, const char classname[]) { jclass c = env->FindClass(classname); @@ -609,10 +686,9 @@ int register_android_graphics_Graphics(JNIEnv* env) gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); - gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); + gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", - "(IZ[BI)V"); - + "(I[BZ[BI)V"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index d0f9125..505d24e 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -1,9 +1,12 @@ #ifndef GraphicsJNI_DEFINED #define GraphicsJNI_DEFINED +#include "SkBitmap.h" +#include "SkDevice.h" +#include "SkPixelRef.h" +#include "SkMallocPixelRef.h" #include "SkPoint.h" #include "SkRect.h" -#include "SkBitmap.h" #include "../images/SkBitmapRegionDecoder.h" #include "../images/SkImageDecoder.h" #include <jni.h> @@ -47,23 +50,26 @@ public: static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig); /** Create a java Bitmap object given the native bitmap (required) and optional - storage array (may be null). If storage is specified, then it must already be - locked, and its native address set as the bitmap's pixels. If storage is null, - then the bitmap must be an owner of its natively allocated pixels (via allocPixels). - */ + storage array (may be null). + */ + static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, + bool isMutable, jbyteArray ninepatch, int density = -1); + static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, - jbyteArray ninePatch, int density = -1); + jbyteArray ninepatch, int density = -1); static jobject createRegion(JNIEnv* env, SkRegion* region); static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, + SkColorTable* ctable); + /** Set a pixelref for the bitmap (needs setConfig to already be called) Returns true on success. If it returns false, then it failed, and the appropriate exception will have been raised. */ - static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable, - bool reportSizeToVM); + static bool mallocPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable); /** Copy the colors in colors[] to the bitmap, convert to the correct format along the way. @@ -71,17 +77,71 @@ public: static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, int srcStride, int x, int y, int width, int height, const SkBitmap& dstBitmap); + + static jbyteArray getBitmapStorageObj(SkPixelRef *pixref); }; +class AndroidPixelRef : public SkMallocPixelRef { +public: + AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, + SkColorTable* ctable); + + virtual ~AndroidPixelRef(); + + jbyteArray getStorageObj() { return fStorageObj; } + + void setLocalJNIRef(jbyteArray arr); + + virtual void globalRef(); + virtual void globalUnref(); + +private: + JavaVM* fVM; + bool fOnJavaHeap; // If true, the memory was allocated on the Java heap + + jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store + bool fHasGlobalRef; // If true, fStorageObj holds a JNI global ref + + mutable int32_t fGlobalRefCnt; +}; + +/** A helper class for accessing Java-heap-allocated bitmaps. + * This should be used when calling into a JNI method that retains a + * reference to the bitmap longer than the lifetime of the Java Bitmap. + * + * After creating an instance of this class, a call to + * AndroidPixelRef::globalRef() will allocate a JNI global reference + * to the backing buffer object. + */ +class JavaHeapBitmapRef { +public: + + JavaHeapBitmapRef(JNIEnv *env, SkBitmap* nativeBitmap, jbyteArray buffer); + ~JavaHeapBitmapRef(); + +private: + JNIEnv* fEnv; + SkBitmap* fNativeBitmap; + jbyteArray fBuffer; +}; + +/** Allocator which allocates the backing buffer in the Java heap. + * Instances can only be used to perform a single allocation, which helps + * ensure that the allocated buffer is properly accounted for with a + * reference in the heap (or a JNI global reference). + */ class JavaPixelAllocator : public SkBitmap::Allocator { public: - JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM); + JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap=true); // overrides virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); + jbyteArray getStorageObj() { return fStorageObj; }; + private: JavaVM* fVM; - bool fReportSizeToVM; + bool fAllocateInJavaHeap; + jbyteArray fStorageObj; }; class JavaMemoryUsageReporter : public SkVMMemoryReporter { diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp index f653b36..1ebb36c 100644 --- a/core/jni/android_emoji_EmojiFactory.cpp +++ b/core/jni/android_emoji_EmojiFactory.cpp @@ -185,7 +185,7 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua( jobject obj = env->AllocObject(gBitmap_class); if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, - reinterpret_cast<jint>(bitmap), false, NULL, -1); + reinterpret_cast<jint>(bitmap), NULL, false, NULL, -1); if (env->ExceptionCheck() != 0) { LOGE("*** Uncaught exception returned from Java call!\n"); env->ExceptionDescribe(); @@ -298,7 +298,7 @@ static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, int register_android_emoji_EmojiFactory(JNIEnv* env) { gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", - "(IZ[BI)V"); + "(I[BZ[BI)V"); gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory"); gEmojiFactory_constructorMethodID = env->GetMethodID( gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V"); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index cb261d4..c3d3572 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "OpenGLRenderer" #include "jni.h" +#include "GraphicsJNI.h" #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/ResourceTypes.h> @@ -66,8 +67,6 @@ using namespace uirenderer; #endif // ---------------------------------------------------------------------------- -// Java APIs -// ---------------------------------------------------------------------------- static struct { jclass clazz; @@ -238,32 +237,46 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject canvas, // ---------------------------------------------------------------------------- static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas, - OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top, SkPaint* paint) { + OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, float left, + float top, SkPaint* paint) { + // This object allows the renderer to allocate a global JNI ref to the buffer object. + JavaHeapBitmapRef bitmapRef(env, bitmap, buffer); + renderer->drawBitmap(bitmap, left, top, paint); } static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas, - OpenGLRenderer* renderer, SkBitmap* bitmap, + OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) { + // This object allows the renderer to allocate a global JNI ref to the buffer object. + JavaHeapBitmapRef bitmapRef(env, bitmap, buffer); + renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight, dstBottom, paint); } static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas, - OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { + OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, SkMatrix* matrix, + SkPaint* paint) { + // This object allows the renderer to allocate a global JNI ref to the buffer object. + JavaHeapBitmapRef bitmapRef(env, bitmap, buffer); + renderer->drawBitmap(bitmap, matrix, paint); } static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas, - OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks, + OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, jbyteArray chunks, float left, float top, float right, float bottom, SkPaint* paint) { + // This object allows the renderer to allocate a global JNI ref to the buffer object. + JavaHeapBitmapRef bitmapRef(env, bitmap, buffer); + jbyte* storage = env->GetByteArrayElements(chunks, NULL); Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage); Res_png_9patch::deserialize(patch); - renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0], &patch->colors[0], - patch->numXDivs, patch->numYDivs, patch->numColors, + renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0], + &patch->colors[0], patch->numXDivs, patch->numYDivs, patch->numColors, left, top, right, bottom, paint); env->ReleaseByteArrayElements(chunks, storage, 0); @@ -477,10 +490,11 @@ static JNINativeMethod gMethods[] = { { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix }, { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix }, - { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap }, - { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect }, - { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix }, - { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, + { "nDrawBitmap", "(II[BFFI)V", (void*) android_view_GLES20Canvas_drawBitmap }, + { "nDrawBitmap", "(II[BFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect }, + { "nDrawBitmap", "(II[BII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix }, + { "nDrawPatch", "(II[B[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch }, + { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects }, |