summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Dubroy <dubroy@google.com>2010-12-14 16:56:07 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-12-14 16:56:07 -0800
commit982b71bf4713d57b0fdb9acaaf9f9465a817aacb (patch)
tree211deba434e54321f1427f3e3d1d2057213aae57
parent52e08834094aef141c1352c601415132785f533d (diff)
parente4ac2d6b5723c95e648c489b187ddde449452c13 (diff)
downloadframeworks_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.java28
-rw-r--r--core/jni/android/graphics/Bitmap.cpp28
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp4
-rw-r--r--core/jni/android/graphics/BitmapRegionDecoder.cpp11
-rw-r--r--core/jni/android/graphics/Graphics.cpp224
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h80
-rw-r--r--core/jni/android_emoji_EmojiFactory.cpp4
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp38
-rw-r--r--graphics/java/android/graphics/Bitmap.java52
-rw-r--r--graphics/java/android/graphics/Canvas.java33
-rw-r--r--libs/hwui/ResourceCache.cpp8
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);
}