Loading core/java/android/view/GLES20Canvas.java +15 −13 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } Loading @@ -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); Loading @@ -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); } Loading core/jni/android/graphics/Bitmap.cpp +13 −15 Original line number Diff line number Diff line #include "SkBitmap.h" #include "SkPixelRef.h" #include "SkImageEncoder.h" #include "SkColorPriv.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, 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) { Loading @@ -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; } Loading @@ -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) { 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(); delete bitmap; return NULL; Loading @@ -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, Loading Loading @@ -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; Loading @@ -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); } /////////////////////////////////////////////////////////////////////////////// Loading core/jni/android/graphics/BitmapFactory.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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, Loading core/jni/android/graphics/BitmapRegionDecoder.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) { Loading core/jni/android/graphics/Graphics.cpp +150 −74 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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); Loading Loading @@ -408,8 +417,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) return obj; } #include "SkPixelRef.h" static JNIEnv* vm2env(JavaVM* vm) { JNIEnv* env = NULL; Loading @@ -427,17 +434,7 @@ 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, AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { SkASSERT(storage); SkASSERT(env); Loading @@ -446,68 +443,116 @@ public: 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; } } private: JavaVM* fVM; }; void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) { if (!fHasGlobalRef) { fStorageObj = arr; } } bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable, bool reportSizeToVM) { 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(); } /////////////////////////////////////////////////////////////////////////////// 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; 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(); } 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 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 Loading @@ -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(); Loading @@ -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); } //////////////////////////////////////////////////////////////////////////////// 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[]) { 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_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"); Loading Loading
core/java/android/view/GLES20Canvas.java +15 −13 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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); } Loading @@ -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); Loading @@ -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); } Loading
core/jni/android/graphics/Bitmap.cpp +13 −15 Original line number Diff line number Diff line #include "SkBitmap.h" #include "SkPixelRef.h" #include "SkImageEncoder.h" #include "SkColorPriv.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, 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) { Loading @@ -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; } Loading @@ -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) { 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(); delete bitmap; return NULL; Loading @@ -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, Loading Loading @@ -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; Loading @@ -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); } /////////////////////////////////////////////////////////////////////////////// Loading
core/jni/android/graphics/BitmapFactory.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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, Loading
core/jni/android/graphics/BitmapRegionDecoder.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) { Loading
core/jni/android/graphics/Graphics.cpp +150 −74 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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; } Loading @@ -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); Loading Loading @@ -408,8 +417,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) return obj; } #include "SkPixelRef.h" static JNIEnv* vm2env(JavaVM* vm) { JNIEnv* env = NULL; Loading @@ -427,17 +434,7 @@ 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, AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj, SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { SkASSERT(storage); SkASSERT(env); Loading @@ -446,68 +443,116 @@ public: 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; } } private: JavaVM* fVM; }; void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) { if (!fHasGlobalRef) { fStorageObj = arr; } } bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable, bool reportSizeToVM) { 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(); } /////////////////////////////////////////////////////////////////////////////// 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; 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(); } 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 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 Loading @@ -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(); Loading @@ -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); } //////////////////////////////////////////////////////////////////////////////// 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[]) { 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_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"); Loading