Merge "Remove all hacks around ref counting in Bitmap Test: refactoring cl. bug:27762775"
This commit is contained in:
committed by
Android (Google) Code Review
commit
ac347fb4ea
@@ -31,152 +31,160 @@
|
||||
#define DEBUG_PARCEL 0
|
||||
#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10))
|
||||
|
||||
static jclass gBitmap_class;
|
||||
static jfieldID gBitmap_nativePtr;
|
||||
static jmethodID gBitmap_constructorMethodID;
|
||||
static jmethodID gBitmap_reinitMethodID;
|
||||
static jmethodID gBitmap_getAllocationByteCountMethodID;
|
||||
|
||||
namespace android {
|
||||
|
||||
class WrappedPixelRef : public SkPixelRef {
|
||||
class Bitmap {
|
||||
public:
|
||||
WrappedPixelRef(Bitmap* wrapper, void* storage,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: SkPixelRef(info)
|
||||
, mBitmap(*wrapper)
|
||||
, mStorage(storage) {
|
||||
reconfigure(info, rowBytes, ctable);
|
||||
Bitmap(PixelRef* pixelRef)
|
||||
: mPixelRef(pixelRef) { }
|
||||
|
||||
void freePixels() {
|
||||
mInfo = mPixelRef->info();
|
||||
mHasHardwareMipMap = mPixelRef->hasHardwareMipMap();
|
||||
mAllocationSize = mPixelRef->getAllocationByteCount();
|
||||
mRowBytes = mPixelRef->rowBytes();
|
||||
mGenerationId = mPixelRef->getGenerationID();
|
||||
mPixelRef.reset();
|
||||
}
|
||||
|
||||
~WrappedPixelRef() {
|
||||
// Tell SkRefCnt that everything is as it expects by forcing
|
||||
// the refcnt to 1
|
||||
internal_dispose_restore_refcnt_to_1();
|
||||
SkSafeUnref(mColorTable);
|
||||
bool valid() {
|
||||
return !!mPixelRef;
|
||||
}
|
||||
|
||||
void reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
|
||||
if (kIndex_8_SkColorType != newInfo.colorType()) {
|
||||
ctable = nullptr;
|
||||
PixelRef* pixelRef() { return mPixelRef.get(); }
|
||||
|
||||
void assertValid() {
|
||||
LOG_ALWAYS_FATAL_IF(!valid(), "Error, cannot access an invalid/free'd bitmap here!");
|
||||
}
|
||||
|
||||
void getSkBitmap(SkBitmap* outBitmap) {
|
||||
assertValid();
|
||||
mPixelRef->getSkBitmap(outBitmap);
|
||||
}
|
||||
|
||||
bool hasHardwareMipMap() {
|
||||
if (mPixelRef) {
|
||||
return mPixelRef->hasHardwareMipMap();
|
||||
}
|
||||
mRowBytes = rowBytes;
|
||||
if (mColorTable != ctable) {
|
||||
SkSafeUnref(mColorTable);
|
||||
mColorTable = ctable;
|
||||
SkSafeRef(mColorTable);
|
||||
}
|
||||
|
||||
// Need to validate the alpha type to filter against the color type
|
||||
// to prevent things like a non-opaque RGB565 bitmap
|
||||
SkAlphaType alphaType;
|
||||
LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
|
||||
newInfo.colorType(), newInfo.alphaType(), &alphaType),
|
||||
"Failed to validate alpha type!");
|
||||
|
||||
// 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 = newInfo;
|
||||
changeAlphaType(alphaType);
|
||||
|
||||
// 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;
|
||||
assertValid();
|
||||
mPixelRef->setHasHardwareMipMap(hasMipMap);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool onNewLockPixels(LockRec* rec) override {
|
||||
rec->fPixels = mStorage;
|
||||
rec->fRowBytes = mRowBytes;
|
||||
rec->fColorTable = mColorTable;
|
||||
return true;
|
||||
void setAlphaType(SkAlphaType alphaType) {
|
||||
assertValid();
|
||||
mPixelRef->setAlphaType(alphaType);
|
||||
}
|
||||
|
||||
virtual void onUnlockPixels() override {
|
||||
// nothing
|
||||
const SkImageInfo& info() {
|
||||
if (mPixelRef) {
|
||||
return mPixelRef->info();
|
||||
}
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
virtual size_t getAllocatedSizeInBytes() const override {
|
||||
return info().getSafeSize(mRowBytes);
|
||||
size_t getAllocationByteCount() const {
|
||||
if (mPixelRef) {
|
||||
return mPixelRef->getAllocationByteCount();
|
||||
}
|
||||
return mAllocationSize;
|
||||
}
|
||||
|
||||
size_t rowBytes() const {
|
||||
if (mPixelRef) {
|
||||
return mPixelRef->rowBytes();
|
||||
}
|
||||
return mRowBytes;
|
||||
}
|
||||
|
||||
uint32_t getGenerationID() const {
|
||||
if (mPixelRef) {
|
||||
return mPixelRef->getGenerationID();
|
||||
}
|
||||
return mGenerationId;
|
||||
}
|
||||
|
||||
~Bitmap() { }
|
||||
|
||||
private:
|
||||
Bitmap& mBitmap;
|
||||
void* mStorage;
|
||||
size_t mRowBytes = 0;
|
||||
SkColorTable* mColorTable = nullptr;
|
||||
bool mHasHardwareMipMap = false;
|
||||
|
||||
virtual void internal_dispose() const override {
|
||||
mBitmap.onStrongRefDestroyed();
|
||||
}
|
||||
sk_sp<PixelRef> mPixelRef;
|
||||
SkImageInfo mInfo;
|
||||
bool mHasHardwareMipMap;
|
||||
size_t mAllocationSize;
|
||||
size_t mRowBytes;
|
||||
uint32_t mGenerationId;
|
||||
};
|
||||
|
||||
Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: mPixelStorageType(PixelStorageType::Heap) {
|
||||
mPixelStorage.heap.address = address;
|
||||
mPixelStorage.heap.size = size;
|
||||
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();
|
||||
void PixelRef::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
|
||||
if (kIndex_8_SkColorType != newInfo.colorType()) {
|
||||
ctable = nullptr;
|
||||
}
|
||||
mRowBytes = rowBytes;
|
||||
if (mColorTable.get() != ctable) {
|
||||
mColorTable.reset(ctable);
|
||||
}
|
||||
|
||||
// Need to validate the alpha type to filter against the color type
|
||||
// to prevent things like a non-opaque RGB565 bitmap
|
||||
SkAlphaType alphaType;
|
||||
LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
|
||||
newInfo.colorType(), newInfo.alphaType(), &alphaType),
|
||||
"Failed to validate alpha type!");
|
||||
|
||||
// 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 = newInfo;
|
||||
changeAlphaType(alphaType);
|
||||
|
||||
// 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(getStorage(), mRowBytes, mColorTable.get());
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: mPixelStorageType(PixelStorageType::External) {
|
||||
PixelRef::PixelRef(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: SkPixelRef(info)
|
||||
, mPixelStorageType(PixelStorageType::Heap) {
|
||||
mPixelStorage.heap.address = address;
|
||||
mPixelStorage.heap.size = size;
|
||||
reconfigure(info, rowBytes, ctable);
|
||||
}
|
||||
|
||||
PixelRef::PixelRef(void* address, void* context, FreeFunc freeFunc,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: SkPixelRef(info)
|
||||
, 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();
|
||||
reconfigure(info, rowBytes, ctable);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: mPixelStorageType(PixelStorageType::Ashmem) {
|
||||
PixelRef::PixelRef(void* address, int fd, size_t mappedSize,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
|
||||
: SkPixelRef(info)
|
||||
, mPixelStorageType(PixelStorageType::Ashmem) {
|
||||
mPixelStorage.ashmem.address = address;
|
||||
mPixelStorage.ashmem.fd = fd;
|
||||
mPixelStorage.ashmem.size = mappedSize;
|
||||
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();
|
||||
reconfigure(info, rowBytes, ctable);
|
||||
}
|
||||
|
||||
void Bitmap::freePixels() {
|
||||
AutoMutex _lock(mLock);
|
||||
if (mPinnedRefCount == 0) {
|
||||
doFreePixels();
|
||||
mPixelStorageType = PixelStorageType::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::doFreePixels() {
|
||||
PixelRef::~PixelRef() {
|
||||
switch (mPixelStorageType) {
|
||||
case PixelStorageType::Invalid:
|
||||
// already free'd, nothing to do
|
||||
break;
|
||||
case PixelStorageType::External:
|
||||
mPixelStorage.external.freeFunc(mPixelStorage.external.address,
|
||||
mPixelStorage.external.context);
|
||||
@@ -191,20 +199,41 @@ void Bitmap::doFreePixels() {
|
||||
}
|
||||
|
||||
if (android::uirenderer::Caches::hasInstance()) {
|
||||
android::uirenderer::Caches::getInstance().textureCache.releaseTexture(
|
||||
mPixelRef->getStableID());
|
||||
android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
|
||||
}
|
||||
}
|
||||
|
||||
bool Bitmap::hasHardwareMipMap() {
|
||||
return mPixelRef->hasHardwareMipMap();
|
||||
bool PixelRef::hasHardwareMipMap() const {
|
||||
return mHasHardwareMipMap;
|
||||
}
|
||||
|
||||
void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
|
||||
mPixelRef->setHasHardwareMipMap(hasMipMap);
|
||||
void PixelRef::setHasHardwareMipMap(bool hasMipMap) {
|
||||
mHasHardwareMipMap = hasMipMap;
|
||||
}
|
||||
|
||||
int Bitmap::getAshmemFd() const {
|
||||
void* PixelRef::getStorage() const {
|
||||
switch (mPixelStorageType) {
|
||||
case PixelStorageType::External:
|
||||
return mPixelStorage.external.address;
|
||||
case PixelStorageType::Ashmem:
|
||||
return mPixelStorage.ashmem.address;
|
||||
case PixelStorageType::Heap:
|
||||
return mPixelStorage.heap.address;
|
||||
}
|
||||
}
|
||||
|
||||
bool PixelRef::onNewLockPixels(LockRec* rec) {
|
||||
rec->fPixels = getStorage();
|
||||
rec->fRowBytes = mRowBytes;
|
||||
rec->fColorTable = mColorTable.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t PixelRef::getAllocatedSizeInBytes() const {
|
||||
return info().getSafeSize(mRowBytes);
|
||||
}
|
||||
|
||||
int PixelRef::getAshmemFd() const {
|
||||
switch (mPixelStorageType) {
|
||||
case PixelStorageType::Ashmem:
|
||||
return mPixelStorage.ashmem.fd;
|
||||
@@ -213,7 +242,7 @@ int Bitmap::getAshmemFd() const {
|
||||
}
|
||||
}
|
||||
|
||||
size_t Bitmap::getAllocationByteCount() const {
|
||||
size_t PixelRef::getAllocationByteCount() const {
|
||||
switch (mPixelStorageType) {
|
||||
case PixelStorageType::Heap:
|
||||
return mPixelStorage.heap.size;
|
||||
@@ -222,104 +251,24 @@ size_t Bitmap::getAllocationByteCount() const {
|
||||
}
|
||||
}
|
||||
|
||||
const SkImageInfo& Bitmap::info() const {
|
||||
return mPixelRef->info();
|
||||
}
|
||||
|
||||
size_t Bitmap::rowBytes() const {
|
||||
return mPixelRef->rowBytes();
|
||||
}
|
||||
|
||||
SkPixelRef* Bitmap::peekAtPixelRef() const {
|
||||
assertValid();
|
||||
return mPixelRef.get();
|
||||
}
|
||||
|
||||
SkPixelRef* Bitmap::refPixelRef() {
|
||||
assertValid();
|
||||
android::AutoMutex _lock(mLock);
|
||||
return refPixelRefLocked();
|
||||
}
|
||||
|
||||
SkPixelRef* Bitmap::refPixelRefLocked() {
|
||||
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
|
||||
mPinnedRefCount++;
|
||||
}
|
||||
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) {
|
||||
void PixelRef::reconfigure(const SkImageInfo& info) {
|
||||
reconfigure(info, info.minRowBytes(), nullptr);
|
||||
}
|
||||
|
||||
void Bitmap::setAlphaType(SkAlphaType alphaType) {
|
||||
void PixelRef::setAlphaType(SkAlphaType alphaType) {
|
||||
if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPixelRef->changeAlphaType(alphaType);
|
||||
changeAlphaType(alphaType);
|
||||
}
|
||||
|
||||
void Bitmap::detachFromJava() {
|
||||
bool disposeSelf;
|
||||
{
|
||||
android::AutoMutex _lock(mLock);
|
||||
mAttachedToJava = false;
|
||||
disposeSelf = shouldDisposeSelfLocked();
|
||||
}
|
||||
if (disposeSelf) {
|
||||
delete this;
|
||||
}
|
||||
void PixelRef::getSkBitmap(SkBitmap* outBitmap) {
|
||||
outBitmap->setInfo(info(), rowBytes());
|
||||
outBitmap->setPixelRef(this);
|
||||
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
|
||||
}
|
||||
|
||||
bool Bitmap::shouldDisposeSelfLocked() {
|
||||
return mPinnedRefCount == 0 && !mAttachedToJava;
|
||||
}
|
||||
|
||||
|
||||
void Bitmap::onStrongRefDestroyed() {
|
||||
bool disposeSelf = false;
|
||||
{
|
||||
android::AutoMutex _lock(mLock);
|
||||
if (mPinnedRefCount > 0) {
|
||||
mPinnedRefCount--;
|
||||
if (mPinnedRefCount == 0) {
|
||||
disposeSelf = shouldDisposeSelfLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (disposeSelf) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
|
||||
assertValid();
|
||||
android::AutoMutex _lock(mLock);
|
||||
// Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes()
|
||||
// would require locking the pixels first.
|
||||
outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
|
||||
outBitmap->setPixelRef(refPixelRefLocked())->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
|
||||
@@ -333,7 +282,7 @@ public:
|
||||
}
|
||||
|
||||
void* pixels() {
|
||||
return mBitmap->peekAtPixelRef()->pixels();
|
||||
return mBitmap->pixelRef()->pixels();
|
||||
}
|
||||
|
||||
bool valid() {
|
||||
@@ -344,6 +293,78 @@ private:
|
||||
Bitmap* mBitmap;
|
||||
};
|
||||
|
||||
namespace bitmap {
|
||||
|
||||
// Assert that bitmap's SkAlphaType is consistent with 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(info.alphaType() != kUnpremul_SkAlphaType);
|
||||
} else {
|
||||
SkASSERT(info.alphaType() != kPremul_SkAlphaType);
|
||||
}
|
||||
}
|
||||
|
||||
void 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(info, isPremultiplied);
|
||||
|
||||
env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
|
||||
info.width(), info.height(), isPremultiplied);
|
||||
}
|
||||
|
||||
int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
|
||||
{
|
||||
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
|
||||
}
|
||||
|
||||
jobject createBitmap(JNIEnv* env, PixelRef* pixelRef,
|
||||
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(pixelRef->info(), isPremultiplied);
|
||||
Bitmap* bitmap = new Bitmap(pixelRef);
|
||||
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
|
||||
reinterpret_cast<jlong>(bitmap), pixelRef->width(), pixelRef->height(), density, isMutable,
|
||||
isPremultiplied, ninePatchChunk, ninePatchInsets);
|
||||
|
||||
if (env->ExceptionCheck() != 0) {
|
||||
ALOGE("*** Uncaught exception returned from Java call!\n");
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap) {
|
||||
LocalScopedBitmap bitmap(bitmapHandle);
|
||||
bitmap->getSkBitmap(outBitmap);
|
||||
}
|
||||
|
||||
PixelRef* toPixelRef(JNIEnv* env, jobject bitmap) {
|
||||
SkASSERT(env);
|
||||
SkASSERT(bitmap);
|
||||
SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
|
||||
jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
|
||||
LocalScopedBitmap localBitmap(bitmapHandle);
|
||||
localBitmap->assertValid();
|
||||
return localBitmap->pixelRef();
|
||||
}
|
||||
|
||||
} // namespace bitmap
|
||||
|
||||
} // namespace android
|
||||
|
||||
using namespace android;
|
||||
using namespace android::bitmap;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Conversions to/from SkColor, for get/setPixels, and the create method, which
|
||||
// is basically like setPixels
|
||||
@@ -649,8 +670,8 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int getPremulBitmapCreateFlags(bool isMutable) {
|
||||
int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
|
||||
int flags = android::bitmap::kBitmapCreateFlag_Premultiplied;
|
||||
if (isMutable) flags |= android::bitmap::kBitmapCreateFlag_Mutable;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -674,7 +695,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
|
||||
SkBitmap bitmap;
|
||||
bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
|
||||
|
||||
Bitmap* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
|
||||
PixelRef* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
|
||||
if (!nativeBitmap) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -684,7 +705,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
|
||||
0, 0, width, height, bitmap);
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, nativeBitmap,
|
||||
return createBitmap(env, nativeBitmap,
|
||||
getPremulBitmapCreateFlags(isMutable));
|
||||
}
|
||||
|
||||
@@ -699,29 +720,28 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
|
||||
if (!src.copyTo(&result, dstCT, &allocator)) {
|
||||
return NULL;
|
||||
}
|
||||
Bitmap* bitmap = allocator.getStorageObjAndReset();
|
||||
return GraphicsJNI::createBitmap(env, bitmap,
|
||||
getPremulBitmapCreateFlags(isMutable));
|
||||
auto pixelRef = allocator.getStorageObjAndReset();
|
||||
return createBitmap(env, pixelRef, getPremulBitmapCreateFlags(isMutable));
|
||||
}
|
||||
|
||||
static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
|
||||
static PixelRef* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
|
||||
SkBitmap result;
|
||||
|
||||
AshmemPixelAllocator allocator(env);
|
||||
if (!src.copyTo(&result, dstCT, &allocator)) {
|
||||
return NULL;
|
||||
}
|
||||
Bitmap* bitmap = allocator.getStorageObjAndReset();
|
||||
bitmap->peekAtPixelRef()->setImmutable();
|
||||
return bitmap;
|
||||
auto pixelRef = allocator.getStorageObjAndReset();
|
||||
pixelRef->setImmutable();
|
||||
return pixelRef;
|
||||
}
|
||||
|
||||
static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
|
||||
SkBitmap src;
|
||||
reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
|
||||
SkColorType dstCT = src.colorType();
|
||||
Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
|
||||
jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
|
||||
auto pixelRef = Bitmap_copyAshmemImpl(env, src, dstCT);
|
||||
jobject ret = createBitmap(env, pixelRef, getPremulBitmapCreateFlags(false));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -729,13 +749,13 @@ static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, ji
|
||||
SkBitmap src;
|
||||
reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
|
||||
SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
|
||||
Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
|
||||
jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
|
||||
auto pixelRef = Bitmap_copyAshmemImpl(env, src, dstCT);
|
||||
jobject ret = createBitmap(env, pixelRef, getPremulBitmapCreateFlags(false));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void Bitmap_destruct(Bitmap* bitmap) {
|
||||
bitmap->detachFromJava();
|
||||
delete bitmap;
|
||||
}
|
||||
|
||||
static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) {
|
||||
@@ -751,6 +771,7 @@ static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
|
||||
jint width, jint height, jint configHandle, jboolean requestPremul) {
|
||||
LocalScopedBitmap bitmap(bitmapHandle);
|
||||
bitmap->assertValid();
|
||||
SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
|
||||
|
||||
// ARGB_4444 is a deprecated format, convert automatically to 8888
|
||||
@@ -773,7 +794,7 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
|
||||
// Otherwise respect the premultiplied request.
|
||||
alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
|
||||
}
|
||||
bitmap->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType));
|
||||
bitmap->pixelRef()->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType));
|
||||
}
|
||||
|
||||
// These must match the int values in Bitmap.java
|
||||
@@ -843,7 +864,7 @@ static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
|
||||
static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
LocalScopedBitmap bitmap(bitmapHandle);
|
||||
return static_cast<jint>(bitmap->peekAtPixelRef()->getGenerationID());
|
||||
return static_cast<jint>(bitmap->getGenerationID());
|
||||
}
|
||||
|
||||
static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
@@ -955,7 +976,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
|
||||
}
|
||||
|
||||
// Map the bitmap in place from the ashmem region if possible otherwise copy.
|
||||
Bitmap* nativeBitmap;
|
||||
PixelRef* nativeBitmap;
|
||||
if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) {
|
||||
#if DEBUG_PARCEL
|
||||
ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob "
|
||||
@@ -1018,7 +1039,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
|
||||
blob.release();
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, nativeBitmap,
|
||||
return createBitmap(env, nativeBitmap,
|
||||
getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
|
||||
}
|
||||
|
||||
@@ -1034,7 +1055,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
|
||||
android::Parcel* p = android::parcelForJavaObject(env, parcel);
|
||||
SkBitmap bitmap;
|
||||
|
||||
android::Bitmap* androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
|
||||
auto androidBitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
|
||||
androidBitmap->getSkBitmap(&bitmap);
|
||||
|
||||
p->writeInt32(isMutable);
|
||||
@@ -1061,7 +1082,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
|
||||
|
||||
// Transfer the underlying ashmem region if we have one and it's immutable.
|
||||
android::status_t status;
|
||||
int fd = androidBitmap->getAshmemFd();
|
||||
int fd = androidBitmap->pixelRef()->getAshmemFd();
|
||||
if (fd >= 0 && !isMutable && p->allowFds()) {
|
||||
#if DEBUG_PARCEL
|
||||
ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as "
|
||||
@@ -1131,7 +1152,7 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
|
||||
env->ReleaseIntArrayElements(offsetXY, array, 0);
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, allocator.getStorageObjAndReset(),
|
||||
return createBitmap(env, allocator.getStorageObjAndReset(),
|
||||
getPremulBitmapCreateFlags(true));
|
||||
}
|
||||
|
||||
@@ -1307,7 +1328,7 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
|
||||
|
||||
static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
|
||||
LocalScopedBitmap bitmap(bitmapHandle);
|
||||
SkPixelRef* pixelRef = bitmap.valid() ? bitmap->peekAtPixelRef() : nullptr;
|
||||
SkPixelRef* pixelRef = bitmap->pixelRef();
|
||||
SkSafeRef(pixelRef);
|
||||
return reinterpret_cast<jlong>(pixelRef);
|
||||
}
|
||||
@@ -1326,6 +1347,20 @@ static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static jclass make_globalref(JNIEnv* env, const char classname[])
|
||||
{
|
||||
jclass c = env->FindClass(classname);
|
||||
SkASSERT(c);
|
||||
return (jclass) env->NewGlobalRef(c);
|
||||
}
|
||||
|
||||
static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
|
||||
const char fieldname[], const char type[])
|
||||
{
|
||||
jfieldID id = env->GetFieldID(clazz, fieldname, type);
|
||||
SkASSERT(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
static const JNINativeMethod gBitmapMethods[] = {
|
||||
{ "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
|
||||
@@ -1374,6 +1409,11 @@ static const JNINativeMethod gBitmapMethods[] = {
|
||||
|
||||
int register_android_graphics_Bitmap(JNIEnv* env)
|
||||
{
|
||||
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
|
||||
gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
|
||||
gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
|
||||
gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
|
||||
return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
|
||||
NELEM(gBitmapMethods));
|
||||
}
|
||||
}
|
||||
@@ -20,80 +20,91 @@
|
||||
#include <SkBitmap.h>
|
||||
#include <SkColorTable.h>
|
||||
#include <SkImageInfo.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <memory>
|
||||
#include <SkPixelRef.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
enum class PixelStorageType {
|
||||
Invalid,
|
||||
External,
|
||||
Heap,
|
||||
Ashmem,
|
||||
};
|
||||
|
||||
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(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
|
||||
SkColorTable* ctable);
|
||||
Bitmap(void* address, void* context, FreeFunc freeFunc,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
|
||||
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
|
||||
size_t rowBytes, SkColorTable* ctable);
|
||||
class PixelRef;
|
||||
|
||||
const SkImageInfo& info() const;
|
||||
namespace bitmap {
|
||||
|
||||
enum BitmapCreateFlags {
|
||||
kBitmapCreateFlag_None = 0x0,
|
||||
kBitmapCreateFlag_Mutable = 0x1,
|
||||
kBitmapCreateFlag_Premultiplied = 0x2,
|
||||
};
|
||||
|
||||
jobject createBitmap(JNIEnv* env, PixelRef* bitmap,
|
||||
int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL,
|
||||
jobject ninePatchInsets = NULL, int density = -1);
|
||||
|
||||
|
||||
void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap);
|
||||
|
||||
PixelRef* toPixelRef(JNIEnv* env, jobject bitmap);
|
||||
|
||||
/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
|
||||
sync with isPremultiplied
|
||||
*/
|
||||
void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
|
||||
bool isPremultiplied);
|
||||
|
||||
int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);
|
||||
|
||||
} // namespace bitmap
|
||||
|
||||
class PixelRef : public SkPixelRef {
|
||||
public:
|
||||
PixelRef(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
|
||||
SkColorTable* ctable);
|
||||
PixelRef(void* address, void* context, FreeFunc freeFunc,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
|
||||
PixelRef(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
|
||||
size_t rowBytes, SkColorTable* ctable);
|
||||
|
||||
int width() const { return info().width(); }
|
||||
int height() const { return info().height(); }
|
||||
size_t rowBytes() const;
|
||||
SkPixelRef* peekAtPixelRef() const;
|
||||
SkPixelRef* refPixelRef();
|
||||
bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; }
|
||||
|
||||
// Can't mark as override since SkPixelRef::rowBytes isn't virtual
|
||||
// but that's OK since we just want Bitmap to be able to rely
|
||||
// on calling rowBytes() on an unlocked pixelref, which it will be
|
||||
// doing on a PixelRef type, not a SkPixelRef, so static
|
||||
// dispatching will do what we want.
|
||||
size_t rowBytes() const { return mRowBytes; }
|
||||
void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
|
||||
void reconfigure(const SkImageInfo& info);
|
||||
void setAlphaType(SkAlphaType alphaType);
|
||||
|
||||
void getSkBitmap(SkBitmap* outBitmap);
|
||||
void detachFromJava();
|
||||
|
||||
void freePixels();
|
||||
|
||||
bool hasHardwareMipMap();
|
||||
void setHasHardwareMipMap(bool hasMipMap);
|
||||
int getAshmemFd() const;
|
||||
size_t getAllocationByteCount() const;
|
||||
|
||||
protected:
|
||||
virtual bool onNewLockPixels(LockRec* rec) override;
|
||||
virtual void onUnlockPixels() override { };
|
||||
virtual size_t getAllocatedSizeInBytes() const override;
|
||||
private:
|
||||
friend class WrappedPixelRef;
|
||||
|
||||
~Bitmap();
|
||||
friend class Bitmap;
|
||||
virtual ~PixelRef();
|
||||
void doFreePixels();
|
||||
void onStrongRefDestroyed();
|
||||
void* getStorage() const;
|
||||
void setHasHardwareMipMap(bool hasMipMap);
|
||||
bool hasHardwareMipMap() const;
|
||||
|
||||
void pinPixelsLocked();
|
||||
bool shouldDisposeSelfLocked();
|
||||
void assertValid() const;
|
||||
SkPixelRef* refPixelRefLocked();
|
||||
|
||||
android::Mutex mLock;
|
||||
int mPinnedRefCount = 0;
|
||||
std::unique_ptr<WrappedPixelRef> mPixelRef;
|
||||
PixelStorageType mPixelStorageType;
|
||||
bool mAttachedToJava = true;
|
||||
|
||||
size_t mRowBytes = 0;
|
||||
sk_sp<SkColorTable> mColorTable;
|
||||
bool mHasHardwareMipMap = false;
|
||||
|
||||
union {
|
||||
struct {
|
||||
|
||||
@@ -162,7 +162,7 @@ private:
|
||||
|
||||
class RecyclingPixelAllocator : public SkBitmap::Allocator {
|
||||
public:
|
||||
RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
|
||||
RecyclingPixelAllocator(android::PixelRef* bitmap, unsigned int size)
|
||||
: mBitmap(bitmap), mSize(size) {
|
||||
}
|
||||
|
||||
@@ -190,7 +190,8 @@ public:
|
||||
}
|
||||
|
||||
mBitmap->reconfigure(info, bitmap->rowBytes(), ctable);
|
||||
bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
|
||||
mBitmap->ref();
|
||||
bitmap->setPixelRef(mBitmap)->unref();
|
||||
|
||||
// since we're already allocated, we lockPixels right away
|
||||
// HeapAllocator behaves this way too
|
||||
@@ -199,7 +200,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
android::Bitmap* const mBitmap;
|
||||
android::PixelRef* const mBitmap;
|
||||
const unsigned int mSize;
|
||||
};
|
||||
|
||||
@@ -326,16 +327,16 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding
|
||||
scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
|
||||
}
|
||||
|
||||
android::Bitmap* reuseBitmap = nullptr;
|
||||
android::PixelRef* reuseBitmap = nullptr;
|
||||
unsigned int existingBufferSize = 0;
|
||||
if (javaBitmap != NULL) {
|
||||
reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
|
||||
if (reuseBitmap->peekAtPixelRef()->isImmutable()) {
|
||||
reuseBitmap = bitmap::toPixelRef(env, javaBitmap);
|
||||
if (reuseBitmap->isImmutable()) {
|
||||
ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
|
||||
javaBitmap = NULL;
|
||||
reuseBitmap = nullptr;
|
||||
} else {
|
||||
existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
|
||||
existingBufferSize = bitmap::getBitmapAllocationByteCount(env, javaBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,18 +530,18 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding
|
||||
|
||||
bool isPremultiplied = !requireUnpremultiplied;
|
||||
if (javaBitmap != nullptr) {
|
||||
GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
|
||||
bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
|
||||
outputBitmap.notifyPixelsChanged();
|
||||
// If a java bitmap was passed in for reuse, pass it back
|
||||
return javaBitmap;
|
||||
}
|
||||
|
||||
int bitmapCreateFlags = 0x0;
|
||||
if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
|
||||
if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
if (isMutable) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Mutable;
|
||||
if (isPremultiplied) bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
|
||||
|
||||
// now create the java bitmap
|
||||
return GraphicsJNI::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
|
||||
return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
|
||||
bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,14 +148,14 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
|
||||
}
|
||||
|
||||
// Recycle a bitmap if possible.
|
||||
android::Bitmap* recycledBitmap = nullptr;
|
||||
android::PixelRef* recycledBitmap = nullptr;
|
||||
size_t recycledBytes = 0;
|
||||
if (javaBitmap) {
|
||||
recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
|
||||
if (recycledBitmap->peekAtPixelRef()->isImmutable()) {
|
||||
recycledBitmap = bitmap::toPixelRef(env, javaBitmap);
|
||||
if (recycledBitmap->isImmutable()) {
|
||||
ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
|
||||
}
|
||||
recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
|
||||
recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap);
|
||||
}
|
||||
|
||||
// Set up the pixel allocator
|
||||
@@ -198,9 +198,9 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
|
||||
|
||||
int bitmapCreateFlags = 0;
|
||||
if (!requireUnpremul) {
|
||||
bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
|
||||
}
|
||||
return GraphicsJNI::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
|
||||
return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
|
||||
}
|
||||
|
||||
static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
|
||||
|
||||
@@ -157,12 +157,6 @@ static jclass gPointF_class;
|
||||
static jfieldID gPointF_xFieldID;
|
||||
static jfieldID gPointF_yFieldID;
|
||||
|
||||
static jclass gBitmap_class;
|
||||
static jfieldID gBitmap_nativePtr;
|
||||
static jmethodID gBitmap_constructorMethodID;
|
||||
static jmethodID gBitmap_reinitMethodID;
|
||||
static jmethodID gBitmap_getAllocationByteCountMethodID;
|
||||
|
||||
static jclass gBitmapConfig_class;
|
||||
static jfieldID gBitmapConfig_nativeInstanceID;
|
||||
|
||||
@@ -342,24 +336,15 @@ SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
|
||||
return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
|
||||
}
|
||||
|
||||
android::Bitmap* GraphicsJNI::getBitmap(JNIEnv* env, jobject bitmap) {
|
||||
SkASSERT(env);
|
||||
SkASSERT(bitmap);
|
||||
SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
|
||||
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) {
|
||||
getBitmap(env, bitmap)->getSkBitmap(outBitmap);
|
||||
android::bitmap::toPixelRef(env, bitmap)->getSkBitmap(outBitmap);
|
||||
}
|
||||
|
||||
SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) {
|
||||
return getBitmap(env, bitmap)->refPixelRef();
|
||||
SkPixelRef* pixelRef = android::bitmap::toPixelRef(env, bitmap);
|
||||
pixelRef->ref();
|
||||
return pixelRef;
|
||||
}
|
||||
|
||||
SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
|
||||
SkASSERT(env);
|
||||
if (NULL == jconfig) {
|
||||
@@ -394,50 +379,6 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Assert that bitmap's SkAlphaType is consistent with 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(info.alphaType() != kUnpremul_SkAlphaType);
|
||||
} else {
|
||||
SkASSERT(info.alphaType() != kPremul_SkAlphaType);
|
||||
}
|
||||
}
|
||||
|
||||
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->info(), isPremultiplied);
|
||||
|
||||
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
|
||||
reinterpret_cast<jlong>(bitmap), 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, 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(info, isPremultiplied);
|
||||
|
||||
env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
|
||||
info.width(), info.height(), isPremultiplied);
|
||||
}
|
||||
|
||||
int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
|
||||
{
|
||||
return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
|
||||
}
|
||||
|
||||
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
|
||||
{
|
||||
SkASSERT(bitmap != NULL);
|
||||
@@ -482,7 +423,7 @@ static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
android::Bitmap* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
android::PixelRef* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
const SkImageInfo& info = bitmap->info();
|
||||
if (info.colorType() == kUnknown_SkColorType) {
|
||||
LOG_ALWAYS_FATAL("unknown bitmap configuration");
|
||||
@@ -503,7 +444,7 @@ android::Bitmap* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTabl
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
android::Bitmap* wrapper = new android::Bitmap(addr, size, info, rowBytes, ctable);
|
||||
auto wrapper = new android::PixelRef(addr, size, info, rowBytes, ctable);
|
||||
wrapper->getSkBitmap(bitmap);
|
||||
// since we're already allocated, we lockPixels right away
|
||||
// HeapAllocator behaves this way too
|
||||
@@ -566,7 +507,7 @@ bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ct
|
||||
return true;
|
||||
}
|
||||
|
||||
android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
android::PixelRef* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable) {
|
||||
int fd;
|
||||
|
||||
@@ -603,7 +544,7 @@ android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitm
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
android::Bitmap* wrapper = new android::Bitmap(addr, fd, size, info, rowBytes, ctable);
|
||||
auto wrapper = new android::PixelRef(addr, fd, size, info, rowBytes, ctable);
|
||||
wrapper->getSkBitmap(bitmap);
|
||||
// since we're already allocated, we lockPixels right away
|
||||
// HeapAllocator behaves this way too
|
||||
@@ -612,7 +553,7 @@ android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitm
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
android::PixelRef* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly) {
|
||||
const SkImageInfo& info = bitmap->info();
|
||||
if (info.colorType() == kUnknown_SkColorType) {
|
||||
@@ -634,7 +575,7 @@ android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
// attempting to compute our own.
|
||||
const size_t rowBytes = bitmap->rowBytes();
|
||||
|
||||
android::Bitmap* wrapper = new android::Bitmap(addr, fd, size, info, rowBytes, ctable);
|
||||
auto wrapper = new android::PixelRef(addr, fd, size, info, rowBytes, ctable);
|
||||
wrapper->getSkBitmap(bitmap);
|
||||
if (readOnly) {
|
||||
bitmap->pixelRef()->setImmutable();
|
||||
@@ -647,24 +588,15 @@ android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HeapAllocator::HeapAllocator() {}
|
||||
|
||||
HeapAllocator::~HeapAllocator() {
|
||||
if (mStorage) {
|
||||
mStorage->detachFromJava();
|
||||
}
|
||||
}
|
||||
|
||||
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
mStorage = GraphicsJNI::allocateHeapPixelRef(bitmap, ctable);
|
||||
return mStorage != nullptr;
|
||||
mStorage.reset(GraphicsJNI::allocateHeapPixelRef(bitmap, ctable));
|
||||
return !!mStorage;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(
|
||||
android::Bitmap* recycledBitmap, size_t recycledBytes)
|
||||
android::PixelRef* recycledBitmap, size_t recycledBytes)
|
||||
: mRecycledBitmap(recycledBitmap)
|
||||
, mRecycledBytes(recycledBytes)
|
||||
, mSkiaBitmap(nullptr)
|
||||
@@ -707,7 +639,8 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTab
|
||||
// skbug.com/4538: We also need to make sure that the rowBytes on the pixel ref
|
||||
// match the rowBytes on the bitmap.
|
||||
bitmap->setInfo(bitmap->info(), rowBytes);
|
||||
bitmap->setPixelRef(mRecycledBitmap->refPixelRef())->unref();
|
||||
mRecycledBitmap->ref();
|
||||
bitmap->setPixelRef(mRecycledBitmap)->unref();
|
||||
|
||||
// Make sure that the recycled bitmap has the correct alpha type.
|
||||
mRecycledBitmap->setAlphaType(bitmap->alphaType());
|
||||
@@ -734,7 +667,8 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTab
|
||||
|
||||
void RecyclingClippingPixelAllocator::copyIfNecessary() {
|
||||
if (mNeedsCopy) {
|
||||
SkPixelRef* recycledPixels = mRecycledBitmap->refPixelRef();
|
||||
mRecycledBitmap->ref();
|
||||
SkPixelRef* recycledPixels = mRecycledBitmap;
|
||||
void* dst = recycledPixels->pixels();
|
||||
const size_t dstRowBytes = mRecycledBitmap->rowBytes();
|
||||
const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(),
|
||||
@@ -759,16 +693,10 @@ AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) {
|
||||
"env->GetJavaVM failed");
|
||||
}
|
||||
|
||||
AshmemPixelAllocator::~AshmemPixelAllocator() {
|
||||
if (mStorage) {
|
||||
mStorage->detachFromJava();
|
||||
}
|
||||
}
|
||||
|
||||
bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
JNIEnv* env = vm2env(mJavaVM);
|
||||
mStorage = GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable);
|
||||
return mStorage != nullptr;
|
||||
mStorage.reset(GraphicsJNI::allocateAshmemPixelRef(env, bitmap, ctable));
|
||||
return !!mStorage;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -813,11 +741,6 @@ int register_android_graphics_Graphics(JNIEnv* env)
|
||||
gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
|
||||
gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
|
||||
|
||||
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
|
||||
gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
|
||||
gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
|
||||
gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
|
||||
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
|
||||
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(J)V");
|
||||
|
||||
|
||||
@@ -23,12 +23,6 @@ struct Typeface;
|
||||
|
||||
class GraphicsJNI {
|
||||
public:
|
||||
enum BitmapCreateFlags {
|
||||
kBitmapCreateFlag_None = 0x0,
|
||||
kBitmapCreateFlag_Mutable = 0x1,
|
||||
kBitmapCreateFlag_Premultiplied = 0x2,
|
||||
};
|
||||
|
||||
// returns true if an exception is set (and dumps it out to the Log)
|
||||
static bool hasException(JNIEnv*);
|
||||
|
||||
@@ -51,7 +45,6 @@ public:
|
||||
static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
|
||||
|
||||
static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
|
||||
static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap);
|
||||
static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap);
|
||||
static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
|
||||
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
|
||||
@@ -73,32 +66,16 @@ public:
|
||||
*/
|
||||
static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
|
||||
|
||||
/*
|
||||
* Create a java Bitmap object given the native bitmap
|
||||
* bitmap's SkAlphaType must already be in sync with bitmapCreateFlags.
|
||||
*/
|
||||
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, const SkImageInfo& info,
|
||||
bool isPremultiplied);
|
||||
|
||||
static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);
|
||||
|
||||
static jobject createRegion(JNIEnv* env, SkRegion* region);
|
||||
|
||||
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
|
||||
|
||||
static android::Bitmap* allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
|
||||
static android::PixelRef* allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
|
||||
|
||||
static android::Bitmap* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
static android::PixelRef* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable);
|
||||
|
||||
static android::Bitmap* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
static android::PixelRef* mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly);
|
||||
|
||||
/**
|
||||
@@ -120,23 +97,21 @@ public:
|
||||
|
||||
class HeapAllocator : public SkBRDAllocator {
|
||||
public:
|
||||
HeapAllocator();
|
||||
~HeapAllocator();
|
||||
HeapAllocator() { };
|
||||
~HeapAllocator() { };
|
||||
|
||||
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
|
||||
|
||||
/**
|
||||
* Fetches the backing allocation object. Must be called!
|
||||
*/
|
||||
android::Bitmap* getStorageObjAndReset() {
|
||||
android::Bitmap* result = mStorage;
|
||||
mStorage = NULL;
|
||||
return result;
|
||||
android::PixelRef* getStorageObjAndReset() {
|
||||
return mStorage.release();
|
||||
};
|
||||
|
||||
SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
|
||||
private:
|
||||
android::Bitmap* mStorage = nullptr;
|
||||
sk_sp<android::PixelRef> mStorage;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -169,7 +144,7 @@ private:
|
||||
class RecyclingClippingPixelAllocator : public SkBRDAllocator {
|
||||
public:
|
||||
|
||||
RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap,
|
||||
RecyclingClippingPixelAllocator(android::PixelRef* recycledBitmap,
|
||||
size_t recycledBytes);
|
||||
|
||||
~RecyclingClippingPixelAllocator();
|
||||
@@ -194,7 +169,7 @@ public:
|
||||
SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; }
|
||||
|
||||
private:
|
||||
android::Bitmap* mRecycledBitmap;
|
||||
android::PixelRef* mRecycledBitmap;
|
||||
const size_t mRecycledBytes;
|
||||
SkBitmap* mSkiaBitmap;
|
||||
bool mNeedsCopy;
|
||||
@@ -203,17 +178,15 @@ private:
|
||||
class AshmemPixelAllocator : public SkBitmap::Allocator {
|
||||
public:
|
||||
explicit AshmemPixelAllocator(JNIEnv* env);
|
||||
~AshmemPixelAllocator();
|
||||
~AshmemPixelAllocator() { };
|
||||
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
|
||||
android::Bitmap* getStorageObjAndReset() {
|
||||
android::Bitmap* result = mStorage;
|
||||
mStorage = NULL;
|
||||
return result;
|
||||
android::PixelRef* getStorageObjAndReset() {
|
||||
return mStorage.release();
|
||||
};
|
||||
|
||||
private:
|
||||
JavaVM* mJavaVM;
|
||||
android::Bitmap* mStorage = nullptr;
|
||||
sk_sp<android::PixelRef> mStorage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -341,9 +341,8 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap
|
||||
jlong paintHandle, jint dstDensity, jint srcDensity) {
|
||||
|
||||
Canvas* canvas = get_canvas(canvasHandle);
|
||||
Bitmap* bitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
|
||||
SkBitmap skiaBitmap;
|
||||
bitmap->getSkBitmap(&skiaBitmap);
|
||||
bitmap::toSkBitmap(bitmapHandle, &skiaBitmap);
|
||||
const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
|
||||
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
|
||||
|
||||
|
||||
@@ -184,14 +184,14 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bitmap* bitmap = new Bitmap(
|
||||
auto pixelRef = new PixelRef(
|
||||
(void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
|
||||
screenshotInfo, rowBytes, nullptr);
|
||||
screenshot.release();
|
||||
bitmap->peekAtPixelRef()->setImmutable();
|
||||
pixelRef->setImmutable();
|
||||
|
||||
return GraphicsJNI::createBitmap(env, bitmap,
|
||||
GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
|
||||
return bitmap::createBitmap(env, pixelRef,
|
||||
bitmap::kBitmapCreateFlag_Premultiplied, NULL);
|
||||
}
|
||||
|
||||
static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
|
||||
|
||||
Reference in New Issue
Block a user