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:
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
114
core/jni/android/graphics/Bitmap.h
Normal file
114
core/jni/android/graphics/Bitmap.h
Normal 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_ */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user