diff options
-rwxr-xr-x | core/jni/android/graphics/Bitmap.cpp | 592 | ||||
-rw-r--r-- | core/jni/android/graphics/Bitmap.h | 114 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 52 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 24 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 120 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 73 | ||||
-rw-r--r-- | core/jni/android_view_SurfaceControl.cpp | 22 | ||||
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 88 |
8 files changed, 701 insertions, 384 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 5c95f8a..8ae2e3b 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1,3 +1,7 @@ +#define LOG_TAG "Bitmap" + +#include "Bitmap.h" + #include "Paint.h" #include "SkBitmap.h" #include "SkPixelRef.h" @@ -14,11 +18,322 @@ #include "android_util_Binder.h" #include "android_nio_utils.h" #include "CreateJavaOutputStreamAdaptor.h" +#include <Caches.h> #include "core_jni_helpers.h" #include <jni.h> +namespace android { + +class WrappedPixelRef : public SkPixelRef { +public: + WrappedPixelRef(Bitmap* wrapper, void* storage, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : SkPixelRef(info) + , mBitmap(*wrapper) + , mStorage(storage) { + reconfigure(info, rowBytes, ctable); + } + + ~WrappedPixelRef() { + // Tell SkRefCnt that everything is as it expects by forcing + // the refcnt to 1 + internal_dispose_restore_refcnt_to_1(); + SkSafeUnref(mColorTable); + } + + void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { + if (kIndex_8_SkColorType != info.colorType()) { + ctable = nullptr; + } + mRowBytes = rowBytes; + if (mColorTable != ctable) { + SkSafeUnref(mColorTable); + mColorTable = ctable; + SkSafeRef(mColorTable); + } + // Dirty hack is dirty + // TODO: Figure something out here, Skia's current design makes this + // really hard to work with. Skia really, really wants immutable objects, + // but with the nested-ref-count hackery going on that's just not + // feasible without going insane trying to figure it out + SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info()); + *myInfo = info; + + // Docs say to only call this in the ctor, but we're going to call + // it anyway even if this isn't always the ctor. + // TODO: Fix this too as part of the above TODO + setPreLocked(mStorage, mRowBytes, mColorTable); + } + + // Can't mark as override since SkPixelRef::rowBytes isn't virtual + // but that's OK since we just want BitmapWrapper to be able to rely + // on calling rowBytes() on an unlocked pixelref, which it will be + // doing on a WrappedPixelRef type, not a SkPixelRef, so static + // dispatching will do what we want. + size_t rowBytes() const { return mRowBytes; } + SkColorTable* colorTable() const { return mColorTable; } + + bool hasHardwareMipMap() const { + return mHasHardwareMipMap; + } + + void setHasHardwareMipMap(bool hasMipMap) { + mHasHardwareMipMap = hasMipMap; + } + +protected: + virtual bool onNewLockPixels(LockRec* rec) override { + rec->fPixels = mStorage; + rec->fRowBytes = mRowBytes; + rec->fColorTable = mColorTable; + return true; + } + + virtual void onUnlockPixels() override { + // nothing + } + + virtual size_t getAllocatedSizeInBytes() const override { + return info().getSafeSize(mRowBytes); + } + +private: + Bitmap& mBitmap; + void* mStorage; + size_t mRowBytes = 0; + SkColorTable* mColorTable = nullptr; + bool mHasHardwareMipMap = false; + + virtual void internal_dispose() const override { + mBitmap.onStrongRefDestroyed(); + } +}; + +Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : mPixelStorageType(PixelStorageType::Java) { + env->GetJavaVM(&mPixelStorage.java.jvm); + mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj); + mPixelStorage.java.jstrongRef = nullptr; + mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); + // Note: this will trigger a call to onStrongRefDestroyed(), but + // we want the pixel ref to have a ref count of 0 at this point + mPixelRef->unref(); +} + +Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) + : mPixelStorageType(PixelStorageType::External) { + mPixelStorage.external.address = address; + mPixelStorage.external.context = context; + mPixelStorage.external.freeFunc = freeFunc; + mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable)); + // Note: this will trigger a call to onStrongRefDestroyed(), but + // we want the pixel ref to have a ref count of 0 at this point + mPixelRef->unref(); +} + +Bitmap::~Bitmap() { + doFreePixels(); +} + +void Bitmap::freePixels() { + AutoMutex _lock(mLock); + if (mPinnedRefCount == 0) { + doFreePixels(); + mPixelStorageType = PixelStorageType::Invalid; + } +} + +void Bitmap::doFreePixels() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + // already free'd, nothing to do + break; + case PixelStorageType::External: + mPixelStorage.external.freeFunc(mPixelStorage.external.address, + mPixelStorage.external.context); + break; + case PixelStorageType::Java: + JNIEnv* env = jniEnv(); + LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef, + "Deleting a bitmap wrapper while there are outstanding strong " + "references! mPinnedRefCount = %d", mPinnedRefCount); + env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef); + break; + } + + if (android::uirenderer::Caches::hasInstance()) { + android::uirenderer::Caches::getInstance().textureCache.releaseTexture( + mPixelRef->getStableID()); + } +} + +bool Bitmap::hasHardwareMipMap() { + return mPixelRef->hasHardwareMipMap(); +} + +void Bitmap::setHasHardwareMipMap(bool hasMipMap) { + mPixelRef->setHasHardwareMipMap(hasMipMap); +} + +const SkImageInfo& Bitmap::info() const { + assertValid(); + return mPixelRef->info(); +} + +size_t Bitmap::rowBytes() const { + return mPixelRef->rowBytes(); +} + +SkPixelRef* Bitmap::pixelRef() const { + assertValid(); + return mPixelRef.get(); +} + +void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes, + SkColorTable* ctable) { + mPixelRef->reconfigure(info, rowBytes, ctable); +} + +void Bitmap::reconfigure(const SkImageInfo& info) { + mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable()); +} + +void Bitmap::detachFromJava() { + bool disposeSelf; + { + android::AutoMutex _lock(mLock); + mAttachedToJava = false; + disposeSelf = shouldDisposeSelfLocked(); + } + if (disposeSelf) { + delete this; + } +} + +bool Bitmap::shouldDisposeSelfLocked() { + return mPinnedRefCount == 0 && !mAttachedToJava; +} + +JNIEnv* Bitmap::jniEnv() { + JNIEnv* env; + auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + LOG_ALWAYS_FATAL_IF(success != JNI_OK, + "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm); + return env; +} + +void Bitmap::onStrongRefDestroyed() { + bool disposeSelf = false; + { + android::AutoMutex _lock(mLock); + if (mPinnedRefCount > 0) { + mPinnedRefCount--; + if (mPinnedRefCount == 0) { + unpinPixelsLocked(); + disposeSelf = shouldDisposeSelfLocked(); + } + } + } + if (disposeSelf) { + delete this; + } +} + +void Bitmap::pinPixelsLocked() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + LOG_ALWAYS_FATAL("Cannot pin invalid pixels!"); + break; + case PixelStorageType::External: + // Nothing to do + break; + case PixelStorageType::Java: { + JNIEnv* env = jniEnv(); + if (!mPixelStorage.java.jstrongRef) { + mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>( + env->NewGlobalRef(mPixelStorage.java.jweakRef)); + if (!mPixelStorage.java.jstrongRef) { + LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels"); + } + } + break; + } + } +} + +void Bitmap::unpinPixelsLocked() { + switch (mPixelStorageType) { + case PixelStorageType::Invalid: + LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!"); + break; + case PixelStorageType::External: + // Don't need to do anything + break; + case PixelStorageType::Java: { + JNIEnv* env = jniEnv(); + if (mPixelStorage.java.jstrongRef) { + env->DeleteGlobalRef(mPixelStorage.java.jstrongRef); + mPixelStorage.java.jstrongRef = nullptr; + } + break; + } + } +} + +void Bitmap::getSkBitmap(SkBitmap* outBitmap) { + assertValid(); + android::AutoMutex _lock(mLock); + mPixelRef->ref(); + if (mPixelRef->unique()) { + // We just restored this from 0, pin the pixels and inc the strong count + // Note that there *might be* an incoming onStrongRefDestroyed from whatever + // last unref'd + pinPixelsLocked(); + mPinnedRefCount++; + } + // Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes() + // would require locking the pixels first. + outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes()); + outBitmap->setPixelRef(mPixelRef.get())->unref(); + outBitmap->setHasHardwareMipMap(hasHardwareMipMap()); +} + +void Bitmap::assertValid() const { + LOG_ALWAYS_FATAL_IF(mPixelStorageType == PixelStorageType::Invalid, + "Error, cannot access an invalid/free'd bitmap here!"); +} + +} // namespace android + +using namespace android; + +// Convenience class that does not take a global ref on the pixels, relying +// on the caller already having a local JNI ref +class LocalScopedBitmap { +public: + LocalScopedBitmap(jlong bitmapHandle) + : mBitmap(reinterpret_cast<Bitmap*>(bitmapHandle)) {} + + Bitmap* operator->() { + return mBitmap; + } + + void* pixels() { + return mBitmap->pixelRef()->pixels(); + } + + bool valid() { + return mBitmap && mBitmap->valid(); + } + +private: + Bitmap* mBitmap; +}; + /////////////////////////////////////////////////////////////////////////////// // Conversions to/from SkColor, for get/setPixels, and the create method, which // is basically like setPixels @@ -328,8 +643,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType)); - jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); - if (NULL == buff) { + Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); + if (!nativeBitmap) { return NULL; } @@ -338,39 +653,41 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 0, 0, width, height, bitmap); } - return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, - getPremulBitmapCreateFlags(isMutable), NULL, NULL); + return GraphicsJNI::createBitmap(env, nativeBitmap, + getPremulBitmapCreateFlags(isMutable)); } static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle, jboolean isMutable) { - const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + SkBitmap src; + reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle); SkBitmap result; JavaPixelAllocator allocator(env); - if (!src->copyTo(&result, dstCT, &allocator)) { + if (!src.copyTo(&result, dstCT, &allocator)) { return NULL; } - return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), - getPremulBitmapCreateFlags(isMutable), NULL, NULL); + Bitmap* bitmap = allocator.getStorageObjAndReset(); + return GraphicsJNI::createBitmap(env, bitmap, + getPremulBitmapCreateFlags(isMutable)); } static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - delete bitmap; + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->detachFromJava(); } static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->setPixels(NULL, NULL); + LocalScopedBitmap bitmap(bitmapHandle); + bitmap->freePixels(); return JNI_TRUE; } static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint width, jint height, jint configHandle, jint allocSize, jboolean requestPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); // ARGB_4444 is a deprecated format, convert automatically to 8888 @@ -383,11 +700,9 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, doThrowIAE(env, "Bitmap not large enough to support new configuration"); return; } - SkPixelRef* ref = bitmap->pixelRef(); - ref->ref(); SkAlphaType alphaType; - if (bitmap->colorType() != kRGB_565_SkColorType - && bitmap->alphaType() == kOpaque_SkAlphaType) { + if (bitmap->info().colorType() != kRGB_565_SkColorType + && bitmap->info().alphaType() == kOpaque_SkAlphaType) { // If the original bitmap was set to opaque, keep that setting, unless it // was 565, which is required to be opaque. alphaType = kOpaque_SkAlphaType; @@ -395,22 +710,7 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, // Otherwise respect the premultiplied request. alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; } - bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); - // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for - // its alphatype), so it would make more sense from Skia's perspective to create a - // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key - // for its cache, so it won't realize this is the same Java Bitmap. - SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); - // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. - // (e.g. 565 non-opaque) - info = bitmap->info(); - bitmap->setPixelRef(ref); - - // notifyPixelsChanged will increment the generation ID even though the actual pixel data - // hasn't been touched. This signals the renderer that the bitmap (including width, height, - // colortype and alphatype) has changed. - ref->notifyPixelsChanged(); - ref->unref(); + bitmap->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType)); } // These must match the int values in Bitmap.java @@ -423,7 +723,8 @@ enum JavaEncodeFormat { static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint format, jint quality, jobject jstream, jbyteArray jstorage) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + + LocalScopedBitmap bitmap(bitmapHandle); SkImageEncoder::Type fm; switch (format) { @@ -440,92 +741,92 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, return JNI_FALSE; } - bool success = false; - if (NULL != bitmap) { - SkAutoLockPixels alp(*bitmap); + if (!bitmap.valid()) { + return JNI_FALSE; + } - if (NULL == bitmap->getPixels()) { - return JNI_FALSE; - } + bool success = false; - SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - if (NULL == strm) { - return JNI_FALSE; - } + std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage)); + if (!strm.get()) { + return JNI_FALSE; + } - SkImageEncoder* encoder = SkImageEncoder::Create(fm); - if (NULL != encoder) { - success = encoder->encodeStream(strm, *bitmap, quality); - delete encoder; - } - delete strm; + std::unique_ptr<SkImageEncoder> encoder(SkImageEncoder::Create(fm)); + if (encoder.get()) { + SkBitmap skbitmap; + bitmap->getSkBitmap(&skbitmap); + success = encoder->encodeStream(strm.get(), skbitmap, quality); } return success ? JNI_TRUE : JNI_FALSE; } static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->eraseColor(color); + LocalScopedBitmap bitmap(bitmapHandle); + SkBitmap skBitmap; + bitmap->getSkBitmap(&skBitmap); + skBitmap.eraseColor(color); } static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); return static_cast<jint>(bitmap->rowBytes()); } static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->colorType()); + LocalScopedBitmap bitmap(bitmapHandle); + return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType()); } static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return static_cast<jint>(bitmap->getGenerationID()); + LocalScopedBitmap bitmap(bitmapHandle); + return static_cast<jint>(bitmap->pixelRef()->getGenerationID()); } static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - if (bitmap->alphaType() == kPremul_SkAlphaType) { + LocalScopedBitmap bitmap(bitmapHandle); + if (bitmap->info().alphaType() == kPremul_SkAlphaType) { return JNI_TRUE; } return JNI_FALSE; } static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE; + LocalScopedBitmap bitmap(bitmapHandle); + return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE; } static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasAlpha, jboolean requestPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); if (hasAlpha) { - bitmap->setAlphaType(requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType( + requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); } else { - bitmap->setAlphaType(kOpaque_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType); } } static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isPremul) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - if (!bitmap->isOpaque()) { + LocalScopedBitmap bitmap(bitmapHandle); + if (!bitmap->info().isOpaque()) { if (isPremul) { - bitmap->setAlphaType(kPremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType); } else { - bitmap->setAlphaType(kUnpremul_SkAlphaType); + bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType); } } } static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE; } static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasMipMap) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + LocalScopedBitmap bitmap(bitmapHandle); bitmap->setHasHardwareMipMap(hasMipMap); } @@ -580,8 +881,8 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { } } - jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable); - if (NULL == buffer) { + android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable); + if (!nativeBitmap) { SkSafeUnref(ctable); return NULL; } @@ -593,6 +894,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { android::Parcel::ReadableBlob blob; android::status_t status = p->readBlob(size, &blob); if (status) { + nativeBitmap->detachFromJava(); doThrowRE(env, "Could not read bitmap from parcel blob."); return NULL; } @@ -603,7 +905,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { blob.release(); - return GraphicsJNI::createBitmap(env, bitmap.release(), buffer, + return GraphicsJNI::createBitmap(env, nativeBitmap, getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); } @@ -611,24 +913,25 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isMutable, jint density, jobject parcel) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); if (parcel == NULL) { SkDebugf("------- writeToParcel null parcel\n"); return JNI_FALSE; } android::Parcel* p = android::parcelForJavaObject(env, parcel); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); p->writeInt32(isMutable); - p->writeInt32(bitmap->colorType()); - p->writeInt32(bitmap->alphaType()); - p->writeInt32(bitmap->width()); - p->writeInt32(bitmap->height()); - p->writeInt32(bitmap->rowBytes()); + p->writeInt32(bitmap.colorType()); + p->writeInt32(bitmap.alphaType()); + p->writeInt32(bitmap.width()); + p->writeInt32(bitmap.height()); + p->writeInt32(bitmap.rowBytes()); p->writeInt32(density); - if (bitmap->colorType() == kIndex_8_SkColorType) { - SkColorTable* ctable = bitmap->getColorTable(); + if (bitmap.colorType() == kIndex_8_SkColorType) { + SkColorTable* ctable = bitmap.getColorTable(); if (ctable != NULL) { int count = ctable->count(); p->writeInt32(count); @@ -639,7 +942,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, } } - size_t size = bitmap->getSize(); + size_t size = bitmap.getSize(); android::Parcel::WritableBlob blob; android::status_t status = p->writeBlob(size, &blob); @@ -648,14 +951,14 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, return JNI_FALSE; } - bitmap->lockPixels(); - const void* pSrc = bitmap->getPixels(); + bitmap.lockPixels(); + const void* pSrc = bitmap.getPixels(); if (pSrc == NULL) { memset(blob.data(), 0, size); } else { memcpy(blob.data(), pSrc, size); } - bitmap->unlockPixels(); + bitmap.unlockPixels(); blob.release(); return JNI_TRUE; @@ -664,17 +967,17 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, jlong srcHandle, jlong paintHandle, jintArray offsetXY) { - const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle); + SkBitmap src; + reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src); const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle); SkIPoint offset; - SkBitmap* dst = new SkBitmap; + SkBitmap dst; JavaPixelAllocator allocator(env); - src->extractAlpha(dst, paint, &allocator, &offset); + src.extractAlpha(&dst, paint, &allocator, &offset); // If Skia can't allocate pixels for destination bitmap, it resets // it, that is set its pixels buffer to NULL, and zero width and height. - if (dst->getPixels() == NULL && src->getPixels() != NULL) { - delete dst; + if (dst.getPixels() == NULL && src.getPixels() != NULL) { doThrowOOME(env, "failed to allocate pixels for alpha"); return NULL; } @@ -685,53 +988,55 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(offsetXY, array, 0); } - return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), - getPremulBitmapCreateFlags(true), NULL, NULL); + return GraphicsJNI::createBitmap(env, allocator.getStorageObjAndReset(), + getPremulBitmapCreateFlags(true)); } /////////////////////////////////////////////////////////////////////////////// static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle, jint x, jint y) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); - ToColorProc proc = ChooseToColorProc(*bitmap); + ToColorProc proc = ChooseToColorProc(bitmap); if (NULL == proc) { return 0; } - const void* src = bitmap->getAddr(x, y); + const void* src = bitmap.getAddr(x, y); if (NULL == src) { return 0; } SkColor dst[1]; - proc(dst, src, 1, bitmap->getColorTable()); + proc(dst, src, 1, bitmap.getColorTable()); return static_cast<jint>(dst[0]); } static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, jintArray pixelArray, jint offset, jint stride, jint x, jint y, jint width, jint height) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); - ToColorProc proc = ChooseToColorProc(*bitmap); + ToColorProc proc = ChooseToColorProc(bitmap); if (NULL == proc) { return; } - const void* src = bitmap->getAddr(x, y); + const void* src = bitmap.getAddr(x, y); if (NULL == src) { return; } - SkColorTable* ctable = bitmap->getColorTable(); + SkColorTable* ctable = bitmap.getColorTable(); jint* dst = env->GetIntArrayElements(pixelArray, NULL); SkColor* d = (SkColor*)dst + offset; while (--height >= 0) { proc(d, src, width, ctable); d += stride; - src = (void*)((const char*)src + bitmap->rowBytes()); + src = (void*)((const char*)src + bitmap.rowBytes()); } env->ReleaseIntArrayElements(pixelArray, dst, 0); } @@ -740,79 +1045,85 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle, static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle, jint x, jint y, jint colorHandle) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); SkColor color = static_cast<SkColor>(colorHandle); - SkAutoLockPixels alp(*bitmap); - if (NULL == bitmap->getPixels()) { + SkAutoLockPixels alp(bitmap); + if (NULL == bitmap.getPixels()) { return; } - FromColorProc proc = ChooseFromColorProc(*bitmap); + FromColorProc proc = ChooseFromColorProc(bitmap); if (NULL == proc) { return; } - proc(bitmap->getAddr(x, y), &color, 1, x, y); - bitmap->notifyPixelsChanged(); + proc(bitmap.getAddr(x, y), &color, 1, x, y); + bitmap.notifyPixelsChanged(); } static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle, jintArray pixelArray, jint offset, jint stride, jint x, jint y, jint width, jint height) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); GraphicsJNI::SetPixels(env, pixelArray, offset, stride, - x, y, width, height, *bitmap); + x, y, width, height, bitmap); } static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, jlong bitmapHandle, jobject jbuffer) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); - const void* src = bitmap->getPixels(); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); + const void* src = bitmap.getPixels(); if (NULL != src) { android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); // the java side has already checked that buffer is large enough - memcpy(abp.pointer(), src, bitmap->getSize()); + memcpy(abp.pointer(), src, bitmap.getSize()); } } static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, jlong bitmapHandle, jobject jbuffer) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkAutoLockPixels alp(*bitmap); - void* dst = bitmap->getPixels(); + SkBitmap bitmap; + reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap); + SkAutoLockPixels alp(bitmap); + void* dst = bitmap.getPixels(); if (NULL != dst) { android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); // the java side has already checked that buffer is large enough - memcpy(dst, abp.pointer(), bitmap->getSize()); - bitmap->notifyPixelsChanged(); + memcpy(dst, abp.pointer(), bitmap.getSize()); + bitmap.notifyPixelsChanged(); } } static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) { - const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle); - const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle); - if (bm0->width() != bm1->width() || - bm0->height() != bm1->height() || - bm0->colorType() != bm1->colorType()) { + SkBitmap bm0; + SkBitmap bm1; + reinterpret_cast<Bitmap*>(bm0Handle)->getSkBitmap(&bm0); + reinterpret_cast<Bitmap*>(bm1Handle)->getSkBitmap(&bm1); + if (bm0.width() != bm1.width() || + bm0.height() != bm1.height() || + bm0.colorType() != bm1.colorType()) { return JNI_FALSE; } - SkAutoLockPixels alp0(*bm0); - SkAutoLockPixels alp1(*bm1); + SkAutoLockPixels alp0(bm0); + SkAutoLockPixels alp1(bm1); // if we can't load the pixels, return false - if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { + if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) { return JNI_FALSE; } - if (bm0->colorType() == kIndex_8_SkColorType) { - SkColorTable* ct0 = bm0->getColorTable(); - SkColorTable* ct1 = bm1->getColorTable(); + if (bm0.colorType() == kIndex_8_SkColorType) { + SkColorTable* ct0 = bm0.getColorTable(); + SkColorTable* ct1 = bm1.getColorTable(); if (NULL == ct0 || NULL == ct1) { return JNI_FALSE; } @@ -829,16 +1140,16 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, // now compare each scanline. We can't do the entire buffer at once, // since we don't care about the pixel values that might extend beyond // the width (since the scanline might be larger than the logical width) - const int h = bm0->height(); - const size_t size = bm0->width() * bm0->bytesPerPixel(); + const int h = bm0.height(); + const size_t size = bm0.width() * bm0.bytesPerPixel(); for (int y = 0; y < h; y++) { // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 // and bm1 both have pixel data() (have passed NULL == getPixels() check), // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE // to warn user those 2 unrecognized config bitmaps may be different. - void *bm0Addr = bm0->getAddr(0, y); - void *bm1Addr = bm1->getAddr(0, y); + void *bm0Addr = bm0.getAddr(0, y); + void *bm1Addr = bm1.getAddr(0, y); if(bm0Addr == NULL || bm1Addr == NULL) { return JNI_FALSE; @@ -851,15 +1162,9 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, return JNI_TRUE; } -static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - bitmap->lockPixels(); - bitmap->unlockPixels(); -} - static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkPixelRef* pixelRef = bitmap ? bitmap->pixelRef() : nullptr; + LocalScopedBitmap bitmap(bitmapHandle); + SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr; SkSafeRef(pixelRef); return reinterpret_cast<jlong>(pixelRef); } @@ -902,7 +1207,6 @@ static JNINativeMethod gBitmapMethods[] = { { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V", (void*)Bitmap_copyPixelsFromBuffer }, { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs }, - { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw }, { "nativeRefPixelRef", "(J)J", (void*)Bitmap_refPixelRef }, }; diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h new file mode 100644 index 0000000..d6e5c61 --- /dev/null +++ b/core/jni/android/graphics/Bitmap.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef BITMAP_H_ +#define BITMAP_H_ + +#include <jni.h> +#include <SkBitmap.h> +#include <SkColorTable.h> +#include <SkImageInfo.h> +#include <utils/Mutex.h> +#include <memory> + +namespace android { + +enum class PixelStorageType { + Invalid, + External, + Java, +}; + +class WrappedPixelRef; + +typedef void (*FreeFunc)(void* addr, void* context); + +/** + * Glue-thingy that deals with managing the interaction between the Java + * Bitmap object & SkBitmap along with trying to map a notion of strong/weak + * lifecycles onto SkPixelRef which only has strong counts to avoid requiring + * two GC passes to free the byte[] that backs a Bitmap. + * + * Since not all Bitmaps are byte[]-backed it also supports external allocations, + * which currently is used by screenshots to wrap a gralloc buffer. + */ +class Bitmap { +public: + Bitmap(JNIEnv* env, jbyteArray storageObj, void* address, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + Bitmap(void* address, void* context, FreeFunc freeFunc, + const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + + const SkImageInfo& info() const; + + // Returns nullptr if it is not backed by a jbyteArray + jbyteArray javaByteArray() const { + return mPixelStorageType == PixelStorageType::Java + ? mPixelStorage.java.jstrongRef : nullptr; + } + + int width() const { return info().width(); } + int height() const { return info().height(); } + size_t rowBytes() const; + SkPixelRef* pixelRef() const; + bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; } + + void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); + void reconfigure(const SkImageInfo& info); + + void getSkBitmap(SkBitmap* outBitmap); + void detachFromJava(); + + void freePixels(); + + bool hasHardwareMipMap(); + void setHasHardwareMipMap(bool hasMipMap); + +private: + friend class WrappedPixelRef; + + ~Bitmap(); + void doFreePixels(); + void onStrongRefDestroyed(); + + void pinPixelsLocked(); + void unpinPixelsLocked(); + JNIEnv* jniEnv(); + bool shouldDisposeSelfLocked(); + void assertValid() const; + + android::Mutex mLock; + int mPinnedRefCount = 0; + std::unique_ptr<WrappedPixelRef> mPixelRef; + PixelStorageType mPixelStorageType; + bool mAttachedToJava = true; + + union { + struct { + void* address; + void* context; + FreeFunc freeFunc; + } external; + struct { + JavaVM* jvm; + jweak jweakRef; + jbyteArray jstrongRef; + } java; + } mPixelStorage; +}; + +} // namespace android + +#endif /* BITMAP_H_ */ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index d4069a1..cdd397d 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -156,13 +156,11 @@ private: class RecyclingPixelAllocator : public SkBitmap::Allocator { public: - RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size) - : mPixelRef(pixelRef), mSize(size) { - SkSafeRef(mPixelRef); + RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size) + : mBitmap(bitmap), mSize(size) { } ~RecyclingPixelAllocator() { - SkSafeUnref(mPixelRef); } virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { @@ -185,11 +183,9 @@ public: return false; } - // Create a new pixelref with the new ctable that wraps the previous pixelref - SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef), - info, bitmap->rowBytes(), ctable); + mBitmap->reconfigure(info, bitmap->rowBytes(), ctable); + bitmap->setPixelRef(mBitmap->pixelRef()); - bitmap->setPixelRef(pr)->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator/JavaPixelAllocator behaves this way too bitmap->lockPixels(); @@ -197,7 +193,7 @@ public: } private: - SkPixelRef* const mPixelRef; + android::Bitmap* const mBitmap; const unsigned int mSize; }; @@ -258,27 +254,24 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); - SkBitmap* outputBitmap = NULL; + android::Bitmap* reuseBitmap = nullptr; unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { - outputBitmap = GraphicsJNI::getSkBitmapDeprecated(env, javaBitmap); - if (outputBitmap->isImmutable()) { + reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap); + if (reuseBitmap->pixelRef()->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); javaBitmap = NULL; - outputBitmap = NULL; + reuseBitmap = nullptr; } else { existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); } } - SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL); - if (outputBitmap == NULL) outputBitmap = adb.get(); - NinePatchPeeker peeker(decoder); decoder->setPeeker(&peeker); JavaPixelAllocator javaAllocator(env); - RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); + RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize); ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ? (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator; @@ -374,6 +367,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } } + SkBitmap outputBitmap; if (willScale) { // This is weird so let me explain: we could use the scale parameter // directly, but for historical reasons this is how the corresponding @@ -388,26 +382,27 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // FIXME: If the alphaType is kUnpremul and the image has alpha, the // colors may not be correct, since Skia does not yet support drawing // to/from unpremultiplied bitmaps. - outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, + outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); - if (!outputBitmap->tryAllocPixels(outputAllocator, NULL)) { + if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap's pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { - outputBitmap->eraseColor(0); + outputBitmap.eraseColor(0); } SkPaint paint; paint.setFilterQuality(kLow_SkFilterQuality); - SkCanvas canvas(*outputBitmap); + SkCanvas canvas(outputBitmap); canvas.scale(sx, sy); + canvas.drawARGB(0x00, 0x00, 0x00, 0x00); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); } else { - outputBitmap->swap(decodingBitmap); + outputBitmap.swap(decodingBitmap); } if (padding) { @@ -422,22 +417,19 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. - if (outputBitmap->pixelRef() == NULL) { + if (outputBitmap.pixelRef() == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) - outputBitmap->setImmutable(); + outputBitmap.setImmutable(); } - // detach bitmap from its autodeleter, since we want to own it now - adb.detach(); - if (javaBitmap != NULL) { bool isPremultiplied = !requireUnpremultiplied; - GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied); - outputBitmap->notifyPixelsChanged(); + GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied); + outputBitmap.notifyPixelsChanged(); // If a java bitmap was passed in for reuse, pass it back return javaBitmap; } @@ -447,7 +439,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; // now create the java bitmap - return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(), + return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); } diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index aeea808..08a3f6f 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -212,26 +212,21 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, region.fTop = start_y; region.fRight = start_x + width; region.fBottom = start_y + height; - SkBitmap* bitmap = NULL; - SkAutoTDelete<SkBitmap> adb; + SkBitmap bitmap; if (tileBitmap != NULL) { // Re-use bitmap. - bitmap = GraphicsJNI::getSkBitmapDeprecated(env, tileBitmap); - } - if (bitmap == NULL) { - bitmap = new SkBitmap; - adb.reset(bitmap); + GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap); } - if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) { + if (!brd->decodeRegion(&bitmap, region, prefColorType, sampleSize)) { return nullObjectReturn("decoder->decodeRegion returned false"); } // update options (if any) if (NULL != options) { - env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); - env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); + env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); + env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); // TODO: set the mimeType field with the data from the codec. // but how to reuse a set of strings, rather than allocating new one // each time? @@ -240,19 +235,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, } if (tileBitmap != NULL) { - bitmap->notifyPixelsChanged(); + bitmap.notifyPixelsChanged(); return tileBitmap; } - // detach bitmap from its autodeleter, since we want to own it now - adb.detach(); - JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); - jbyteArray buff = allocator->getStorageObjAndReset(); int bitmapCreateFlags = 0; if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; - return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1); + return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(), + bitmapCreateFlags); } static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index f793df1..0deb8cc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -154,7 +154,7 @@ static jfieldID gPointF_xFieldID; static jfieldID gPointF_yFieldID; static jclass gBitmap_class; -static jfieldID gBitmap_skBitmapPtr; +static jfieldID gBitmap_nativePtr; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_reinitMethodID; static jmethodID gBitmap_getAllocationByteCountMethodID; @@ -338,27 +338,22 @@ SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) { return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]); } -SkBitmap* GraphicsJNI::getSkBitmapDeprecated(JNIEnv* env, jobject bitmap) { +android::Bitmap* GraphicsJNI::getBitmap(JNIEnv* env, jobject bitmap) { SkASSERT(env); SkASSERT(bitmap); SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr); - SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle); + jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + android::Bitmap* b = reinterpret_cast<android::Bitmap*>(bitmapHandle); SkASSERT(b); return b; } void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) { - // TODO: We have to copy from the existing bitmap due to rowBytes not - // being updated on the SkPixelRef at reconfigure time. This is a short term - // problem that will be fixed with the specialized wrapper - *outBitmap = *getSkBitmapDeprecated(env, bitmap); + getBitmap(env, bitmap)->getSkBitmap(outBitmap); } SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) { - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr); - SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle); - return b->pixelRef(); + return getBitmap(env, bitmap)->pixelRef(); } SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) { @@ -396,47 +391,43 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// // Assert that bitmap's SkAlphaType is consistent with isPremultiplied. -static void assert_premultiplied(const SkBitmap& bitmap, bool isPremultiplied) { +static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) { // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is // irrelevant. This just tests to ensure that the SkAlphaType is not // opposite of isPremultiplied. if (isPremultiplied) { - SkASSERT(bitmap.alphaType() != kUnpremul_SkAlphaType); + SkASSERT(info.alphaType() != kUnpremul_SkAlphaType); } else { - SkASSERT(bitmap.alphaType() != kPremul_SkAlphaType); + SkASSERT(info.alphaType() != kPremul_SkAlphaType); } } -jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density) -{ - SkASSERT(bitmap); - SkASSERT(bitmap->pixelRef()); - SkASSERT(!env->ExceptionCheck()); +jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, + int density) { bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; - // The caller needs to have already set the alpha type properly, so the // native SkBitmap stays in sync with the Java Bitmap. - assert_premultiplied(*bitmap, isPremultiplied); + assert_premultiplied(bitmap->info(), isPremultiplied); jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID, - reinterpret_cast<jlong>(bitmap), buffer, + reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(), bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets); hasException(env); // For the side effect of logging. return obj; } -void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, +void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied) { // The caller needs to have already set the alpha type properly, so the // native SkBitmap stays in sync with the Java Bitmap. - assert_premultiplied(*bitmap, isPremultiplied); + assert_premultiplied(info, isPremultiplied); env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID, - bitmap->width(), bitmap->height(), isPremultiplied); + info.width(), info.height(), isPremultiplied); } int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) @@ -477,51 +468,6 @@ static JNIEnv* vm2env(JavaVM* vm) /////////////////////////////////////////////////////////////////////////////// -AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, - size_t rowBytes, jbyteArray storageObj, SkColorTable* ctable) : - SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)), - fWrappedPixelRef(NULL) { - SkASSERT(storage); - SkASSERT(storageObj); - SkASSERT(env); - - if (env->GetJavaVM(&fVM) != JNI_OK) { - SkDebugf("------ [%p] env->GetJavaVM failed\n", env); - sk_throw(); - } - - fStorageObj = (jbyteArray) env->NewGlobalRef(storageObj); -} - -AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, - size_t rowBytes, SkColorTable* ctable) : - SkMallocPixelRef(info, wrappedPixelRef.getAddr(), rowBytes, ctable, false), - fWrappedPixelRef(wrappedPixelRef.fWrappedPixelRef ? - wrappedPixelRef.fWrappedPixelRef : &wrappedPixelRef) -{ - SkASSERT(fWrappedPixelRef); - SkSafeRef(fWrappedPixelRef); - - // don't need to initialize this, as all the relevant logic delegates to the wrapped ref - fStorageObj = NULL; -} - -AndroidPixelRef::~AndroidPixelRef() { - if (fWrappedPixelRef) { - SkSafeUnref(fWrappedPixelRef); - } else { - SkASSERT(fStorageObj); - JNIEnv* env = vm2env(fVM); - env->DeleteGlobalRef(fStorageObj); - } - - if (android::uirenderer::Caches::hasInstance()) { - android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) { int32_t rowBytes32 = SkToS32(bitmap.rowBytes()); int64_t bigSize = (int64_t)bitmap.height() * rowBytes32; @@ -533,7 +479,7 @@ static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) { return true; } -jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, +android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) { const SkImageInfo& info = bitmap->info(); if (info.fColorType == kUnknown_SkColorType) { @@ -562,13 +508,14 @@ jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, return NULL; } SkASSERT(addr); - SkPixelRef* pr = new AndroidPixelRef(env, info, (void*) addr, rowBytes, arrayObj, ctable); - bitmap->setPixelRef(pr)->unref(); + android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr, + info, rowBytes, ctable); + wrapper->getSkBitmap(bitmap); // since we're already allocated, we lockPixels right away // HeapAllocator behaves this way too bitmap->lockPixels(); - return arrayObj; + return wrapper; } struct AndroidPixelRefContext { @@ -627,21 +574,22 @@ bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ct /////////////////////////////////////////////////////////////////////////////// -JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) - : fStorageObj(NULL), - fAllocCount(0) { - if (env->GetJavaVM(&fVM) != JNI_OK) { - SkDebugf("------ [%p] env->GetJavaVM failed\n", env); - sk_throw(); +JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK, + "env->GetJavaVM failed"); +} + +JavaPixelAllocator::~JavaPixelAllocator() { + if (mStorage) { + mStorage->detachFromJava(); } } bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { - JNIEnv* env = vm2env(fVM); + JNIEnv* env = vm2env(mJavaVM); - fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); - fAllocCount += 1; - return fStorageObj != NULL; + mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable); + return mStorage != nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -687,7 +635,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); - gBitmap_skBitmapPtr = getFieldIDCheck(env, gBitmap_class, "mSkBitmapPtr", "J"); + gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V"); gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V"); gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 8eb43f8..e748bac 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -1,6 +1,7 @@ #ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ +#include "Bitmap.h" #include "SkBitmap.h" #include "SkDevice.h" #include "SkPixelRef.h" @@ -49,7 +50,7 @@ public: static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); - static SkBitmap* getSkBitmapDeprecated(JNIEnv*, jobject bitmap); + static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap); static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap); static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap); static SkRegion* getNativeRegion(JNIEnv*, jobject region); @@ -71,22 +72,18 @@ public: */ static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig); - /** Create a java Bitmap object given the native bitmap (required) and optional - storage array (may be null). - bitmap's SkAlphaType must already be in sync with bitmapCreateFlags. + /* + * Create a java Bitmap object given the native bitmap + * bitmap's SkAlphaType must already be in sync with bitmapCreateFlags. */ - static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer, - int bitmapCreateFlags, jbyteArray ninePatch, jobject ninePatchInsets, int density = -1); - - static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags, - jbyteArray ninePatch, int density = -1) { - return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninePatch, NULL, density); - } + static jobject createBitmap(JNIEnv* env, android::Bitmap* bitmap, + int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL, + jobject ninePatchInsets = NULL, int density = -1); /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in sync with isPremultiplied */ - static void reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap, + static void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied); static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap); @@ -95,7 +92,7 @@ public: static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); - static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, + static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable); /** @@ -113,30 +110,6 @@ public: static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset, int srcStride, int x, int y, int width, int height, const SkBitmap& dstBitmap); - - static jbyteArray getBitmapStorageObj(SkPixelRef *pixref); -}; - -class AndroidPixelRef : public SkMallocPixelRef { -public: - AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, size_t rowBytes, - jbyteArray storageObj, SkColorTable* ctable); - - /** - * Creates an AndroidPixelRef that wraps (and refs) another to reuse/share - * the same storage and java byte array refcounting, yet have a different - * color table. - */ - AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info, - size_t rowBytes, SkColorTable* ctable); - - virtual ~AndroidPixelRef(); - -private: - AndroidPixelRef* const fWrappedPixelRef; // if set, delegate memory management calls to this - - JavaVM* fVM; - jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store }; /** Allocator which allocates the backing buffer in the Java heap. @@ -147,30 +120,22 @@ private: class JavaPixelAllocator : public SkBitmap::Allocator { public: JavaPixelAllocator(JNIEnv* env); - // overrides - virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable); + ~JavaPixelAllocator(); - /** Return the Java array object created for the last allocation. - * This returns a local JNI reference which the caller is responsible - * for storing appropriately (usually by passing it to the Bitmap - * constructor). - */ - jbyteArray getStorageObj() { return fStorageObj; } + virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override; - /** Same as getStorageObj(), but also resets the allocator so that it - * can allocate again. + /** + * Fetches the backing allocation object. Must be called! */ - jbyteArray getStorageObjAndReset() { - jbyteArray result = fStorageObj; - fStorageObj = NULL; - fAllocCount = 0; + android::Bitmap* getStorageObjAndReset() { + android::Bitmap* result = mStorage; + mStorage = NULL; return result; }; private: - JavaVM* fVM; - jbyteArray fStorageObj; - int fAllocCount; + JavaVM* mJavaVM; + android::Bitmap* mStorage = nullptr; }; enum JNIAccess { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a8355c2..1965cd3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -23,6 +23,7 @@ #include "android_os_Parcel.h" #include "android_util_Binder.h" +#include "android/graphics/Bitmap.h" #include "android/graphics/GraphicsJNI.h" #include "android/graphics/Region.h" @@ -168,22 +169,19 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, } } - const ssize_t rowBytes = + const size_t rowBytes = screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat()); - SkBitmap* bitmap = new SkBitmap(); - bitmap->setInfo(screenshotInfo, (size_t)rowBytes); - if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) { - // takes ownership of ScreenshotClient - SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo, - (size_t) rowBytes, NULL, (void*) screenshot->getPixels(), &DeleteScreenshot, - (void*) (screenshot.get())); - screenshot.detach(); - pixels->setImmutable(); - bitmap->setPixelRef(pixels)->unref(); - bitmap->lockPixels(); + if (!screenshotInfo.fWidth || !screenshotInfo.fHeight) { + return NULL; } + Bitmap* bitmap = new Bitmap( + (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot, + screenshotInfo, rowBytes, nullptr); + screenshot.detach(); + bitmap->pixelRef()->setImmutable(); + return GraphicsJNI::createBitmap(env, bitmap, GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL); } diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index be5c52b..c850b07 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -41,13 +41,13 @@ public final class Bitmap implements Parcelable { */ public static final int DENSITY_NONE = 0; - private final long mSkBitmapPtr; - /** * Backing buffer for the Bitmap. */ private byte[] mBuffer; + // Convenience for JNI access + private final long mNativePtr; private final BitmapFinalizer mFinalizer; private final boolean mIsMutable; @@ -115,17 +115,16 @@ public final class Bitmap implements Parcelable { mRequestPremultiplied = requestPremultiplied; mBuffer = buffer; - // we delete this in our finalizer - mSkBitmapPtr = nativeBitmap; - mNinePatchChunk = ninePatchChunk; mNinePatchInsets = ninePatchInsets; if (density >= 0) { mDensity = density; } - int nativeAllocationByteCount = buffer == null ? getByteCount() : 0; - mFinalizer = new BitmapFinalizer(nativeBitmap, nativeAllocationByteCount); + mNativePtr = nativeBitmap; + mFinalizer = new BitmapFinalizer(nativeBitmap); + int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0); + mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount); } /** @@ -223,8 +222,8 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } - nativeReconfigure(mSkBitmapPtr, width, height, config.nativeInt, mBuffer.length, - mRequestPremultiplied); + nativeReconfigure(mFinalizer.mNativeBitmap, width, height, config.nativeInt, + mBuffer.length, mRequestPremultiplied); mWidth = width; mHeight = height; } @@ -301,7 +300,7 @@ public final class Bitmap implements Parcelable { */ public void recycle() { if (!mRecycled && mFinalizer.mNativeBitmap != 0) { - if (nativeRecycle(mSkBitmapPtr)) { + if (nativeRecycle(mFinalizer.mNativeBitmap)) { // return value indicates whether native pixel object was actually recycled. // false indicates that it is still in use at the native level and these // objects should not be collected now. They will be collected later when the @@ -331,7 +330,7 @@ public final class Bitmap implements Parcelable { * @return The current generation ID for this bitmap. */ public int getGenerationId() { - return nativeGenerationId(mSkBitmapPtr); + return nativeGenerationId(mFinalizer.mNativeBitmap); } /** @@ -487,7 +486,7 @@ public final class Bitmap implements Parcelable { throw new RuntimeException("Buffer not large enough for pixels"); } - nativeCopyPixelsToBuffer(mSkBitmapPtr, dst); + nativeCopyPixelsToBuffer(mFinalizer.mNativeBitmap, dst); // now update the buffer's position int position = dst.position(); @@ -527,7 +526,7 @@ public final class Bitmap implements Parcelable { throw new RuntimeException("Buffer not large enough for pixels"); } - nativeCopyPixelsFromBuffer(mSkBitmapPtr, src); + nativeCopyPixelsFromBuffer(mFinalizer.mNativeBitmap, src); // now update the buffer's position int position = src.position(); @@ -549,7 +548,7 @@ public final class Bitmap implements Parcelable { */ public Bitmap copy(Config config, boolean isMutable) { checkRecycled("Can't copy a recycled bitmap"); - Bitmap b = nativeCopy(mSkBitmapPtr, config.nativeInt, isMutable); + Bitmap b = nativeCopy(mFinalizer.mNativeBitmap, config.nativeInt, isMutable); if (b != null) { b.setPremultiplied(mRequestPremultiplied); b.mDensity = mDensity; @@ -810,7 +809,7 @@ public final class Bitmap implements Parcelable { } bm.setHasAlpha(hasAlpha); if (config == Config.ARGB_8888 && !hasAlpha) { - nativeErase(bm.mSkBitmapPtr, 0xff000000); + nativeErase(bm.mFinalizer.mNativeBitmap, 0xff000000); } // No need to initialize the bitmap to zeroes with other configs; // it is backed by a VM byte array which is by definition preinitialized @@ -1000,8 +999,8 @@ public final class Bitmap implements Parcelable { throw new IllegalArgumentException("quality must be 0..100"); } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress"); - boolean result = nativeCompress(mSkBitmapPtr, format.nativeInt, quality, - stream, new byte[WORKING_COMPRESS_STORAGE]); + boolean result = nativeCompress(mFinalizer.mNativeBitmap, format.nativeInt, + quality, stream, new byte[WORKING_COMPRESS_STORAGE]); Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); return result; } @@ -1041,7 +1040,7 @@ public final class Bitmap implements Parcelable { * @see BitmapFactory.Options#inPremultiplied */ public final boolean isPremultiplied() { - return nativeIsPremultiplied(mSkBitmapPtr); + return nativeIsPremultiplied(mFinalizer.mNativeBitmap); } /** @@ -1066,7 +1065,7 @@ public final class Bitmap implements Parcelable { */ public final void setPremultiplied(boolean premultiplied) { mRequestPremultiplied = premultiplied; - nativeSetPremultiplied(mSkBitmapPtr, premultiplied); + nativeSetPremultiplied(mFinalizer.mNativeBitmap, premultiplied); } /** Returns the bitmap's width */ @@ -1158,7 +1157,7 @@ public final class Bitmap implements Parcelable { * @return number of bytes between rows of the native bitmap pixels. */ public final int getRowBytes() { - return nativeRowBytes(mSkBitmapPtr); + return nativeRowBytes(mFinalizer.mNativeBitmap); } /** @@ -1201,7 +1200,7 @@ public final class Bitmap implements Parcelable { * that config, otherwise return null. */ public final Config getConfig() { - return Config.nativeToConfig(nativeConfig(mSkBitmapPtr)); + return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap)); } /** Returns true if the bitmap's config supports per-pixel alpha, and @@ -1213,7 +1212,7 @@ public final class Bitmap implements Parcelable { * it will return true by default. */ public final boolean hasAlpha() { - return nativeHasAlpha(mSkBitmapPtr); + return nativeHasAlpha(mFinalizer.mNativeBitmap); } /** @@ -1227,7 +1226,7 @@ public final class Bitmap implements Parcelable { * non-opaque per-pixel alpha values. */ public void setHasAlpha(boolean hasAlpha) { - nativeSetHasAlpha(mSkBitmapPtr, hasAlpha, mRequestPremultiplied); + nativeSetHasAlpha(mFinalizer.mNativeBitmap, hasAlpha, mRequestPremultiplied); } /** @@ -1248,7 +1247,7 @@ public final class Bitmap implements Parcelable { * @see #setHasMipMap(boolean) */ public final boolean hasMipMap() { - return nativeHasMipMap(mSkBitmapPtr); + return nativeHasMipMap(mFinalizer.mNativeBitmap); } /** @@ -1272,7 +1271,7 @@ public final class Bitmap implements Parcelable { * @see #hasMipMap() */ public final void setHasMipMap(boolean hasMipMap) { - nativeSetHasMipMap(mSkBitmapPtr, hasMipMap); + nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap); } /** @@ -1285,7 +1284,7 @@ public final class Bitmap implements Parcelable { if (!isMutable()) { throw new IllegalStateException("cannot erase immutable bitmaps"); } - nativeErase(mSkBitmapPtr, c); + nativeErase(mFinalizer.mNativeBitmap, c); } /** @@ -1302,7 +1301,7 @@ public final class Bitmap implements Parcelable { public int getPixel(int x, int y) { checkRecycled("Can't call getPixel() on a recycled bitmap"); checkPixelAccess(x, y); - return nativeGetPixel(mSkBitmapPtr, x, y); + return nativeGetPixel(mFinalizer.mNativeBitmap, x, y); } /** @@ -1335,7 +1334,7 @@ public final class Bitmap implements Parcelable { return; // nothing to do } checkPixelsAccess(x, y, width, height, offset, stride, pixels); - nativeGetPixels(mSkBitmapPtr, pixels, offset, stride, + nativeGetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride, x, y, width, height); } @@ -1416,7 +1415,7 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException(); } checkPixelAccess(x, y); - nativeSetPixel(mSkBitmapPtr, x, y, color); + nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color); } /** @@ -1452,7 +1451,7 @@ public final class Bitmap implements Parcelable { return; // nothing to do } checkPixelsAccess(x, y, width, height, offset, stride, pixels); - nativeSetPixels(mSkBitmapPtr, pixels, offset, stride, + nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride, x, y, width, height); } @@ -1490,7 +1489,7 @@ public final class Bitmap implements Parcelable { */ public void writeToParcel(Parcel p, int flags) { checkRecycled("Can't parcel a recycled bitmap"); - if (!nativeWriteToParcel(mSkBitmapPtr, mIsMutable, mDensity, p)) { + if (!nativeWriteToParcel(mFinalizer.mNativeBitmap, mIsMutable, mDensity, p)) { throw new RuntimeException("native writeToParcel failed"); } } @@ -1536,7 +1535,7 @@ public final class Bitmap implements Parcelable { public Bitmap extractAlpha(Paint paint, int[] offsetXY) { checkRecycled("Can't extractAlpha on a recycled bitmap"); long nativePaint = paint != null ? paint.getNativeInstance() : 0; - Bitmap bm = nativeExtractAlpha(mSkBitmapPtr, nativePaint, offsetXY); + Bitmap bm = nativeExtractAlpha(mFinalizer.mNativeBitmap, nativePaint, offsetXY); if (bm == null) { throw new RuntimeException("Failed to extractAlpha on Bitmap"); } @@ -1550,7 +1549,8 @@ public final class Bitmap implements Parcelable { * If other is null, return false. */ public boolean sameAs(Bitmap other) { - return this == other || (other != null && nativeSameAs(mSkBitmapPtr, other.mSkBitmapPtr)); + return this == other || (other != null + && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap)); } /** @@ -1565,7 +1565,9 @@ public final class Bitmap implements Parcelable { * and therefore is harmless. */ public void prepareToDraw() { - nativePrepareToDraw(mSkBitmapPtr); + // TODO: Consider having this start an async upload? + // With inPurgeable no-op'd there's currently no use for this + // method, but it could have interesting future uses. } /** @@ -1574,7 +1576,7 @@ public final class Bitmap implements Parcelable { * @hide * */ public final long refSkPixelRef() { - return nativeRefPixelRef(mSkBitmapPtr); + return nativeRefPixelRef(mNativePtr); } private static class BitmapFinalizer { @@ -1582,12 +1584,17 @@ public final class Bitmap implements Parcelable { // Native memory allocated for the duration of the Bitmap, // if pixel data allocated into native memory, instead of java byte[] - private final int mNativeAllocationByteCount; + private int mNativeAllocationByteCount; - BitmapFinalizer(long nativeBitmap, int nativeAllocationByteCount) { + BitmapFinalizer(long nativeBitmap) { mNativeBitmap = nativeBitmap; - mNativeAllocationByteCount = nativeAllocationByteCount; + } + public void setNativeAllocationByteCount(int nativeByteCount) { + if (mNativeAllocationByteCount != 0) { + VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount); + } + mNativeAllocationByteCount = nativeByteCount; if (mNativeAllocationByteCount != 0) { VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount); } @@ -1600,9 +1607,7 @@ public final class Bitmap implements Parcelable { } catch (Throwable t) { // Ignore } finally { - if (mNativeAllocationByteCount != 0) { - VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount); - } + setNativeAllocationByteCount(0); nativeDestructor(mNativeBitmap); mNativeBitmap = 0; } @@ -1654,7 +1659,6 @@ public final class Bitmap implements Parcelable { long nativePaint, int[] offsetXY); - private static native void nativePrepareToDraw(long nativeBitmap); private static native boolean nativeHasAlpha(long nativeBitmap); private static native boolean nativeIsPremultiplied(long nativeBitmap); private static native void nativeSetPremultiplied(long nativeBitmap, |