am f1f48bc7
: Do JPEG tile-based decoding.
Merge commit 'f1f48bc7f200f54c76b22d845d8ba8419879b375' into gingerbread-plus-aosp * commit 'f1f48bc7f200f54c76b22d845d8ba8419879b375': Do JPEG tile-based decoding.
This commit is contained in:
@ -78,6 +78,7 @@ LOCAL_SRC_FILES:= \
|
||||
android_util_Process.cpp \
|
||||
android_util_StringBlock.cpp \
|
||||
android_util_XmlBlock.cpp \
|
||||
android/graphics/AutoDecodeCancel.cpp \
|
||||
android/graphics/Bitmap.cpp \
|
||||
android/graphics/BitmapFactory.cpp \
|
||||
android/graphics/Camera.cpp \
|
||||
@ -101,6 +102,7 @@ LOCAL_SRC_FILES:= \
|
||||
android_graphics_PixelFormat.cpp \
|
||||
android/graphics/Picture.cpp \
|
||||
android/graphics/PorterDuff.cpp \
|
||||
android/graphics/LargeBitmap.cpp \
|
||||
android/graphics/Rasterizer.cpp \
|
||||
android/graphics/Region.cpp \
|
||||
android/graphics/Shader.cpp \
|
||||
|
@ -53,6 +53,7 @@ extern int register_android_os_Binder(JNIEnv* env);
|
||||
extern int register_android_os_Process(JNIEnv* env);
|
||||
extern int register_android_graphics_Bitmap(JNIEnv*);
|
||||
extern int register_android_graphics_BitmapFactory(JNIEnv*);
|
||||
extern int register_android_graphics_LargeBitmap(JNIEnv*);
|
||||
extern int register_android_graphics_Camera(JNIEnv* env);
|
||||
extern int register_android_graphics_Graphics(JNIEnv* env);
|
||||
extern int register_android_graphics_Interpolator(JNIEnv* env);
|
||||
@ -1264,6 +1265,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
|
||||
REG_JNI(register_android_graphics_Bitmap),
|
||||
REG_JNI(register_android_graphics_BitmapFactory),
|
||||
REG_JNI(register_android_graphics_LargeBitmap),
|
||||
REG_JNI(register_android_graphics_Camera),
|
||||
REG_JNI(register_android_graphics_Canvas),
|
||||
REG_JNI(register_android_graphics_ColorFilter),
|
||||
|
100
core/jni/android/graphics/AutoDecodeCancel.cpp
Normal file
100
core/jni/android/graphics/AutoDecodeCancel.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "AutoDecodeCancel.h"
|
||||
|
||||
static SkMutex gAutoDecoderCancelMutex;
|
||||
static AutoDecoderCancel* gAutoDecoderCancel;
|
||||
#ifdef SK_DEBUG
|
||||
static int gAutoDecoderCancelCount;
|
||||
#endif
|
||||
|
||||
AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
|
||||
SkImageDecoder* decoder) {
|
||||
fJOptions = joptions;
|
||||
fDecoder = decoder;
|
||||
|
||||
if (NULL != joptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
// Add us as the head of the list
|
||||
fPrev = NULL;
|
||||
fNext = gAutoDecoderCancel;
|
||||
if (gAutoDecoderCancel) {
|
||||
gAutoDecoderCancel->fPrev = this;
|
||||
}
|
||||
gAutoDecoderCancel = this;
|
||||
|
||||
SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
|
||||
Validate();
|
||||
}
|
||||
}
|
||||
|
||||
AutoDecoderCancel::~AutoDecoderCancel() {
|
||||
if (NULL != fJOptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
// take us out of the dllist
|
||||
AutoDecoderCancel* prev = fPrev;
|
||||
AutoDecoderCancel* next = fNext;
|
||||
|
||||
if (prev) {
|
||||
SkASSERT(prev->fNext == this);
|
||||
prev->fNext = next;
|
||||
} else {
|
||||
SkASSERT(gAutoDecoderCancel == this);
|
||||
gAutoDecoderCancel = next;
|
||||
}
|
||||
if (next) {
|
||||
SkASSERT(next->fPrev == this);
|
||||
next->fPrev = prev;
|
||||
}
|
||||
|
||||
SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
|
||||
Validate();
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoDecoderCancel::RequestCancel(jobject joptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
Validate();
|
||||
|
||||
AutoDecoderCancel* pair = gAutoDecoderCancel;
|
||||
while (pair != NULL) {
|
||||
if (pair->fJOptions == joptions) {
|
||||
pair->fDecoder->cancelDecode();
|
||||
return true;
|
||||
}
|
||||
pair = pair->fNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// can only call this inside a lock on gAutoDecoderCancelMutex
|
||||
void AutoDecoderCancel::Validate() {
|
||||
const int gCount = gAutoDecoderCancelCount;
|
||||
|
||||
if (gCount == 0) {
|
||||
SkASSERT(gAutoDecoderCancel == NULL);
|
||||
} else {
|
||||
SkASSERT(gCount > 0);
|
||||
|
||||
AutoDecoderCancel* curr = gAutoDecoderCancel;
|
||||
SkASSERT(curr);
|
||||
SkASSERT(curr->fPrev == NULL);
|
||||
|
||||
int count = 0;
|
||||
while (curr) {
|
||||
count += 1;
|
||||
SkASSERT(count <= gCount);
|
||||
if (curr->fPrev) {
|
||||
SkASSERT(curr->fPrev->fNext == curr);
|
||||
}
|
||||
if (curr->fNext) {
|
||||
SkASSERT(curr->fNext->fPrev == curr);
|
||||
}
|
||||
curr = curr->fNext;
|
||||
}
|
||||
SkASSERT(count == gCount);
|
||||
}
|
||||
}
|
||||
#endif
|
27
core/jni/android/graphics/AutoDecodeCancel.h
Normal file
27
core/jni/android/graphics/AutoDecodeCancel.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef AutoDecodeCancel_DEFINED
|
||||
#define AutoDecodeCancel_DEFINED
|
||||
|
||||
#include <jni.h>
|
||||
#include "SkImageDecoder.h"
|
||||
|
||||
class AutoDecoderCancel {
|
||||
public:
|
||||
AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
|
||||
~AutoDecoderCancel();
|
||||
|
||||
static bool RequestCancel(jobject options);
|
||||
|
||||
private:
|
||||
AutoDecoderCancel* fNext;
|
||||
AutoDecoderCancel* fPrev;
|
||||
jobject fJOptions; // java options object
|
||||
SkImageDecoder* fDecoder;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
static void Validate();
|
||||
#else
|
||||
static void Validate() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
@ -1,14 +1,15 @@
|
||||
#define LOG_TAG "BitmapFactory"
|
||||
|
||||
#include "BitmapFactory.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkImageRef_ashmem.h"
|
||||
#include "SkImageRef_GlobalPool.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "SkStream.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkUtils.h"
|
||||
#include "CreateJavaOutputStreamAdaptor.h"
|
||||
#include "AutoDecodeCancel.h"
|
||||
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <utils/Asset.h>
|
||||
@ -16,18 +17,18 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static jclass gOptions_class;
|
||||
static jfieldID gOptions_justBoundsFieldID;
|
||||
static jfieldID gOptions_sampleSizeFieldID;
|
||||
static jfieldID gOptions_configFieldID;
|
||||
static jfieldID gOptions_ditherFieldID;
|
||||
static jfieldID gOptions_purgeableFieldID;
|
||||
static jfieldID gOptions_shareableFieldID;
|
||||
static jfieldID gOptions_nativeAllocFieldID;
|
||||
static jfieldID gOptions_widthFieldID;
|
||||
static jfieldID gOptions_heightFieldID;
|
||||
static jfieldID gOptions_mimeFieldID;
|
||||
static jfieldID gOptions_mCancelID;
|
||||
jclass gOptions_class;
|
||||
jfieldID gOptions_justBoundsFieldID;
|
||||
jfieldID gOptions_sampleSizeFieldID;
|
||||
jfieldID gOptions_configFieldID;
|
||||
jfieldID gOptions_ditherFieldID;
|
||||
jfieldID gOptions_purgeableFieldID;
|
||||
jfieldID gOptions_shareableFieldID;
|
||||
jfieldID gOptions_nativeAllocFieldID;
|
||||
jfieldID gOptions_widthFieldID;
|
||||
jfieldID gOptions_heightFieldID;
|
||||
jfieldID gOptions_mimeFieldID;
|
||||
jfieldID gOptions_mCancelID;
|
||||
|
||||
static jclass gFileDescriptor_class;
|
||||
static jfieldID gFileDescriptor_descriptor;
|
||||
@ -38,129 +39,6 @@ static jfieldID gFileDescriptor_descriptor;
|
||||
#define TRACE_BITMAP(code)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AutoDecoderCancel {
|
||||
public:
|
||||
AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
|
||||
~AutoDecoderCancel();
|
||||
|
||||
static bool RequestCancel(jobject options);
|
||||
|
||||
private:
|
||||
AutoDecoderCancel* fNext;
|
||||
AutoDecoderCancel* fPrev;
|
||||
jobject fJOptions; // java options object
|
||||
SkImageDecoder* fDecoder;
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
static void Validate();
|
||||
#else
|
||||
static void Validate() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
static SkMutex gAutoDecoderCancelMutex;
|
||||
static AutoDecoderCancel* gAutoDecoderCancel;
|
||||
#ifdef SK_DEBUG
|
||||
static int gAutoDecoderCancelCount;
|
||||
#endif
|
||||
|
||||
AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
|
||||
SkImageDecoder* decoder) {
|
||||
fJOptions = joptions;
|
||||
fDecoder = decoder;
|
||||
|
||||
if (NULL != joptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
// Add us as the head of the list
|
||||
fPrev = NULL;
|
||||
fNext = gAutoDecoderCancel;
|
||||
if (gAutoDecoderCancel) {
|
||||
gAutoDecoderCancel->fPrev = this;
|
||||
}
|
||||
gAutoDecoderCancel = this;
|
||||
|
||||
SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
|
||||
Validate();
|
||||
}
|
||||
}
|
||||
|
||||
AutoDecoderCancel::~AutoDecoderCancel() {
|
||||
if (NULL != fJOptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
// take us out of the dllist
|
||||
AutoDecoderCancel* prev = fPrev;
|
||||
AutoDecoderCancel* next = fNext;
|
||||
|
||||
if (prev) {
|
||||
SkASSERT(prev->fNext == this);
|
||||
prev->fNext = next;
|
||||
} else {
|
||||
SkASSERT(gAutoDecoderCancel == this);
|
||||
gAutoDecoderCancel = next;
|
||||
}
|
||||
if (next) {
|
||||
SkASSERT(next->fPrev == this);
|
||||
next->fPrev = prev;
|
||||
}
|
||||
|
||||
SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
|
||||
Validate();
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoDecoderCancel::RequestCancel(jobject joptions) {
|
||||
SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
|
||||
|
||||
Validate();
|
||||
|
||||
AutoDecoderCancel* pair = gAutoDecoderCancel;
|
||||
while (pair != NULL) {
|
||||
if (pair->fJOptions == joptions) {
|
||||
pair->fDecoder->cancelDecode();
|
||||
return true;
|
||||
}
|
||||
pair = pair->fNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// can only call this inside a lock on gAutoDecoderCancelMutex
|
||||
void AutoDecoderCancel::Validate() {
|
||||
const int gCount = gAutoDecoderCancelCount;
|
||||
|
||||
if (gCount == 0) {
|
||||
SkASSERT(gAutoDecoderCancel == NULL);
|
||||
} else {
|
||||
SkASSERT(gCount > 0);
|
||||
|
||||
AutoDecoderCancel* curr = gAutoDecoderCancel;
|
||||
SkASSERT(curr);
|
||||
SkASSERT(curr->fPrev == NULL);
|
||||
|
||||
int count = 0;
|
||||
while (curr) {
|
||||
count += 1;
|
||||
SkASSERT(count <= gCount);
|
||||
if (curr->fPrev) {
|
||||
SkASSERT(curr->fPrev->fNext == curr);
|
||||
}
|
||||
if (curr->fNext) {
|
||||
SkASSERT(curr->fNext->fPrev == curr);
|
||||
}
|
||||
curr = curr->fNext;
|
||||
}
|
||||
SkASSERT(count == gCount);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using namespace android;
|
||||
|
||||
class NinePatchPeeker : public SkImageDecoder::Peeker {
|
||||
@ -279,7 +157,7 @@ static inline int32_t validOrNeg1(bool isValid, int32_t value) {
|
||||
return ((int32_t)isValid - 1) | value;
|
||||
}
|
||||
|
||||
static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
|
||||
jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
|
||||
static const struct {
|
||||
SkImageDecoder::Format fFormat;
|
||||
const char* fMimeType;
|
||||
@ -477,7 +355,7 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
|
||||
jobject padding,
|
||||
jobject options) { // BitmapFactory$Options
|
||||
jobject bitmap = NULL;
|
||||
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
|
||||
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
|
||||
|
||||
if (stream) {
|
||||
// for now we don't allow purgeable with java inputstreams
|
||||
@ -682,6 +560,107 @@ static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
|
||||
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
|
||||
int width, height;
|
||||
if (NULL == decoder) {
|
||||
doThrowIOE(env, "Image format not supported");
|
||||
return nullObjectReturn("SkImageDecoder::Factory returned null");
|
||||
}
|
||||
|
||||
JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
|
||||
decoder->setAllocator(javaAllocator);
|
||||
JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
|
||||
decoder->setReporter(javaMemoryReporter);
|
||||
javaAllocator->unref();
|
||||
javaMemoryReporter->unref();
|
||||
|
||||
if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
|
||||
char msg[1024];
|
||||
snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
|
||||
doThrowIOE(env, msg);
|
||||
return nullObjectReturn("decoder->buildTileIndex returned false");
|
||||
}
|
||||
|
||||
SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
|
||||
|
||||
return GraphicsJNI::createLargeBitmap(env, bm);
|
||||
}
|
||||
|
||||
static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
|
||||
int offset, int length, jboolean isShareable) {
|
||||
AutoJavaByteArray ar(env, byteArray);
|
||||
SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
|
||||
SkAutoUnref aur(stream);
|
||||
if (isShareable) {
|
||||
aur.detach();
|
||||
}
|
||||
return doBuildTileIndex(env, stream, isShareable);
|
||||
}
|
||||
|
||||
static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
|
||||
jobject fileDescriptor, jboolean isShareable) {
|
||||
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
|
||||
|
||||
jint descriptor = env->GetIntField(fileDescriptor,
|
||||
gFileDescriptor_descriptor);
|
||||
bool weOwnTheFD = false;
|
||||
|
||||
if (isShareable) {
|
||||
int newFD = ::dup(descriptor);
|
||||
if (-1 != newFD) {
|
||||
weOwnTheFD = true;
|
||||
descriptor = newFD;
|
||||
}
|
||||
}
|
||||
|
||||
SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
|
||||
SkAutoUnref aur(stream);
|
||||
if (!stream->isValid()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (isShareable) {
|
||||
aur.detach();
|
||||
}
|
||||
|
||||
/* Restore our offset when we leave, so we can be called more than once
|
||||
with the same descriptor. This is only required if we didn't dup the
|
||||
file descriptor, but it is OK to do it all the time.
|
||||
*/
|
||||
AutoFDSeek as(descriptor);
|
||||
|
||||
return doBuildTileIndex(env, stream, isShareable);
|
||||
}
|
||||
|
||||
static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
|
||||
jobject is, // InputStream
|
||||
jbyteArray storage, // byte[]
|
||||
jboolean isShareable) {
|
||||
jobject largeBitmap = NULL;
|
||||
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
|
||||
|
||||
if (stream) {
|
||||
// for now we don't allow shareable with java inputstreams
|
||||
largeBitmap = doBuildTileIndex(env, stream, false);
|
||||
stream->unref();
|
||||
}
|
||||
return largeBitmap;
|
||||
}
|
||||
|
||||
static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
|
||||
jint native_asset, // Asset
|
||||
jboolean isShareable) {
|
||||
SkStream* stream;
|
||||
Asset* asset = reinterpret_cast<Asset*>(native_asset);
|
||||
stream = new AssetStreamAdaptor(asset);
|
||||
SkAutoUnref aur(stream);
|
||||
if (isShareable) {
|
||||
aur.detach();
|
||||
}
|
||||
return doBuildTileIndex(env, stream, isShareable);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
@ -711,6 +690,26 @@ static JNINativeMethod gMethods[] = {
|
||||
},
|
||||
|
||||
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
|
||||
|
||||
{ "nativeCreateLargeBitmap",
|
||||
"([BIIZ)Landroid/graphics/LargeBitmap;",
|
||||
(void*)nativeCreateLargeBitmapFromByteArray
|
||||
},
|
||||
|
||||
{ "nativeCreateLargeBitmap",
|
||||
"(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
|
||||
(void*)nativeCreateLargeBitmapFromStream
|
||||
},
|
||||
|
||||
{ "nativeCreateLargeBitmap",
|
||||
"(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
|
||||
(void*)nativeCreateLargeBitmapFromFileDescriptor
|
||||
},
|
||||
|
||||
{ "nativeCreateLargeBitmap",
|
||||
"(IZ)Landroid/graphics/LargeBitmap;",
|
||||
(void*)nativeCreateLargeBitmapFromAsset
|
||||
},
|
||||
};
|
||||
|
||||
static JNINativeMethod gOptionsMethods[] = {
|
||||
|
21
core/jni/android/graphics/BitmapFactory.h
Normal file
21
core/jni/android/graphics/BitmapFactory.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef BitmapFactory_DEFINE
|
||||
#define BitmapFactory_DEFINE
|
||||
|
||||
#include "GraphicsJNI.h"
|
||||
|
||||
extern jclass gOptions_class;
|
||||
extern jfieldID gOptions_justBoundsFieldID;
|
||||
extern jfieldID gOptions_sampleSizeFieldID;
|
||||
extern jfieldID gOptions_configFieldID;
|
||||
extern jfieldID gOptions_ditherFieldID;
|
||||
extern jfieldID gOptions_purgeableFieldID;
|
||||
extern jfieldID gOptions_shareableFieldID;
|
||||
extern jfieldID gOptions_nativeAllocFieldID;
|
||||
extern jfieldID gOptions_widthFieldID;
|
||||
extern jfieldID gOptions_heightFieldID;
|
||||
extern jfieldID gOptions_mimeFieldID;
|
||||
extern jfieldID gOptions_mCancelID;
|
||||
|
||||
jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
|
||||
|
||||
#endif
|
@ -5,6 +5,7 @@
|
||||
|
||||
static jclass gInputStream_Clazz;
|
||||
static jmethodID gInputStream_resetMethodID;
|
||||
static jmethodID gInputStream_markMethodID;
|
||||
static jmethodID gInputStream_availableMethodID;
|
||||
static jmethodID gInputStream_readMethodID;
|
||||
static jmethodID gInputStream_skipMethodID;
|
||||
@ -143,7 +144,7 @@ private:
|
||||
};
|
||||
|
||||
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
jbyteArray storage) {
|
||||
jbyteArray storage, int markSize) {
|
||||
static bool gInited;
|
||||
|
||||
if (!gInited) {
|
||||
@ -153,6 +154,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
|
||||
gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
|
||||
"reset", "()V");
|
||||
gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
|
||||
"mark", "(I)V");
|
||||
gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
|
||||
"available", "()I");
|
||||
gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
|
||||
@ -161,6 +164,7 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
"skip", "(J)J");
|
||||
|
||||
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
|
||||
RETURN_NULL_IF_NULL(gInputStream_markMethodID);
|
||||
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
|
||||
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
|
||||
RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
|
||||
@ -168,6 +172,10 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
gInited = true;
|
||||
}
|
||||
|
||||
if (markSize) {
|
||||
env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
|
||||
}
|
||||
|
||||
return new JavaInputStreamAdaptor(env, stream, storage);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "SkStream.h"
|
||||
|
||||
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
jbyteArray storage);
|
||||
jbyteArray storage, int markSize = 0);
|
||||
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
|
||||
jbyteArray storage);
|
||||
|
||||
|
@ -46,6 +46,10 @@ void doThrowOOME(JNIEnv* env, const char* msg) {
|
||||
doThrow(env, "java/lang/OutOfMemoryError", msg);
|
||||
}
|
||||
|
||||
void doThrowIOE(JNIEnv* env, const char* msg) {
|
||||
doThrow(env, "java/lang/IOException", msg);
|
||||
}
|
||||
|
||||
bool GraphicsJNI::hasException(JNIEnv *env) {
|
||||
if (env->ExceptionCheck() != 0) {
|
||||
LOGE("*** Uncaught exception returned from Java call!\n");
|
||||
@ -165,6 +169,9 @@ static jmethodID gBitmap_allocBufferMethodID;
|
||||
static jclass gBitmapConfig_class;
|
||||
static jfieldID gBitmapConfig_nativeInstanceID;
|
||||
|
||||
static jclass gLargeBitmap_class;
|
||||
static jmethodID gLargeBitmap_constructorMethodID;
|
||||
|
||||
static jclass gCanvas_class;
|
||||
static jfieldID gCanvas_nativeInstanceID;
|
||||
|
||||
@ -370,6 +377,23 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
|
||||
{
|
||||
SkASSERT(bitmap != NULL);
|
||||
|
||||
jobject obj = env->AllocObject(gLargeBitmap_class);
|
||||
if (hasException(env)) {
|
||||
obj = NULL;
|
||||
return obj;
|
||||
}
|
||||
if (obj) {
|
||||
env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
|
||||
if (hasException(env)) {
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
|
||||
{
|
||||
@ -502,6 +526,35 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
|
||||
: fEnv(env), fTotalSize(0) {}
|
||||
|
||||
JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
|
||||
jlong jtotalSize = fTotalSize;
|
||||
fEnv->CallVoidMethod(gVMRuntime_singleton,
|
||||
gVMRuntime_trackExternalFreeMethodID,
|
||||
jtotalSize);
|
||||
}
|
||||
|
||||
bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
|
||||
jlong jsize = memorySize; // the VM wants longs for the size
|
||||
bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
|
||||
gVMRuntime_trackExternalAllocationMethodID,
|
||||
jsize);
|
||||
if (GraphicsJNI::hasException(fEnv)) {
|
||||
return false;
|
||||
}
|
||||
if (!r) {
|
||||
LOGE("VM won't let us allocate %zd bytes\n", memorySize);
|
||||
doThrowOOME(fEnv, "bitmap size exceeds VM budget");
|
||||
return false;
|
||||
}
|
||||
fTotalSize += memorySize;
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static jclass make_globalref(JNIEnv* env, const char classname[])
|
||||
{
|
||||
jclass c = env->FindClass(classname);
|
||||
@ -547,6 +600,9 @@ int register_android_graphics_Graphics(JNIEnv* env)
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
|
||||
"(IZ[BI)V");
|
||||
|
||||
gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
|
||||
gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
|
||||
|
||||
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
|
||||
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
|
||||
"nativeInt", "I");
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "SkPoint.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "../images/SkLargeBitmap.h"
|
||||
#include "../images/SkImageDecoder.h"
|
||||
#include <jni.h>
|
||||
|
||||
class SkCanvas;
|
||||
@ -54,6 +56,8 @@ public:
|
||||
|
||||
static jobject createRegion(JNIEnv* env, SkRegion* region);
|
||||
|
||||
static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
|
||||
|
||||
/** Set a pixelref for the bitmap (needs setConfig to already be called)
|
||||
Returns true on success. If it returns false, then it failed, and the
|
||||
appropriate exception will have been raised.
|
||||
@ -80,6 +84,18 @@ private:
|
||||
bool fReportSizeToVM;
|
||||
};
|
||||
|
||||
class JavaMemoryUsageReporter : public SkVMMemoryReporter {
|
||||
public:
|
||||
JavaMemoryUsageReporter(JNIEnv* env);
|
||||
virtual ~JavaMemoryUsageReporter();
|
||||
// overrides
|
||||
virtual bool reportMemory(size_t memorySize);
|
||||
|
||||
private:
|
||||
JNIEnv* fEnv;
|
||||
size_t fTotalSize;
|
||||
};
|
||||
|
||||
enum JNIAccess {
|
||||
kRO_JNIAccess,
|
||||
kRW_JNIAccess
|
||||
@ -156,6 +172,7 @@ void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument
|
||||
void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime
|
||||
void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State
|
||||
void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory
|
||||
void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
|
||||
|
||||
#define NPE_CHECK_RETURN_ZERO(env, object) \
|
||||
do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
|
||||
|
138
core/jni/android/graphics/LargeBitmap.cpp
Normal file
138
core/jni/android/graphics/LargeBitmap.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#define LOG_TAG "LargeBitmap"
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkImageEncoder.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include "SkDither.h"
|
||||
#include "SkUnPreMultiply.h"
|
||||
#include "SkUtils.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "AutoDecodeCancel.h"
|
||||
#include "SkLargeBitmap.h"
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include "android_util_Binder.h"
|
||||
#include "android_nio_utils.h"
|
||||
#include "CreateJavaOutputStreamAdaptor.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#if 0
|
||||
#define TRACE_BITMAP(code) code
|
||||
#else
|
||||
#define TRACE_BITMAP(code)
|
||||
#endif
|
||||
|
||||
static jobject nullObjectReturn(const char msg[]) {
|
||||
if (msg) {
|
||||
SkDebugf("--- %s\n", msg);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* nine patch not supported
|
||||
*
|
||||
* purgeable not supported
|
||||
* reportSizeToVM not supported
|
||||
*/
|
||||
static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
|
||||
int start_x, int start_y, int width, int height, jobject options) {
|
||||
SkImageDecoder *decoder = bm->getDecoder();
|
||||
int sampleSize = 1;
|
||||
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
|
||||
bool doDither = true;
|
||||
|
||||
if (NULL != options) {
|
||||
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
|
||||
// initialize these, in case we fail later on
|
||||
env->SetIntField(options, gOptions_widthFieldID, -1);
|
||||
env->SetIntField(options, gOptions_heightFieldID, -1);
|
||||
env->SetObjectField(options, gOptions_mimeFieldID, 0);
|
||||
|
||||
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
|
||||
prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
|
||||
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
|
||||
}
|
||||
|
||||
decoder->setDitherImage(doDither);
|
||||
SkBitmap* bitmap = new SkBitmap;
|
||||
SkAutoTDelete<SkBitmap> adb(bitmap);
|
||||
AutoDecoderCancel adc(options, decoder);
|
||||
|
||||
// To fix the race condition in case "requestCancelDecode"
|
||||
// happens earlier than AutoDecoderCancel object is added
|
||||
// to the gAutoDecoderCancelMutex linked list.
|
||||
if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
|
||||
return nullObjectReturn("gOptions_mCancelID");;
|
||||
}
|
||||
|
||||
SkIRect region;
|
||||
region.fLeft = start_x;
|
||||
region.fTop = start_y;
|
||||
region.fRight = start_x + width;
|
||||
region.fBottom = start_y + height;
|
||||
|
||||
if (!bm->decodeRegion(bitmap, region, prefConfig, 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());
|
||||
// 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?
|
||||
env->SetObjectField(options, gOptions_mimeFieldID,
|
||||
getMimeTypeString(env, decoder->getFormat()));
|
||||
}
|
||||
|
||||
// detach bitmap from its autotdeleter, since we want to own it now
|
||||
adb.detach();
|
||||
|
||||
SkPixelRef* pr;
|
||||
pr = bitmap->pixelRef();
|
||||
// promise we will never change our pixels (great for sharing and pictures)
|
||||
pr->setImmutable();
|
||||
// now create the java bitmap
|
||||
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
|
||||
}
|
||||
|
||||
static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
||||
return bm->getHeight();
|
||||
}
|
||||
|
||||
static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
||||
return bm->getWidth();
|
||||
}
|
||||
|
||||
static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
|
||||
delete bm;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
static JNINativeMethod gLargeBitmapMethods[] = {
|
||||
{ "nativeDecodeRegion",
|
||||
"(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
|
||||
(void*)nativeDecodeRegion},
|
||||
{ "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
|
||||
{ "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
|
||||
{ "nativeClean", "(I)V", (void*)nativeClean},
|
||||
};
|
||||
|
||||
#define kClassPathName "android/graphics/LargeBitmap"
|
||||
|
||||
int register_android_graphics_LargeBitmap(JNIEnv* env);
|
||||
int register_android_graphics_LargeBitmap(JNIEnv* env)
|
||||
{
|
||||
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
|
||||
gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* Creates Bitmap objects from various sources, including files, streams,
|
||||
@ -581,6 +582,139 @@ public class BitmapFactory {
|
||||
nativeSetDefaultConfig(config.nativeInt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LargeBitmap from the specified byte array.
|
||||
* Currently only the Jpeg format is supported.
|
||||
*
|
||||
* @param data byte array of compressed image data.
|
||||
* @param offset offset into data for where the decoder should begin
|
||||
* parsing.
|
||||
* @param length the number of bytes, beginning at offset, to parse
|
||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
||||
* shallow reference to the input. If this is false,
|
||||
* then the LargeBitmap will explicitly make a copy of the
|
||||
* input data, and keep that. Even if sharing is allowed,
|
||||
* the implementation may still decide to make a deep
|
||||
* copy of the input data. If an image is progressively encoded,
|
||||
* allowing sharing may degrade the decoding speed.
|
||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
||||
* @throws IOException if the image format is not supported or can not be decoded.
|
||||
* @hide
|
||||
*/
|
||||
public static LargeBitmap createLargeBitmap(byte[] data,
|
||||
int offset, int length, boolean isShareable) throws IOException {
|
||||
if ((offset | length) < 0 || data.length < offset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
return nativeCreateLargeBitmap(data, offset, length, isShareable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LargeBitmap from the file descriptor.
|
||||
* The position within the descriptor will not be changed when
|
||||
* this returns, so the descriptor can be used again as is.
|
||||
* Currently only the Jpeg format is supported.
|
||||
*
|
||||
* @param fd The file descriptor containing the data to decode
|
||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
||||
* shallow reference to the input. If this is false,
|
||||
* then the LargeBitmap will explicitly make a copy of the
|
||||
* input data, and keep that. Even if sharing is allowed,
|
||||
* the implementation may still decide to make a deep
|
||||
* copy of the input data. If an image is progressively encoded,
|
||||
* allowing sharing may degrade the decoding speed.
|
||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
||||
* @throws IOException if the image format is not supported or can not be decoded.
|
||||
* @hide
|
||||
*/
|
||||
public static LargeBitmap createLargeBitmap(
|
||||
FileDescriptor fd, boolean isShareable) throws IOException {
|
||||
if (MemoryFile.isMemoryFile(fd)) {
|
||||
int mappedlength = MemoryFile.getSize(fd);
|
||||
MemoryFile file = new MemoryFile(fd, mappedlength, "r");
|
||||
InputStream is = file.getInputStream();
|
||||
return createLargeBitmap(is, isShareable);
|
||||
}
|
||||
return nativeCreateLargeBitmap(fd, isShareable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LargeBitmap from an input stream.
|
||||
* The stream's position will be where ever it was after the encoded data
|
||||
* was read.
|
||||
* Currently only the Jpeg format is supported.
|
||||
*
|
||||
* @param is The input stream that holds the raw data to be decoded into a
|
||||
* LargeBitmap.
|
||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
||||
* shallow reference to the input. If this is false,
|
||||
* then the LargeBitmap will explicitly make a copy of the
|
||||
* input data, and keep that. Even if sharing is allowed,
|
||||
* the implementation may still decide to make a deep
|
||||
* copy of the input data. If an image is progressively encoded,
|
||||
* allowing sharing may degrade the decoding speed.
|
||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
||||
* @throws IOException if the image format is not supported or can not be decoded.
|
||||
* @hide
|
||||
*/
|
||||
public static LargeBitmap createLargeBitmap(InputStream is,
|
||||
boolean isShareable) throws IOException {
|
||||
// we need mark/reset to work properly in JNI
|
||||
|
||||
if (!is.markSupported()) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
}
|
||||
|
||||
if (is instanceof AssetManager.AssetInputStream) {
|
||||
return nativeCreateLargeBitmap(
|
||||
((AssetManager.AssetInputStream) is).getAssetInt(),
|
||||
isShareable);
|
||||
} else {
|
||||
// pass some temp storage down to the native code. 1024 is made up,
|
||||
// but should be large enough to avoid too many small calls back
|
||||
// into is.read(...).
|
||||
byte [] tempStorage = null;
|
||||
tempStorage = new byte[16 * 1024];
|
||||
return nativeCreateLargeBitmap(is, tempStorage, isShareable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LargeBitmap from a file path.
|
||||
* Currently only the Jpeg format is supported.
|
||||
*
|
||||
* @param pathName complete path name for the file to be decoded.
|
||||
* @param isShareable If this is true, then the LargeBitmap may keep a
|
||||
* shallow reference to the input. If this is false,
|
||||
* then the LargeBitmap will explicitly make a copy of the
|
||||
* input data, and keep that. Even if sharing is allowed,
|
||||
* the implementation may still decide to make a deep
|
||||
* copy of the input data. If an image is progressively encoded,
|
||||
* allowing sharing may degrade the decoding speed.
|
||||
* @return LargeBitmap, or null if the image data could not be decoded.
|
||||
* @throws IOException if the image format is not supported or can not be decoded.
|
||||
* @hide
|
||||
*/
|
||||
public static LargeBitmap createLargeBitmap(String pathName,
|
||||
boolean isShareable) throws FileNotFoundException, IOException {
|
||||
LargeBitmap bm = null;
|
||||
InputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = new FileInputStream(pathName);
|
||||
bm = createLargeBitmap(stream, isShareable);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// do nothing here
|
||||
}
|
||||
}
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
|
||||
private static native void nativeSetDefaultConfig(int nativeConfig);
|
||||
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
|
||||
Rect padding, Options opts);
|
||||
@ -590,5 +724,14 @@ public class BitmapFactory {
|
||||
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
|
||||
int length, Options opts);
|
||||
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
|
||||
|
||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
||||
byte[] data, int offset, int length, boolean isShareable);
|
||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
||||
FileDescriptor fd, boolean isShareable);
|
||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
||||
InputStream is, byte[] storage, boolean isShareable);
|
||||
private static native LargeBitmap nativeCreateLargeBitmap(
|
||||
int asset, boolean isShareable);
|
||||
}
|
||||
|
||||
|
128
graphics/java/android/graphics/LargeBitmap.java
Normal file
128
graphics/java/android/graphics/LargeBitmap.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
package android.graphics;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/**
|
||||
* LargeBitmap can be used to decode a rectangle region from an image.
|
||||
* LargeBimap is particularly useful when an original image is large and
|
||||
* you only need parts of the image.
|
||||
*
|
||||
* To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
|
||||
* Given a LargeBitmap, users can call decodeRegion() repeatedly
|
||||
* to get a decoded Bitmap of the specified region.
|
||||
* @hide
|
||||
*/
|
||||
public final class LargeBitmap {
|
||||
private int mNativeLargeBitmap;
|
||||
private boolean mRecycled;
|
||||
|
||||
/* Private constructor that must received an already allocated native
|
||||
large bitmap int (pointer).
|
||||
|
||||
This can be called from JNI code.
|
||||
*/
|
||||
private LargeBitmap(int lbm) {
|
||||
mNativeLargeBitmap = lbm;
|
||||
mRecycled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a rectangle region in the image specified by rect.
|
||||
*
|
||||
* @param rect The rectangle that specified the region to be decode.
|
||||
* @param opts null-ok; Options that control downsampling.
|
||||
* inPurgeable is not supported.
|
||||
* @return The decoded bitmap, or null if the image data could not be
|
||||
* decoded.
|
||||
*/
|
||||
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
|
||||
checkRecycled("decodeRegion called on recycled large bitmap");
|
||||
if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
|
||||
throw new IllegalArgumentException("rectangle is not inside the image");
|
||||
return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
|
||||
rect.right - rect.left, rect.bottom - rect.top, options);
|
||||
}
|
||||
|
||||
/** Returns the original image's width */
|
||||
public int getWidth() {
|
||||
checkRecycled("getWidth called on recycled large bitmap");
|
||||
return nativeGetWidth(mNativeLargeBitmap);
|
||||
}
|
||||
|
||||
/** Returns the original image's height */
|
||||
public int getHeight() {
|
||||
checkRecycled("getHeight called on recycled large bitmap");
|
||||
return nativeGetHeight(mNativeLargeBitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees up the memory associated with this large bitmap, and mark the
|
||||
* large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
|
||||
* getWidth() or getHeight() is called.
|
||||
* This operation cannot be reversed, so it should only be called if you are
|
||||
* sure there are no further uses for the large bitmap. This is an advanced call,
|
||||
* and normally need not be called, since the normal GC process will free up this
|
||||
* memory when there are no more references to this bitmap.
|
||||
*/
|
||||
public void recycle() {
|
||||
if (!mRecycled) {
|
||||
nativeClean(mNativeLargeBitmap);
|
||||
mRecycled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this large bitmap has been recycled.
|
||||
* If so, then it is an error to try use its method.
|
||||
*
|
||||
* @return true if the large bitmap has been recycled
|
||||
*/
|
||||
public final boolean isRecycled() {
|
||||
return mRecycled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by methods that want to throw an exception if the bitmap
|
||||
* has already been recycled.
|
||||
*/
|
||||
private void checkRecycled(String errorMessage) {
|
||||
if (mRecycled) {
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
protected void finalize() {
|
||||
recycle();
|
||||
}
|
||||
|
||||
private static native Bitmap nativeDecodeRegion(int lbm,
|
||||
int start_x, int start_y, int width, int height,
|
||||
BitmapFactory.Options options);
|
||||
private static native int nativeGetWidth(int lbm);
|
||||
private static native int nativeGetHeight(int lbm);
|
||||
private static native void nativeClean(int lbm);
|
||||
}
|
Reference in New Issue
Block a user