Merge change 7696 into donut

* changes:
  add hidden Options field for native allocations
This commit is contained in:
Android (Google) Code Review
2009-07-17 11:04:06 -07:00
5 changed files with 60 additions and 31 deletions

View File

@ -224,7 +224,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
SkBitmap bitmap; SkBitmap bitmap;
bitmap.setConfig(config, width, height); bitmap.setConfig(config, width, height);
if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) { if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {
return NULL; return NULL;
} }
@ -240,7 +240,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
SkBitmap::Config dstConfig, jboolean isMutable) { SkBitmap::Config dstConfig, jboolean isMutable) {
SkBitmap result; SkBitmap result;
JavaPixelAllocator allocator(env); JavaPixelAllocator allocator(env, true);
if (!src->copyTo(&result, dstConfig, &allocator)) { if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL; return NULL;
@ -356,7 +356,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
} }
} }
if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) { if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {
ctable->safeUnref(); ctable->safeUnref();
delete bitmap; delete bitmap;
return NULL; return NULL;

View File

@ -23,6 +23,7 @@ static jfieldID gOptions_configFieldID;
static jfieldID gOptions_ditherFieldID; static jfieldID gOptions_ditherFieldID;
static jfieldID gOptions_purgeableFieldID; static jfieldID gOptions_purgeableFieldID;
static jfieldID gOptions_shareableFieldID; static jfieldID gOptions_shareableFieldID;
static jfieldID gOptions_nativeAllocFieldID;
static jfieldID gOptions_widthFieldID; static jfieldID gOptions_widthFieldID;
static jfieldID gOptions_heightFieldID; static jfieldID gOptions_heightFieldID;
static jfieldID gOptions_mimeFieldID; static jfieldID gOptions_mimeFieldID;
@ -300,6 +301,11 @@ static bool optionsShareable(JNIEnv* env, jobject options) {
env->GetBooleanField(options, gOptions_shareableFieldID); env->GetBooleanField(options, gOptions_shareableFieldID);
} }
static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
return NULL == options ||
!env->GetBooleanField(options, gOptions_nativeAllocFieldID);
}
static jobject nullObjectReturn(const char msg[]) { static jobject nullObjectReturn(const char msg[]) {
if (msg) { if (msg) {
SkDebugf("--- %s\n", msg); SkDebugf("--- %s\n", msg);
@ -330,6 +336,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
SkBitmap::Config prefConfig = SkBitmap::kNo_Config; SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
bool doDither = true; bool doDither = true;
bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
bool reportSizeToVM = optionsReportSizeToVM(env, options);
if (NULL != options) { if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
@ -355,7 +362,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
decoder->setDitherImage(doDither); decoder->setDitherImage(doDither);
NinePatchPeeker peeker; NinePatchPeeker peeker;
JavaPixelAllocator javaAllocator(env); JavaPixelAllocator javaAllocator(env, reportSizeToVM);
SkBitmap* bitmap = new SkBitmap; SkBitmap* bitmap = new SkBitmap;
Res_png_9patch dummy9Patch; Res_png_9patch dummy9Patch;
@ -699,6 +706,7 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) {
gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");

View File

@ -5,6 +5,7 @@
#include "SkRegion.h" #include "SkRegion.h"
#include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h>
//#define REPORT_SIZE_TO_JVM
//#define TRACK_LOCK_COUNT //#define TRACK_LOCK_COUNT
void doThrow(JNIEnv* env, const char* exc, const char* msg) { void doThrow(JNIEnv* env, const char* exc, const char* msg) {
@ -444,7 +445,7 @@ private:
}; };
bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable) { SkColorTable* ctable, bool reportSizeToVM) {
Sk64 size64 = bitmap->getSize64(); Sk64 size64 = bitmap->getSize64();
if (size64.isNeg() || !size64.is32()) { if (size64.isNeg() || !size64.is32()) {
doThrow(env, "java/lang/IllegalArgumentException", doThrow(env, "java/lang/IllegalArgumentException",
@ -453,35 +454,41 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
} }
size_t size = size64.get32(); size_t size = size64.get32();
// SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
jlong jsize = size; // the VM wants longs for the size jlong jsize = size; // the VM wants longs for the size
bool r = env->CallBooleanMethod(gVMRuntime_singleton, if (reportSizeToVM) {
gVMRuntime_trackExternalAllocationMethodID, // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
jsize); bool r = env->CallBooleanMethod(gVMRuntime_singleton,
if (GraphicsJNI::hasException(env)) { gVMRuntime_trackExternalAllocationMethodID,
return false; 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;
}
} }
if (!r) {
LOGE("VM won't let us allocate %zd bytes\n", size);
doThrowOOME(env, "bitmap size exceeds VM budget");
return false;
}
// call the version of malloc that returns null on failure // call the version of malloc that returns null on failure
void* addr = sk_malloc_flags(size, 0); void* addr = sk_malloc_flags(size, 0);
if (NULL == addr) { if (NULL == addr) {
// SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); if (reportSizeToVM) {
// we didn't actually allocate it, so inform the VM // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);
env->CallVoidMethod(gVMRuntime_singleton, // we didn't actually allocate it, so inform the VM
gVMRuntime_trackExternalFreeMethodID, env->CallVoidMethod(gVMRuntime_singleton,
jsize); gVMRuntime_trackExternalFreeMethodID,
if (!GraphicsJNI::hasException(env)) { jsize);
doThrowOOME(env, "bitmap size too large for malloc"); if (!GraphicsJNI::hasException(env)) {
doThrowOOME(env, "bitmap size too large for malloc");
}
} }
return false; return false;
} }
bitmap->setPixelRef(new AndroidPixelRef(env, addr, size, ctable))->unref(); SkPixelRef* pr = reportSizeToVM ?
new AndroidPixelRef(env, addr, size, ctable) :
new SkMallocPixelRef(addr, size, ctable);
bitmap->setPixelRef(pr)->unref();
// since we're already allocated, we lockPixels right away // since we're already allocated, we lockPixels right away
// HeapAllocator behaves this way too // HeapAllocator behaves this way too
bitmap->lockPixels(); bitmap->lockPixels();
@ -490,12 +497,11 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) : fEnv(env) JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
{ : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
}
bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable); return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -59,7 +59,8 @@ public:
Returns true on success. If it returns false, then it failed, and the Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised. appropriate exception will have been raised.
*/ */
static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable); static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable,
bool reportSizeToVM);
/** Copy the colors in colors[] to the bitmap, convert to the correct /** Copy the colors in colors[] to the bitmap, convert to the correct
format along the way. format along the way.
@ -71,12 +72,13 @@ public:
class JavaPixelAllocator : public SkBitmap::Allocator { class JavaPixelAllocator : public SkBitmap::Allocator {
public: public:
JavaPixelAllocator(JNIEnv* env); JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM);
// overrides // overrides
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
private: private:
JNIEnv* fEnv; JNIEnv* fEnv;
bool fReportSizeToVM;
}; };
class AutoJavaFloatArray { class AutoJavaFloatArray {

View File

@ -128,6 +128,19 @@ public class BitmapFactory {
*/ */
public boolean inInputShareable; public boolean inInputShareable;
/**
* Normally bitmap allocations count against the dalvik heap, which
* means they help trigger GCs when a lot have been allocated. However,
* in rare cases, the caller may want to allocate the bitmap outside of
* that heap. To request that, set inNativeAlloc to true. In these
* rare instances, it is solely up to the caller to ensure that OOM is
* managed explicitly by calling bitmap.recycle() as soon as such a
* bitmap is no longer needed.
*
* @hide pending API council approval
*/
public boolean inNativeAlloc;
/** /**
* The resulting width of the bitmap, set independent of the state of * The resulting width of the bitmap, set independent of the state of
* inJustDecodeBounds. However, if there is an error trying to decode, * inJustDecodeBounds. However, if there is an error trying to decode,