summaryrefslogtreecommitdiffstats
path: root/core/jni
diff options
context:
space:
mode:
authorPatrick Dubroy <dubroy@google.com>2010-12-01 11:23:13 -0800
committerPatrick Dubroy <dubroy@google.com>2010-12-14 16:50:50 -0800
commite4ac2d6b5723c95e648c489b187ddde449452c13 (patch)
treea783470220e1840761dc6696f3e5f4cc7870054b /core/jni
parent89f8d63a991f2dc4a961ad92ab5bb6b6c1ecd60f (diff)
downloadframeworks_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.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
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 },