From 0aa39dc2dcfca20f4d9cbeb1699d48a4808f2c70 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 3 Jun 2014 12:19:32 -0400 Subject: Ignore inPurgeable and inInputShareable. These are discouraged anyway, due to the fact that using them may result in decoding on the UI thread. Now that they are ignored, the images will still be decoded; they just will never have their memory purged, meaning there could be a RAM penalty. This is acceptable, since apps are encouraged to instead use inBitmap to manage bitmap memory. This incidentally fixes BUG:15390468 by converting purgeable index8 (e.g. gif) images to non purgeable. Change-Id: Ieaf3ab25d28d93fb94bdaea1eb3bd762f163b09a --- api/current.txt | 4 +- core/jni/AndroidRuntime.cpp | 6 - core/jni/android/graphics/BitmapFactory.cpp | 139 ++++------------------ core/jni/android/graphics/Canvas.cpp | 3 - graphics/java/android/graphics/BitmapFactory.java | 20 +++- 5 files changed, 40 insertions(+), 132 deletions(-) diff --git a/api/current.txt b/api/current.txt index b059841f1dab..17f49787fe12 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10128,13 +10128,13 @@ package android.graphics { field public android.graphics.Bitmap inBitmap; field public int inDensity; field public boolean inDither; - field public boolean inInputShareable; + field public deprecated boolean inInputShareable; field public boolean inJustDecodeBounds; field public boolean inMutable; field public boolean inPreferQualityOverSpeed; field public android.graphics.Bitmap.Config inPreferredConfig; field public boolean inPremultiplied; - field public boolean inPurgeable; + field public deprecated boolean inPurgeable; field public int inSampleSize; field public boolean inScaled; field public int inScreenDensity; diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e06987608c54..2491609089e8 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -29,7 +29,6 @@ #include #include -#include #include "jni.h" #include "JNIHelp.h" @@ -248,11 +247,6 @@ AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) // this sets our preference for 16bit images during decode // in case the src is opaque and 24bit SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config); - // This cache is shared between browser native images, and java "purgeable" - // bitmaps. This globalpool is for images that do not either use the java - // heap, or are not backed by ashmem. See BitmapFactory.cpp for the key - // java call site. - SkImageRef_GlobalPool::SetRAMBudget(512 * 1024); // There is also a global font cache, but its budget is specified in code // see SkFontHost_android.cpp diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 928a7f84507b..5106f0d37f55 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -2,11 +2,8 @@ #include "BitmapFactory.h" #include "NinePatchPeeker.h" -#include "SkData.h" #include "SkFrontBufferedStream.h" #include "SkImageDecoder.h" -#include "SkImageRef_ashmem.h" -#include "SkImageRef_GlobalPool.h" #include "SkMath.h" #include "SkPixelRef.h" #include "SkStream.h" @@ -32,8 +29,6 @@ jfieldID gOptions_configFieldID; jfieldID gOptions_premultipliedFieldID; jfieldID gOptions_mutableFieldID; jfieldID gOptions_ditherFieldID; -jfieldID gOptions_purgeableFieldID; -jfieldID gOptions_shareableFieldID; jfieldID gOptions_preferQualityOverSpeedFieldID; jfieldID gOptions_scaledFieldID; jfieldID gOptions_densityFieldID; @@ -90,14 +85,6 @@ jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) { return jstr; } -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 bool optionsJustBounds(JNIEnv* env, jobject options) { return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID); } @@ -125,27 +112,6 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { } } -static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, - int sampleSize, bool ditherImage) { - - if (kUnknown_SkColorType == bitmap->colorType()) { - ALOGW("bitmap has unknown configuration so no memory has been allocated"); - return NULL; - } - - SkImageRef* pr; - // only use ashmem for large images, since mmaps come at a price - if (bitmap->getSize() >= 32 * 1024) { - pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize); - } else { - pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize); - } - pr->setDitherImage(ditherImage); - bitmap->setPixelRef(pr)->unref(); - pr->isOpaque(bitmap); - return pr; -} - static SkColorType colorTypeForScaledOutput(SkColorType colorType) { switch (colorType) { case kUnknown_SkColorType: @@ -229,21 +195,17 @@ private: const unsigned int mSize; }; -// 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, SkStreamRewindable* stream, jobject padding, - jobject options, bool allowPurgeable, bool forcePurgeable = false) { + jobject options) { int sampleSize = 1; - SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; + SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; float scale = 1.0f; - bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)); bool preferQualityOverSpeed = false; bool requireUnpremultiplied = false; @@ -252,7 +214,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding if (options != NULL) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); if (optionsJustBounds(env, options)) { - mode = SkImageDecoder::kDecodeBounds_Mode; + decodeMode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail later on @@ -280,7 +242,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } const bool willScale = scale != 1.0f; - isPurgeable &= !willScale; SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (decoder == NULL) { @@ -311,8 +272,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding NinePatchPeeker peeker(decoder); decoder->setPeeker(&peeker); - SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode; - JavaPixelAllocator javaAllocator(env); RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); @@ -353,7 +312,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding int scaledWidth = decodingBitmap.width(); int scaledHeight = decodingBitmap.height(); - if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) { + if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f); } @@ -367,7 +326,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } // if we're in justBounds mode, return now (skip the java bitmap) - if (mode == SkImageDecoder::kDecodeBounds_Mode) { + if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) { return NULL; } @@ -459,21 +418,15 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } } - SkPixelRef* pr; - if (isPurgeable) { - pr = installPixelRef(outputBitmap, stream, sampleSize, doDither); - } else { - // if we get here, we're in kDecodePixels_Mode and will therefore - // already have a pixelref installed. - pr = outputBitmap->pixelRef(); - } - if (pr == NULL) { + // if we get here, we're in kDecodePixels_Mode and will therefore + // already have a pixelref installed. + if (outputBitmap->pixelRef() == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) - pr->setImmutable(); + outputBitmap->setImmutable(); } // detach bitmap from its autodeleter, since we want to own it now @@ -512,8 +465,7 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA SkAutoTUnref bufferedStream( SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER)); SkASSERT(bufferedStream.get() != NULL); - // for now we don't allow purgeable with java inputstreams - bitmap = doDecode(env, bufferedStream, padding, options, false, false); + bitmap = doDecode(env, bufferedStream, padding, options); } return bitmap; } @@ -542,76 +494,33 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi SkAutoTUnref fileStream(new SkFILEStream(file, SkFILEStream::kCallerRetains_Ownership)); - SkAutoTUnref stream; - - // Retain the old behavior of allowing purgeable if both purgeable and - // shareable are set to true. - bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions) - && optionsShareable(env, bitmapFactoryOptions); - if (isPurgeable) { - // Copy the stream, so the image can be decoded multiple times without - // continuing to modify the original file descriptor. - // Copy beginning from the current position. - const size_t fileSize = fileStream->getLength() - fileStream->getPosition(); - void* buffer = sk_malloc_flags(fileSize, 0); - if (buffer == NULL) { - return nullObjectReturn("Could not make a copy for ashmem"); - } - - SkAutoTUnref data(SkData::NewFromMalloc(buffer, fileSize)); + // Use a buffered stream. Although an SkFILEStream can be rewound, this + // ensures that SkImageDecoder::Factory never rewinds beyond the + // current position of the file descriptor. + SkAutoTUnref stream(SkFrontBufferedStream::Create(fileStream, + BYTES_TO_BUFFER)); - if (fileStream->read(buffer, fileSize) != fileSize) { - return nullObjectReturn("Could not read the file."); - } - - stream.reset(new SkMemoryStream(data)); - } else { - // Use a buffered stream. Although an SkFILEStream can be rewound, this - // ensures that SkImageDecoder::Factory never rewinds beyond the - // current position of the file descriptor. - stream.reset(SkFrontBufferedStream::Create(fileStream, BYTES_TO_BUFFER)); - } - - return doDecode(env, stream, padding, bitmapFactoryOptions, isPurgeable); + return doDecode(env, stream, padding, bitmapFactoryOptions); } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, jobject padding, jobject options) { - SkStreamRewindable* stream; Asset* asset = reinterpret_cast(native_asset); - bool forcePurgeable = optionsPurgeable(env, options); - if (forcePurgeable) { - // 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 (stream == NULL) { - 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, - AssetStreamAdaptor::kNo_OwnAsset, - AssetStreamAdaptor::kNo_HasMemoryBase); - } - SkAutoUnref aur(stream); - return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable); + // since we know we'll be done with the asset when we return, we can + // just use a simple wrapper + SkAutoTUnref stream(new AssetStreamAdaptor(asset, + AssetStreamAdaptor::kNo_OwnAsset, AssetStreamAdaptor::kNo_HasMemoryBase)); + return doDecode(env, stream, padding, options); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, jint offset, jint 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(), unless we're just decoding bounds. - */ - bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options); AutoJavaByteArray ar(env, byteArray); - SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); + SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false); SkAutoUnref aur(stream); - return doDecode(env, stream, NULL, options, purgeable); + return doDecode(env, stream, NULL, options); } static void nativeRequestCancel(JNIEnv*, jobject joptions) { @@ -675,8 +584,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z"); gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z"); gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z"); - gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z"); - gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z"); gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class, "inPreferQualityOverSpeed", "Z"); gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z"); diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 5fca58220b26..795b966597e1 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -22,7 +22,6 @@ #include "SkDevice.h" #include "SkDrawFilter.h" #include "SkGraphics.h" -#include "SkImageRef_GlobalPool.h" #include "SkPorterDuff.h" #include "SkShader.h" #include "SkTemplates.h" @@ -161,8 +160,6 @@ public: } static void freeCaches(JNIEnv* env, jobject) { - // these are called in no particular order - SkImageRef_GlobalPool::SetRAMUsed(0); SkGraphics::PurgeFontCache(); } diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index c20502f33889..bc20ea512ee8 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -260,7 +260,11 @@ public class BitmapFactory { public boolean inScaled; /** - * If this is set to true, then the resulting bitmap will allocate its + * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this is + * ignored. + * + * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, 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 @@ -287,14 +291,20 @@ public class BitmapFactory { * android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String, * android.graphics.BitmapFactory.Options)}.

*/ + @Deprecated 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. + * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this is + * ignored. + * + * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, 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. */ + @Deprecated public boolean inInputShareable; /** -- cgit v1.2.3-59-g8ed1b