diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-04-27 05:31:19 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-04-27 05:31:19 -0700 |
commit | 1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6 (patch) | |
tree | f09abf0ff05ea93100ed41541371dd18e651493d /core/jni | |
parent | 01be1fa7930e92aac6ba4dd4e8cb75f8719e8b37 (diff) | |
parent | c70e06bbfac0d92ec218a32e35d9d7fa80f23cc9 (diff) | |
download | frameworks_base-1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6.zip frameworks_base-1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6.tar.gz frameworks_base-1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6.tar.bz2 |
Merge change 546 into donut
* changes:
Add (hidden for now) purgeable bitmaps
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 207 |
2 files changed, 155 insertions, 53 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index b37447c..ac35459 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -129,6 +129,7 @@ LOCAL_C_INCLUDES += \ external/skia/include/core \ external/skia/include/effects \ external/skia/include/images \ + external/skia/src/ports \ external/skia/include/utils \ external/sqlite/dist \ external/sqlite/android \ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 4ad2eb0..1fd15d6 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -1,6 +1,8 @@ #define LOG_TAG "BitmapFactory" #include "SkImageDecoder.h" +#include "SkImageRef_ashmem.h" +#include "SkImageRef_GlobalPool.h" #include "SkPixelRef.h" #include "SkStream.h" #include "GraphicsJNI.h" @@ -19,6 +21,8 @@ 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_widthFieldID; static jfieldID gOptions_heightFieldID; static jfieldID gOptions_mimeFieldID; @@ -33,8 +37,6 @@ static jfieldID gFileDescriptor_descriptor; #define TRACE_BITMAP(code) #endif -//#define MIN_SIZE_TO_USE_MMAP (4*1024) - /////////////////////////////////////////////////////////////////////////////// class AutoDecoderCancel { @@ -207,7 +209,11 @@ public: virtual bool rewind() { off_t pos = fAsset->seek(0, SEEK_SET); - return pos != (off_t)-1; + if (pos == (off_t)-1) { + SkDebugf("----- fAsset->seek(rewind) failed\n"); + return false; + } + return true; } virtual size_t read(void* buffer, size_t size) { @@ -222,15 +228,20 @@ public: off_t oldOffset = fAsset->seek(0, SEEK_CUR); if (-1 == oldOffset) { + SkDebugf("---- fAsset->seek(oldOffset) failed\n"); return 0; } off_t newOffset = fAsset->seek(size, SEEK_CUR); if (-1 == newOffset) { + SkDebugf("---- fAsset->seek(%d) failed\n", size); return 0; } amount = newOffset - oldOffset; } else { amount = fAsset->read(buffer, size); + if (amount <= 0) { + SkDebugf("---- fAsset->read(%d) returned %d\n", size, amount); + } } if (amount < 0) { @@ -279,13 +290,46 @@ static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { return jstr; } -static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, - jobject options) { +static bool optionsPurgeable(JNIEnv* env, jobject options) { + return options != NULL && + env->GetBooleanField(options, gOptions_purgeableFieldID); +} + +static bool optionsShareable(JNIEnv* env, jobject options) { + return options != NULL && + env->GetBooleanField(options, gOptions_shareableFieldID); +} + +static jobject nullObjectReturn(const char msg[]) { + if (msg) { + SkDebugf("--- %s\n", msg); + } + return NULL; +} + +static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, + int sampleSize) { + SkPixelRef* pr; + // only use ashmem for large images, since mmaps come at a price + if (bitmap->getSize() >= 32 * 65536) { + pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize); + } else { + pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize); + } + bitmap->setPixelRef(pr)->unref(); + return pr; +} +// since we "may" create a purgeable imageref, we require the stream be ref'able +// i.e. dynamically allocated, since its lifetime may exceed the current stack +// frame. +static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, + jobject options, bool allowPurgeable) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; + bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); @@ -304,14 +348,14 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (NULL == decoder) { - return NULL; + return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); NinePatchPeeker peeker; - JavaPixelAllocator allocator(env); + JavaPixelAllocator javaAllocator(env); SkBitmap* bitmap = new SkBitmap; Res_png_9patch dummy9Patch; @@ -319,23 +363,27 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, SkAutoTDelete<SkBitmap> adb(bitmap); decoder->setPeeker(&peeker); - decoder->setAllocator(&allocator); - + if (!isPurgeable) { + decoder->setAllocator(&javaAllocator); + } + 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) { - if (env->GetBooleanField(options, gOptions_mCancelID)) { - return NULL; - } + if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { + return nullObjectReturn("gOptions_mCancelID");; } - if (!decoder->decode(stream, bitmap, prefConfig, mode)) { - return NULL; + SkImageDecoder::Mode decodeMode = mode; + if (isPurgeable) { + decodeMode = SkImageDecoder::kDecodeBounds_Mode; } - + if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { + return nullObjectReturn("decoder->decode returned false"); + } + // update options (if any) if (NULL != options) { env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); @@ -346,7 +394,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, env->SetObjectField(options, gOptions_mimeFieldID, getMimeTypeString(env, decoder->getFormat())); } - + // if we're in justBounds mode, return now (skip the java bitmap) if (SkImageDecoder::kDecodeBounds_Mode == mode) { return NULL; @@ -357,12 +405,12 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, size_t ninePatchArraySize = peeker.fPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (NULL == ninePatchChunk) { - return NULL; + return nullObjectReturn("ninePatchChunk == null"); } jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); if (NULL == array) { - return NULL; + return nullObjectReturn("primitive array == null"); } peeker.fPatch->serialize(array); env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); @@ -382,12 +430,18 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } - - // promise we will never change our pixels (great for sharing and pictures) - SkPixelRef* ref = bitmap->pixelRef(); - SkASSERT(ref); - ref->setImmutable(); + SkPixelRef* pr; + if (isPurgeable) { + pr = installPixelRef(bitmap, stream, sampleSize); + } else { + // if we get here, we're in kDecodePixels_Mode and will therefore + // already have a pixelref installed. + 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, ninePatchChunk); } @@ -400,7 +454,8 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage); if (stream) { - bitmap = doDecode(env, stream, padding, options); + // for now we don't allow purgeable with java inputstreams + bitmap = doDecode(env, stream, padding, options, false); stream->unref(); } return bitmap; @@ -441,52 +496,96 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jint descriptor = env->GetIntField(fileDescriptor, gFileDescriptor_descriptor); - -#ifdef MIN_SIZE_TO_USE_MMAP - // First try to use mmap - size_t size = getFDSize(descriptor); - if (size >= MIN_SIZE_TO_USE_MMAP) { - void* addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, 0); -// SkDebugf("-------- mmap returned %p %d\n", addr, size); - if (MAP_FAILED != addr) { - SkMemoryStream strm(addr, size); - jobject obj = doDecode(env, &strm, padding, bitmapFactoryOptions); - munmap(addr, size); - return obj; + + bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions); + bool isShareable = optionsShareable(env, bitmapFactoryOptions); + bool weOwnTheFD = false; + if (isPurgeable && isShareable) { + int newFD = ::dup(descriptor); + if (-1 != newFD) { + weOwnTheFD = true; + descriptor = newFD; } } -#endif - // we pass false for closeWhenDone, since the caller owns the descriptor - SkFDStream file(descriptor, false); - if (!file.isValid()) { + SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD); + SkAutoUnref aur(stream); + if (!stream->isValid()) { return NULL; } - - /* Restore our offset when we leave, so the caller doesn't have to. - This is a real feature, so we can be called more than once with the - same descriptor. + + /* 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 doDecode(env, &file, padding, bitmapFactoryOptions); + return doDecode(env, stream, padding, bitmapFactoryOptions, true); +} + +/* make a deep copy of the asset, and return it as a stream, or NULL if there + was an error. + */ +static SkStream* copyAssetToStream(Asset* asset) { + // if we could "ref/reopen" the asset, we may not need to copy it here + off_t size = asset->seek(0, SEEK_SET); + if ((off_t)-1 == size) { + SkDebugf("---- copyAsset: asset rewind failed\n"); + return NULL; + } + + size = asset->getLength(); + if (size <= 0) { + SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); + return NULL; + } + + SkStream* stream = new SkMemoryStream(size); + void* data = const_cast<void*>(stream->getMemoryBase()); + off_t len = asset->read(data, size); + if (len != size) { + SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); + delete stream; + stream = NULL; + } + return stream; } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset, // Asset jobject padding, // Rect jobject options) { // BitmapFactory$Options - AssetStreamAdaptor mystream((Asset*)native_asset); - - return doDecode(env, &mystream, padding, options); + SkStream* stream; + Asset* asset = reinterpret_cast<Asset*>(native_asset); + + if (optionsPurgeable(env, options)) { + // if we could "ref/reopen" the asset, we may not need to copy it here + // and we could assume optionsShareable, since assets are always RO + stream = copyAssetToStream(asset); + if (NULL == stream) { + return NULL; + } + } else { + // since we know we'll be done with the asset when we return, we can + // just use a simple wrapper + stream = new AssetStreamAdaptor(asset); + } + SkAutoUnref aur(stream); + return doDecode(env, stream, padding, options, true); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, int offset, int length, jobject options) { - AutoJavaByteArray ar(env, byteArray); - SkMemoryStream stream(ar.ptr() + offset, length); - - return doDecode(env, &stream, NULL, options); + /* If optionsShareable() we could decide to just wrap the java array and + share it, but that means adding a globalref to the java array object + and managing its lifetime. For now we just always copy the array's data + if optionsPurgeable(). + */ + AutoJavaByteArray ar(env, byteArray); + SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, + optionsPurgeable(env, options)); + SkAutoUnref aur(stream); + return doDecode(env, stream, NULL, options, true); } static void nativeRequestCancel(JNIEnv*, jobject joptions) { @@ -595,6 +694,8 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_configFieldID = getFieldIDCheck(env, gOptions_class, "inPreferredConfig", "Landroid/graphics/Bitmap$Config;"); gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z"); + gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z"); + gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z"); gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I"); gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I"); gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;"); |