Loading core/java/android/view/GLES20Canvas.java +15 −13 Original line number Original line Diff line number Diff line Loading @@ -459,35 +459,37 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing patches // Shaders are ignored when drawing patches boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top, nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks, dst.right, dst.bottom, nativePaint); dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top, private native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks, float right, float bottom, int paint); float left, float top, float right, float bottom, int paint); @Override @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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); 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 @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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); 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 @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { Loading @@ -507,7 +509,7 @@ class GLES20Canvas extends HardwareCanvas { bottom = src.bottom; 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); dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } Loading @@ -517,12 +519,12 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom, nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right, dst.left, dst.top, dst.right, dst.bottom, nativePaint); src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); 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 srcLeft, float srcTop, float srcRight, float srcBottom, float left, float top, float right, float bottom, int paint); float left, float top, float right, float bottom, int paint); Loading @@ -534,7 +536,7 @@ class GLES20Canvas extends HardwareCanvas { final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; 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 Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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(); b.recycle(); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } Loading core/jni/android/graphics/Bitmap.cpp +13 −15 Original line number Original line Diff line number Diff line #include "SkBitmap.h" #include "SkBitmap.h" #include "SkPixelRef.h" #include "SkImageEncoder.h" #include "SkImageEncoder.h" #include "SkColorPriv.h" #include "SkColorPriv.h" #include "GraphicsJNI.h" #include "GraphicsJNI.h" Loading Loading @@ -210,11 +211,6 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) { static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, int offset, int stride, int width, int height, int offset, int stride, int width, int height, SkBitmap::Config config, jboolean isMutable) { SkBitmap::Config config, jboolean isMutable) { if (width <= 0 || height <= 0) { doThrowIAE(env, "width and height must be > 0"); return NULL; } if (NULL != jColors) { if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { if (n < SkAbs32(stride) * (size_t)height) { Loading @@ -226,7 +222,9 @@ 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, true)) { jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); if (NULL == buff) { return NULL; return NULL; } } Loading @@ -235,21 +233,19 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap); 0, 0, width, height, bitmap); } } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL); NULL); } } 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, true); JavaPixelAllocator allocator(env); if (!src->copyTo(&result, dstConfig, &allocator)) { if (!src->copyTo(&result, dstConfig, &allocator)) { return NULL; return NULL; } } return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable, return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL); NULL); } } static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { Loading Loading @@ -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(); ctable->safeUnref(); delete bitmap; delete bitmap; return NULL; return NULL; Loading @@ -393,7 +390,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { memcpy(bitmap->getPixels(), p->readInplace(size), size); memcpy(bitmap->getPixels(), p->readInplace(size), size); bitmap->unlockPixels(); 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, static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, Loading Loading @@ -447,8 +444,9 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jintArray offsetXY) { jintArray offsetXY) { SkIPoint offset; SkIPoint offset; SkBitmap* dst = new SkBitmap; 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) { if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { int* array = env->GetIntArrayElements(offsetXY, NULL); int* array = env->GetIntArrayElements(offsetXY, NULL); array[0] = offset.fX; array[0] = offset.fX; Loading @@ -456,7 +454,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0); env->ReleaseIntArrayElements(offsetXY, array, 0); } } return GraphicsJNI::createBitmap(env, dst, true, NULL); return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// Loading core/jni/android/graphics/BitmapFactory.cpp +2 −2 Original line number Original line Diff line number Diff line Loading @@ -291,7 +291,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 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(); adb.detach(); if (padding) { if (padding) { Loading Loading @@ -322,7 +322,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, return javaBitmap; return javaBitmap; } } // now create the java bitmap // 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, static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, Loading core/jni/android/graphics/BitmapRegionDecoder.cpp +6 −5 Original line number Original line Diff line number Diff line Loading @@ -79,7 +79,7 @@ static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) { return nullObjectReturn("SkImageDecoder::Factory returned null"); return nullObjectReturn("SkImageDecoder::Factory returned null"); } } JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env); decoder->setAllocator(javaAllocator); decoder->setAllocator(javaAllocator); JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); decoder->setReporter(javaMemoryReporter); decoder->setReporter(javaMemoryReporter); Loading Loading @@ -241,15 +241,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b getMimeTypeString(env, decoder->getFormat())); 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(); adb.detach(); SkPixelRef* pr; SkPixelRef* pr = bitmap->pixelRef(); pr = bitmap->pixelRef(); // promise we will never change our pixels (great for sharing and pictures) // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); pr->setImmutable(); // now create the java bitmap // 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) { static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { Loading core/jni/android/graphics/Graphics.cpp +150 −74 Original line number Original line Diff line number Diff line Loading @@ -2,6 +2,9 @@ #include "jni.h" #include "jni.h" #include "GraphicsJNI.h" #include "GraphicsJNI.h" #include "SkCanvas.h" #include "SkDevice.h" #include "SkPicture.h" #include "SkPicture.h" #include "SkRegion.h" #include "SkRegion.h" #include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h> Loading Loading @@ -163,7 +166,6 @@ static jfieldID gPointF_yFieldID; static jclass gBitmap_class; static jclass gBitmap_class; static jfieldID gBitmap_nativeInstanceID; static jfieldID gBitmap_nativeInstanceID; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_allocBufferMethodID; static jclass gBitmapConfig_class; static jclass gBitmapConfig_class; static jfieldID gBitmapConfig_nativeInstanceID; static jfieldID gBitmapConfig_nativeInstanceID; Loading Loading @@ -360,16 +362,16 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, jbyteArray ninepatch, int density) bool isMutable, jbyteArray ninepatch, int density) { { SkASSERT(bitmap != NULL); SkASSERT(bitmap); SkASSERT(NULL != bitmap->pixelRef()); SkASSERT(bitmap->pixelRef()); jobject obj = env->AllocObject(gBitmap_class); jobject obj = env->AllocObject(gBitmap_class); if (obj) { if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, env->CallVoidMethod(obj, gBitmap_constructorMethodID, (jint)bitmap, isMutable, ninepatch, density); (jint)bitmap, buffer, isMutable, ninepatch, density); if (hasException(env)) { if (hasException(env)) { obj = NULL; obj = NULL; } } Loading @@ -377,6 +379,13 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, return obj; 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) jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) { { SkASSERT(bitmap != NULL); SkASSERT(bitmap != NULL); Loading Loading @@ -408,8 +417,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) return obj; return obj; } } #include "SkPixelRef.h" static JNIEnv* vm2env(JavaVM* vm) static JNIEnv* vm2env(JavaVM* vm) { { JNIEnv* env = NULL; JNIEnv* env = NULL; Loading @@ -427,17 +434,7 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include "SkMallocPixelRef.h" AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, /* 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) { SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { SkASSERT(storage); SkASSERT(storage); SkASSERT(env); SkASSERT(env); Loading @@ -446,68 +443,116 @@ public: SkDebugf("------ [%p] env->GetJavaVM failed\n", env); SkDebugf("------ [%p] env->GetJavaVM failed\n", env); sk_throw(); 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); 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 if (fStorageObj && fHasGlobalRef) { env->CallVoidMethod(gVMRuntime_singleton, env->DeleteGlobalRef(fStorageObj); gVMRuntime_trackExternalFreeMethodID, } jsize); fStorageObj = NULL; if (GraphicsJNI::hasException(env)) { env->ExceptionClear(); // Set this to NULL to prevent the SkMallocPixelRef destructor // from freeing the memory. fStorage = NULL; } } } } private: void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) { JavaVM* fVM; if (!fHasGlobalRef) { }; fStorageObj = arr; } } bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, void AndroidPixelRef::globalRef() { SkColorTable* ctable, bool reportSizeToVM) { 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(); } /////////////////////////////////////////////////////////////////////////////// jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { 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", "bitmap size exceeds 32bits"); "bitmap size exceeds 32bits"); return false; return NULL; } } size_t size = size64.get32(); size_t size = size64.get32(); jlong jsize = size; // the VM wants longs for the size jbyteArray arrayObj = env->NewByteArray(size); if (reportSizeToVM) { if (arrayObj) { // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); jbyte *addr = env->GetByteArrayElements(arrayObj, NULL); bool r = env->CallBooleanMethod(gVMRuntime_singleton, env->ReleaseByteArrayElements(arrayObj, addr, 0); gVMRuntime_trackExternalAllocationMethodID, if (addr) { jsize); SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable); if (GraphicsJNI::hasException(env)) { bitmap->setPixelRef(pr)->unref(); return false; // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too bitmap->lockPixels(); } } if (!r) { LOGE("VM won't let us allocate %zd bytes\n", size); doThrowOOME(env, "bitmap size exceeds VM budget"); return false; } } 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 // 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) { 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; return false; } } SkPixelRef* pr = reportSizeToVM ? SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable); new AndroidPixelRef(env, addr, size, ctable) : new SkMallocPixelRef(addr, size, ctable); bitmap->setPixelRef(pr)->unref(); 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 Loading @@ -517,8 +562,9 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap) : fReportSizeToVM(reportSizeToVM) { : fAllocateInJavaHeap(allocateInJavaHeap), fStorageObj(NULL) { if (env->GetJavaVM(&fVM) != JNI_OK) { if (env->GetJavaVM(&fVM) != JNI_OK) { SkDebugf("------ [%p] env->GetJavaVM failed\n", env); SkDebugf("------ [%p] env->GetJavaVM failed\n", env); sk_throw(); sk_throw(); Loading @@ -527,7 +573,19 @@ JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { JNIEnv* env = vm2env(fVM); 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); } } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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[]) static jclass make_globalref(JNIEnv* env, const char classname[]) { { jclass c = env->FindClass(classname); jclass c = env->FindClass(classname); Loading Loading @@ -611,8 +688,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 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>", gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(IZ[BI)V"); "(I[BZ[BI)V"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); Loading Loading
core/java/android/view/GLES20Canvas.java +15 −13 Original line number Original line Diff line number Diff line Loading @@ -459,35 +459,37 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing patches // Shaders are ignored when drawing patches boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top, nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks, dst.right, dst.bottom, nativePaint); dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top, private native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks, float right, float bottom, int paint); float left, float top, float right, float bottom, int paint); @Override @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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); 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 @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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); 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 @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { Loading @@ -507,7 +509,7 @@ class GLES20Canvas extends HardwareCanvas { bottom = src.bottom; 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); dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } Loading @@ -517,12 +519,12 @@ class GLES20Canvas extends HardwareCanvas { // Shaders are ignored when drawing bitmaps // Shaders are ignored when drawing bitmaps boolean hasColorFilter = paint != null && setupColorFilter(paint); boolean hasColorFilter = paint != null && setupColorFilter(paint); final int nativePaint = paint == null ? 0 : paint.mNativePaint; final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom, nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right, dst.left, dst.top, dst.right, dst.bottom, nativePaint); src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint); if (hasColorFilter) nResetModifiers(mRenderer); 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 srcLeft, float srcTop, float srcRight, float srcBottom, float left, float top, float right, float bottom, int paint); float left, float top, float right, float bottom, int paint); Loading @@ -534,7 +536,7 @@ class GLES20Canvas extends HardwareCanvas { final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; 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 Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); final int nativePaint = paint == null ? 0 : paint.mNativePaint; 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(); b.recycle(); if (hasColorFilter) nResetModifiers(mRenderer); if (hasColorFilter) nResetModifiers(mRenderer); } } Loading
core/jni/android/graphics/Bitmap.cpp +13 −15 Original line number Original line Diff line number Diff line #include "SkBitmap.h" #include "SkBitmap.h" #include "SkPixelRef.h" #include "SkImageEncoder.h" #include "SkImageEncoder.h" #include "SkColorPriv.h" #include "SkColorPriv.h" #include "GraphicsJNI.h" #include "GraphicsJNI.h" Loading Loading @@ -210,11 +211,6 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) { static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, int offset, int stride, int width, int height, int offset, int stride, int width, int height, SkBitmap::Config config, jboolean isMutable) { SkBitmap::Config config, jboolean isMutable) { if (width <= 0 || height <= 0) { doThrowIAE(env, "width and height must be > 0"); return NULL; } if (NULL != jColors) { if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { if (n < SkAbs32(stride) * (size_t)height) { Loading @@ -226,7 +222,9 @@ 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, true)) { jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); if (NULL == buff) { return NULL; return NULL; } } Loading @@ -235,21 +233,19 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap); 0, 0, width, height, bitmap); } } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL); NULL); } } 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, true); JavaPixelAllocator allocator(env); if (!src->copyTo(&result, dstConfig, &allocator)) { if (!src->copyTo(&result, dstConfig, &allocator)) { return NULL; return NULL; } } return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable, return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL); NULL); } } static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { Loading Loading @@ -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(); ctable->safeUnref(); delete bitmap; delete bitmap; return NULL; return NULL; Loading @@ -393,7 +390,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { memcpy(bitmap->getPixels(), p->readInplace(size), size); memcpy(bitmap->getPixels(), p->readInplace(size), size); bitmap->unlockPixels(); 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, static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, Loading Loading @@ -447,8 +444,9 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jintArray offsetXY) { jintArray offsetXY) { SkIPoint offset; SkIPoint offset; SkBitmap* dst = new SkBitmap; 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) { if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { int* array = env->GetIntArrayElements(offsetXY, NULL); int* array = env->GetIntArrayElements(offsetXY, NULL); array[0] = offset.fX; array[0] = offset.fX; Loading @@ -456,7 +454,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0); env->ReleaseIntArrayElements(offsetXY, array, 0); } } return GraphicsJNI::createBitmap(env, dst, true, NULL); return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// Loading
core/jni/android/graphics/BitmapFactory.cpp +2 −2 Original line number Original line Diff line number Diff line Loading @@ -291,7 +291,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); 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(); adb.detach(); if (padding) { if (padding) { Loading Loading @@ -322,7 +322,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, return javaBitmap; return javaBitmap; } } // now create the java bitmap // 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, static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, Loading
core/jni/android/graphics/BitmapRegionDecoder.cpp +6 −5 Original line number Original line Diff line number Diff line Loading @@ -79,7 +79,7 @@ static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) { return nullObjectReturn("SkImageDecoder::Factory returned null"); return nullObjectReturn("SkImageDecoder::Factory returned null"); } } JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env); decoder->setAllocator(javaAllocator); decoder->setAllocator(javaAllocator); JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); decoder->setReporter(javaMemoryReporter); decoder->setReporter(javaMemoryReporter); Loading Loading @@ -241,15 +241,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b getMimeTypeString(env, decoder->getFormat())); 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(); adb.detach(); SkPixelRef* pr; SkPixelRef* pr = bitmap->pixelRef(); pr = bitmap->pixelRef(); // promise we will never change our pixels (great for sharing and pictures) // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); pr->setImmutable(); // now create the java bitmap // 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) { static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { Loading
core/jni/android/graphics/Graphics.cpp +150 −74 Original line number Original line Diff line number Diff line Loading @@ -2,6 +2,9 @@ #include "jni.h" #include "jni.h" #include "GraphicsJNI.h" #include "GraphicsJNI.h" #include "SkCanvas.h" #include "SkDevice.h" #include "SkPicture.h" #include "SkPicture.h" #include "SkRegion.h" #include "SkRegion.h" #include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h> Loading Loading @@ -163,7 +166,6 @@ static jfieldID gPointF_yFieldID; static jclass gBitmap_class; static jclass gBitmap_class; static jfieldID gBitmap_nativeInstanceID; static jfieldID gBitmap_nativeInstanceID; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_allocBufferMethodID; static jclass gBitmapConfig_class; static jclass gBitmapConfig_class; static jfieldID gBitmapConfig_nativeInstanceID; static jfieldID gBitmapConfig_nativeInstanceID; Loading Loading @@ -360,16 +362,16 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, jbyteArray ninepatch, int density) bool isMutable, jbyteArray ninepatch, int density) { { SkASSERT(bitmap != NULL); SkASSERT(bitmap); SkASSERT(NULL != bitmap->pixelRef()); SkASSERT(bitmap->pixelRef()); jobject obj = env->AllocObject(gBitmap_class); jobject obj = env->AllocObject(gBitmap_class); if (obj) { if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, env->CallVoidMethod(obj, gBitmap_constructorMethodID, (jint)bitmap, isMutable, ninepatch, density); (jint)bitmap, buffer, isMutable, ninepatch, density); if (hasException(env)) { if (hasException(env)) { obj = NULL; obj = NULL; } } Loading @@ -377,6 +379,13 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, return obj; 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) jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) { { SkASSERT(bitmap != NULL); SkASSERT(bitmap != NULL); Loading Loading @@ -408,8 +417,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) return obj; return obj; } } #include "SkPixelRef.h" static JNIEnv* vm2env(JavaVM* vm) static JNIEnv* vm2env(JavaVM* vm) { { JNIEnv* env = NULL; JNIEnv* env = NULL; Loading @@ -427,17 +434,7 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include "SkMallocPixelRef.h" AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, /* 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) { SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { SkASSERT(storage); SkASSERT(storage); SkASSERT(env); SkASSERT(env); Loading @@ -446,68 +443,116 @@ public: SkDebugf("------ [%p] env->GetJavaVM failed\n", env); SkDebugf("------ [%p] env->GetJavaVM failed\n", env); sk_throw(); 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); 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 if (fStorageObj && fHasGlobalRef) { env->CallVoidMethod(gVMRuntime_singleton, env->DeleteGlobalRef(fStorageObj); gVMRuntime_trackExternalFreeMethodID, } jsize); fStorageObj = NULL; if (GraphicsJNI::hasException(env)) { env->ExceptionClear(); // Set this to NULL to prevent the SkMallocPixelRef destructor // from freeing the memory. fStorage = NULL; } } } } private: void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) { JavaVM* fVM; if (!fHasGlobalRef) { }; fStorageObj = arr; } } bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, void AndroidPixelRef::globalRef() { SkColorTable* ctable, bool reportSizeToVM) { 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(); } /////////////////////////////////////////////////////////////////////////////// jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { 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", "bitmap size exceeds 32bits"); "bitmap size exceeds 32bits"); return false; return NULL; } } size_t size = size64.get32(); size_t size = size64.get32(); jlong jsize = size; // the VM wants longs for the size jbyteArray arrayObj = env->NewByteArray(size); if (reportSizeToVM) { if (arrayObj) { // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); jbyte *addr = env->GetByteArrayElements(arrayObj, NULL); bool r = env->CallBooleanMethod(gVMRuntime_singleton, env->ReleaseByteArrayElements(arrayObj, addr, 0); gVMRuntime_trackExternalAllocationMethodID, if (addr) { jsize); SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable); if (GraphicsJNI::hasException(env)) { bitmap->setPixelRef(pr)->unref(); return false; // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too bitmap->lockPixels(); } } if (!r) { LOGE("VM won't let us allocate %zd bytes\n", size); doThrowOOME(env, "bitmap size exceeds VM budget"); return false; } } 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 // 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) { 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; return false; } } SkPixelRef* pr = reportSizeToVM ? SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable); new AndroidPixelRef(env, addr, size, ctable) : new SkMallocPixelRef(addr, size, ctable); bitmap->setPixelRef(pr)->unref(); 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 Loading @@ -517,8 +562,9 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap) : fReportSizeToVM(reportSizeToVM) { : fAllocateInJavaHeap(allocateInJavaHeap), fStorageObj(NULL) { if (env->GetJavaVM(&fVM) != JNI_OK) { if (env->GetJavaVM(&fVM) != JNI_OK) { SkDebugf("------ [%p] env->GetJavaVM failed\n", env); SkDebugf("------ [%p] env->GetJavaVM failed\n", env); sk_throw(); sk_throw(); Loading @@ -527,7 +573,19 @@ JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { JNIEnv* env = vm2env(fVM); 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); } } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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[]) static jclass make_globalref(JNIEnv* env, const char classname[]) { { jclass c = env->FindClass(classname); jclass c = env->FindClass(classname); Loading Loading @@ -611,8 +688,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 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>", gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(IZ[BI)V"); "(I[BZ[BI)V"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V"); Loading