summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAndroid (Google) Code Review <android-gerrit@google.com>2009-04-29 13:35:29 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-04-29 13:35:29 -0700
commitcb49634c7373e3b48bb73e516331901525af36c0 (patch)
tree5c39a9c1f870ccc3bb870711aa14cfcff43797a6 /core
parent8c4f42d22958006660fdd60f41957230cdb2e77e (diff)
parent1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6 (diff)
downloadframeworks_base-cb49634c7373e3b48bb73e516331901525af36c0.zip
frameworks_base-cb49634c7373e3b48bb73e516331901525af36c0.tar.gz
frameworks_base-cb49634c7373e3b48bb73e516331901525af36c0.tar.bz2
am 1fb758e: Merge change 546 into donut
Merge commit '1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6' * commit '1fb758e94b5b9e342b6dc6452cb5bd7cf0cc4ed6': Add (hidden for now) purgeable bitmaps
Diffstat (limited to 'core')
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp207
2 files changed, 155 insertions, 53 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bb53315..de64e84 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -128,6 +128,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;");