diff options
author | Joseph Wen <josephwen@google.com> | 2010-08-17 00:35:06 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-08-17 00:35:06 -0700 |
commit | 1b10d3d23512f9f9a091e1f4c27bb3dc47806f6c (patch) | |
tree | 2e6419775f3744b86ba0c4c762694376629b3bbe /core/jni | |
parent | 931831dcb8880e6b591e616467f64a6cc999d3ab (diff) | |
parent | 81dcea6093dfcdadd52982505249a5eacf47a81b (diff) | |
download | frameworks_base-1b10d3d23512f9f9a091e1f4c27bb3dc47806f6c.zip frameworks_base-1b10d3d23512f9f9a091e1f4c27bb3dc47806f6c.tar.gz frameworks_base-1b10d3d23512f9f9a091e1f4c27bb3dc47806f6c.tar.bz2 |
am 81dcea60: am f1f48bc7: Do JPEG tile-based decoding.
Merge commit '81dcea6093dfcdadd52982505249a5eacf47a81b'
* commit '81dcea6093dfcdadd52982505249a5eacf47a81b':
Do JPEG tile-based decoding.
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 2 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/AutoDecodeCancel.cpp | 100 | ||||
-rw-r--r-- | core/jni/android/graphics/AutoDecodeCancel.h | 27 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 275 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.h | 21 | ||||
-rw-r--r-- | core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp | 10 | ||||
-rw-r--r-- | core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 56 | ||||
-rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 17 | ||||
-rw-r--r-- | core/jni/android/graphics/LargeBitmap.cpp | 138 |
11 files changed, 510 insertions, 140 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 860b5b7..cb0bdd3 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -82,6 +82,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 \ @@ -105,6 +106,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 \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 2dd17bb..dba1cea 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.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); @@ -1256,6 +1257,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), diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp new file mode 100644 index 0000000..f0739ea --- /dev/null +++ b/core/jni/android/graphics/AutoDecodeCancel.cpp @@ -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 diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h new file mode 100644 index 0000000..37b86f9 --- /dev/null +++ b/core/jni/android/graphics/AutoDecodeCancel.h @@ -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 diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index b41bad0..21b2e3b 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -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[] = { diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h new file mode 100644 index 0000000..f868434 --- /dev/null +++ b/core/jni/android/graphics/BitmapFactory.h @@ -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 diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 007757f..137acc6 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -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); } diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h index cf21dde..c34c96a 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h @@ -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); diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 5659ba2..204bb74 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -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"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index fe24b05..8d6528b 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -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) diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp new file mode 100644 index 0000000..4cf5dfa --- /dev/null +++ b/core/jni/android/graphics/LargeBitmap.cpp @@ -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)); +} + |