Loading core/jni/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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 \ Loading core/jni/android/graphics/BitmapFactory.cpp +154 −53 Original line number Diff line number Diff line #define LOG_TAG "BitmapFactory" #include "SkImageDecoder.h" #include "SkImageRef_ashmem.h" #include "SkImageRef_GlobalPool.h" #include "SkPixelRef.h" #include "SkStream.h" #include "GraphicsJNI.h" Loading @@ -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; Loading @@ -33,8 +37,6 @@ static jfieldID gFileDescriptor_descriptor; #define TRACE_BITMAP(code) #endif //#define MIN_SIZE_TO_USE_MMAP (4*1024) /////////////////////////////////////////////////////////////////////////////// class AutoDecoderCancel { Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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; Loading @@ -319,21 +363,25 @@ 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) Loading @@ -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); Loading @@ -383,11 +431,17 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, } } 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) SkPixelRef* ref = bitmap->pixelRef(); SkASSERT(ref); ref->setImmutable(); pr->setImmutable(); // now create the java bitmap return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); } Loading @@ -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; Loading Loading @@ -442,51 +497,95 @@ 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) { /* 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); SkMemoryStream stream(ar.ptr() + offset, length); return doDecode(env, &stream, NULL, options); 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) { Loading Loading @@ -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;"); Loading graphics/java/android/graphics/BitmapFactory.java +30 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,36 @@ public class BitmapFactory { */ public boolean inScaled; /** * If this is set to true, then the resulting bitmap will allocate its * pixels such that they can be purged if the system needs to reclaim * memory. In that instance, when the pixels need to be accessed again * (e.g. the bitmap is drawn, getPixels() is called), they will be * automatically re-decoded. * * For the re-decode to happen, the bitmap must have access to the * encoded data, either by sharing a reference to the input * or by making a copy of it. This distinction is controlled by * inInputShareable. If this is true, then the bitmap may keep a shallow * reference to the input. If this is false, then the bitmap 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. * * @hide pending API council approval */ public boolean inPurgeable; /** * This field works in conjuction with inPurgeable. If inPurgeable is * false, then this field is ignored. If inPurgeable is true, then this * field determines whether the bitmap can share a reference to the * input data (inputstream, array, etc.) or if it must make a deep copy. * * @hide pending API council approval */ public boolean inInputShareable; /** * The resulting width of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, Loading Loading
core/jni/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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 \ Loading
core/jni/android/graphics/BitmapFactory.cpp +154 −53 Original line number Diff line number Diff line #define LOG_TAG "BitmapFactory" #include "SkImageDecoder.h" #include "SkImageRef_ashmem.h" #include "SkImageRef_GlobalPool.h" #include "SkPixelRef.h" #include "SkStream.h" #include "GraphicsJNI.h" Loading @@ -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; Loading @@ -33,8 +37,6 @@ static jfieldID gFileDescriptor_descriptor; #define TRACE_BITMAP(code) #endif //#define MIN_SIZE_TO_USE_MMAP (4*1024) /////////////////////////////////////////////////////////////////////////////// class AutoDecoderCancel { Loading Loading @@ -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) { Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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; Loading @@ -319,21 +363,25 @@ 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) Loading @@ -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); Loading @@ -383,11 +431,17 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, } } 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) SkPixelRef* ref = bitmap->pixelRef(); SkASSERT(ref); ref->setImmutable(); pr->setImmutable(); // now create the java bitmap return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); } Loading @@ -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; Loading Loading @@ -442,51 +497,95 @@ 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) { /* 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); SkMemoryStream stream(ar.ptr() + offset, length); return doDecode(env, &stream, NULL, options); 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) { Loading Loading @@ -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;"); Loading
graphics/java/android/graphics/BitmapFactory.java +30 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,36 @@ public class BitmapFactory { */ public boolean inScaled; /** * If this is set to true, then the resulting bitmap will allocate its * pixels such that they can be purged if the system needs to reclaim * memory. In that instance, when the pixels need to be accessed again * (e.g. the bitmap is drawn, getPixels() is called), they will be * automatically re-decoded. * * For the re-decode to happen, the bitmap must have access to the * encoded data, either by sharing a reference to the input * or by making a copy of it. This distinction is controlled by * inInputShareable. If this is true, then the bitmap may keep a shallow * reference to the input. If this is false, then the bitmap 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. * * @hide pending API council approval */ public boolean inPurgeable; /** * This field works in conjuction with inPurgeable. If inPurgeable is * false, then this field is ignored. If inPurgeable is true, then this * field determines whether the bitmap can share a reference to the * input data (inputstream, array, etc.) or if it must make a deep copy. * * @hide pending API council approval */ public boolean inInputShareable; /** * The resulting width of the bitmap, set independent of the state of * inJustDecodeBounds. However, if there is an error trying to decode, Loading