Attempt to solve the double-GC problem

Fix the issue where Bitmap requires two GC passes
to release its byte[] by using some questionable
ref-counting hacks to manage whether or not
native has a strong or weak ref to the byte[]

Change-Id: Ia90a883579f61c0b1904b5549a66bd0ef34b32c5
This commit is contained in:
John Reck
2015-04-07 07:32:03 -07:00
parent 05dc201ff6
commit f29ed28c7b
8 changed files with 701 additions and 384 deletions

View File

@@ -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;
}
if (!bitmap.valid()) {
return JNI_FALSE;
}
bool success = false;
if (NULL != bitmap) {
SkAutoLockPixels alp(*bitmap);
if (NULL == bitmap->getPixels()) {
return JNI_FALSE;
}
std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage));
if (!strm.get()) {
return JNI_FALSE;
}
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
if (NULL == strm) {
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 },
};

View File

@@ -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_ */

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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,