diff options
author | Patrick Dubroy <dubroy@google.com> | 2010-12-14 16:56:07 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-12-14 16:56:07 -0800 |
commit | 982b71bf4713d57b0fdb9acaaf9f9465a817aacb (patch) | |
tree | 211deba434e54321f1427f3e3d1d2057213aae57 | |
parent | 52e08834094aef141c1352c601415132785f533d (diff) | |
parent | e4ac2d6b5723c95e648c489b187ddde449452c13 (diff) | |
download | frameworks_base-982b71bf4713d57b0fdb9acaaf9f9465a817aacb.zip frameworks_base-982b71bf4713d57b0fdb9acaaf9f9465a817aacb.tar.gz frameworks_base-982b71bf4713d57b0fdb9acaaf9f9465a817aacb.tar.bz2 |
Merge "Allocate bitmap backing buffers in the Java heap."
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 28 | ||||
-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 | ||||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 52 | ||||
-rw-r--r-- | graphics/java/android/graphics/Canvas.java | 33 | ||||
-rw-r--r-- | libs/hwui/ResourceCache.cpp | 8 |
11 files changed, 339 insertions, 171 deletions
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 5d9bd1e..773d734 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -459,35 +459,37 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing patches boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top, - dst.right, dst.bottom, nativePaint); + nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); } - private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top, - float right, float bottom, int paint); + private native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks, + float left, float top, float right, float bottom, int paint); @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint); + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); } - private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint); + private native void nDrawBitmap( + int renderer, int bitmap, byte[] buffer, float left, float top, int paint); @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint); + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, + matrix.native_instance, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); } - private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint); + private native void nDrawBitmap(int renderer, int bitmap, byte[] buff, int matrix, int paint); @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { @@ -507,7 +509,7 @@ class GLES20Canvas extends HardwareCanvas { bottom = src.bottom; } - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom, + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); } @@ -517,12 +519,12 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint); + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right, + src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); } - private native void nDrawBitmap(int renderer, int bitmap, + private native void nDrawBitmap(int renderer, int bitmap, byte[] buffer, float srcLeft, float srcTop, float srcRight, float srcBottom, float left, float top, float right, float bottom, int paint); @@ -534,7 +536,7 @@ class GLES20Canvas extends HardwareCanvas { final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, b.mNativeBitmap, x, y, nativePaint); + nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint); b.recycle(); if (hasColorFilter) nResetModifiers(mRenderer); } 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 }, diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index d283dea..a8efd00 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -19,18 +19,15 @@ package android.graphics; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; -import android.util.Finalizers; import java.io.OutputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; public final class Bitmap implements Parcelable { + /** * Indicates that the bitmap was created for an unknown pixel density. * @@ -47,6 +44,17 @@ public final class Bitmap implements Parcelable { */ public final int mNativeBitmap; + /** + * Backing buffer for the Bitmap. + * Made public for quick access from drawing methods -- do NOT modify + * from outside this class. + * + * @hide + */ + public byte[] mBuffer; + + private final BitmapFinalizer mFinalizer; + private final boolean mIsMutable; private byte[] mNinePatchChunk; // may be null private int mWidth = -1; @@ -85,25 +93,22 @@ public final class Bitmap implements Parcelable { This can be called from JNI code. */ - private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) { + private Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk, + int density) { if (nativeBitmap == 0) { throw new RuntimeException("internal error: native bitmap is 0"); } + mBuffer = buffer; // we delete this in our finalizer mNativeBitmap = nativeBitmap; + mFinalizer = new BitmapFinalizer(nativeBitmap); + mIsMutable = isMutable; mNinePatchChunk = ninePatchChunk; if (density >= 0) { mDensity = density; } - - // If the finalizers queue is null, we are running in zygote and the - // bitmap will never be reclaimed, so we don't need to run our native - // destructor - if (Finalizers.getQueue() != null) { - new BitmapFinalizer(this); - } } /** @@ -169,6 +174,7 @@ public final class Bitmap implements Parcelable { */ public void recycle() { if (!mRecycled) { + mBuffer = null; nativeRecycle(mNativeBitmap); mNinePatchChunk = null; mRecycled = true; @@ -520,6 +526,9 @@ public final class Bitmap implements Parcelable { * @throws IllegalArgumentException if the width or height are <= 0 */ private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) { + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("width and height must be > 0"); + } Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true); if (config == Config.ARGB_8888 && !hasAlpha) { bm.eraseColor(0xff000000); @@ -561,6 +570,9 @@ public final class Bitmap implements Parcelable { (lastScanline + width > length)) { throw new ArrayIndexOutOfBoundsException(); } + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("width and height must be > 0"); + } return nativeCreate(colors, offset, stride, width, height, config.nativeInt, false); } @@ -1059,22 +1071,16 @@ public final class Bitmap implements Parcelable { nativePrepareToDraw(mNativeBitmap); } - private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> { - private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet( - new HashSet<BitmapFinalizer>()); - - private int mNativeBitmap; + private static class BitmapFinalizer { + private final int mNativeBitmap; - BitmapFinalizer(Bitmap b) { - super(b, Finalizers.getQueue()); - mNativeBitmap = b.mNativeBitmap; - sFinalizers.add(this); + BitmapFinalizer(int nativeBitmap) { + mNativeBitmap = nativeBitmap; } @Override - public void reclaim() { + public void finalize() { nativeDestructor(mNativeBitmap); - sFinalizers.remove(this); } } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 07fe66c..66c6d81 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -74,6 +74,23 @@ public class Canvas { */ public static final int DIRECTION_RTL = 1; + private final CanvasFinalizer mFinalizer; + + private static class CanvasFinalizer { + private final int mNativeCanvas; + + public CanvasFinalizer(int nativeCanvas) { + mNativeCanvas = nativeCanvas; + } + + @Override + protected void finalize() throws Throwable { + if (mNativeCanvas != 0) { + finalizer(mNativeCanvas); + } + } + } + /** * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to * draw into. The initial target density is {@link Bitmap#DENSITY_NONE}; @@ -83,6 +100,7 @@ public class Canvas { public Canvas() { // 0 means no native bitmap mNativeCanvas = initRaster(0); + mFinalizer = new CanvasFinalizer(0); } /** @@ -101,6 +119,7 @@ public class Canvas { } throwIfRecycled(bitmap); mNativeCanvas = initRaster(bitmap.ni()); + mFinalizer = new CanvasFinalizer(mNativeCanvas); mBitmap = bitmap; mDensity = bitmap.mDensity; } @@ -110,6 +129,7 @@ public class Canvas { throw new IllegalStateException(); } mNativeCanvas = nativeCanvas; + mFinalizer = new CanvasFinalizer(nativeCanvas); mDensity = Bitmap.getDefaultDensity(); } @@ -1607,19 +1627,6 @@ public class Canvas { public void releaseContext() { } - @Override - protected void finalize() throws Throwable { - try { - super.finalize(); - } finally { - // If the constructor threw an exception before setting mNativeCanvas, - // the native finalizer must not be invoked. - if (mNativeCanvas != 0) { - finalizer(mNativeCanvas); - } - } - } - /** * Free up as much memory as possible from private caches (e.g. fonts, images) * diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 00de39b..1c93ea6 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -60,7 +60,9 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) } void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->safeRef(); + SkPixelRef* pixref = bitmapResource->pixelRef(); + if (pixref) pixref->globalRef(); + bitmapResource->getColorTable()->safeRef(); incrementRefcount((void*)bitmapResource, kBitmap); } @@ -89,7 +91,9 @@ void ResourceCache::decrementRefcount(void* resource) { } void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->safeUnref(); + SkPixelRef* pixref = bitmapResource->pixelRef(); + if (pixref) pixref->globalUnref(); + bitmapResource->getColorTable()->safeUnref(); decrementRefcount((void*)bitmapResource); } |