summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Guy <romainguy@google.com>2012-03-19 18:52:09 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-03-19 18:52:09 -0700
commit00843227504528584596da305e6587450c0c5223 (patch)
tree85b310040e7ff0dcf6227f4631960d5f59754db9
parentf1bdf65308af0ced783ffffd60b841a6bc0da970 (diff)
parent7b2f8b8fb7064a1d3b6d942b978c30c24c9d7299 (diff)
downloadframeworks_base-00843227504528584596da305e6587450c0c5223.zip
frameworks_base-00843227504528584596da305e6587450c0c5223.tar.gz
frameworks_base-00843227504528584596da305e6587450c0c5223.tar.bz2
Merge "Pre-scale bitmaps on the native heap"
-rw-r--r--core/java/android/view/ViewRootImpl.java4
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp256
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java65
3 files changed, 221 insertions, 104 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 04ad649..4eb70ab 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -841,7 +841,9 @@ public final class ViewRootImpl implements ViewParent,
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
- localDirty.intersect(0, 0, mWidth, mHeight);
+ final float appScale = mAttachInfo.mApplicationScale;
+ localDirty.intersect(0, 0,
+ (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!mWillDrawSoon) {
scheduleTraversals();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index c9eb640..dcd1d28 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -90,8 +90,54 @@ static bool optionsJustBounds(JNIEnv* env, jobject options) {
return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
}
+static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
+ chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
+ chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
+ chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
+ chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
+
+ for (int i = 0; i < chunk->numXDivs; i++) {
+ chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
+ if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
+ chunk->xDivs[i]++;
+ }
+ }
+
+ for (int i = 0; i < chunk->numYDivs; i++) {
+ chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
+ if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
+ chunk->yDivs[i]++;
+ }
+ }
+}
+
+static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
+ jobject padding) {
+
+ jbyte* array = env->GetByteArrayElements(chunkObject, 0);
+ if (array != NULL) {
+ size_t chunkSize = env->GetArrayLength(chunkObject);
+ void* storage = alloca(chunkSize);
+ android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
+ memcpy(chunk, array, chunkSize);
+ android::Res_png_9patch::deserialize(chunk);
+
+ scaleNinePatchChunk(chunk, scale);
+ memcpy(array, chunk, chunkSize);
+
+ if (padding) {
+ GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
+ chunk->paddingRight, chunk->paddingBottom);
+ }
+
+ env->ReleaseByteArrayElements(chunkObject, array, 0);
+ }
+ return chunkObject;
+}
+
static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
- int sampleSize, bool ditherImage) {
+ int sampleSize, bool ditherImage) {
+
SkImageRef* pr;
// only use ashmem for large images, since mmaps come at a price
if (bitmap->getSize() >= 32 * 1024) {
@@ -109,22 +155,29 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
// 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,
- bool forcePurgeable = false) {
+ jobject options, bool allowPurgeable, bool forcePurgeable = false,
+ bool applyScale = false, float scale = 1.0f) {
+
int sampleSize = 1;
+
SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+
bool doDither = true;
bool isMutable = false;
- bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
+ bool willScale = applyScale && scale != 1.0f;
+ bool isPurgeable = !willScale &&
+ (forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)));
bool preferQualityOverSpeed = false;
+
jobject javaBitmap = NULL;
- if (NULL != options) {
+ if (options != NULL) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
if (optionsJustBounds(env, options)) {
mode = SkImageDecoder::kDecodeBounds_Mode;
}
+
// initialize these, in case we fail later on
env->SetIntField(options, gOptions_widthFieldID, -1);
env->SetIntField(options, gOptions_heightFieldID, -1);
@@ -139,8 +192,12 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
}
+ if (willScale && javaBitmap != NULL) {
+ return nullObjectReturn("Cannot pre-scale a reused bitmap");
+ }
+
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
- if (NULL == decoder) {
+ if (decoder == NULL) {
return nullObjectReturn("SkImageDecoder::Factory returned null");
}
@@ -148,35 +205,35 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
decoder->setDitherImage(doDither);
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
- NinePatchPeeker peeker(decoder);
- JavaPixelAllocator javaAllocator(env);
- SkBitmap* bitmap;
+ NinePatchPeeker peeker(decoder);
+ JavaPixelAllocator javaAllocator(env);
+
+ SkBitmap* bitmap;
if (javaBitmap == NULL) {
bitmap = new SkBitmap;
} else {
if (sampleSize != 1) {
return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
}
- bitmap = (SkBitmap *) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
+ bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
// config of supplied bitmap overrules config set in options
prefConfig = bitmap->getConfig();
}
- Res_png_9patch dummy9Patch;
- SkAutoTDelete<SkImageDecoder> add(decoder);
- SkAutoTDelete<SkBitmap> adb(bitmap, (javaBitmap == NULL));
+ SkAutoTDelete<SkImageDecoder> add(decoder);
+ SkAutoTDelete<SkBitmap> adb(bitmap, javaBitmap == NULL);
decoder->setPeeker(&peeker);
if (!isPurgeable) {
decoder->setAllocator(&javaAllocator);
}
- AutoDecoderCancel adc(options, decoder);
+ 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)) {
+ if (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) {
return nullObjectReturn("gOptions_mCancelID");
}
@@ -184,38 +241,57 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
if (isPurgeable) {
decodeMode = SkImageDecoder::kDecodeBounds_Mode;
}
- if (!decoder->decode(stream, bitmap, prefConfig, decodeMode, javaBitmap != NULL)) {
+
+ SkBitmap* decoded;
+ if (willScale) {
+ decoded = new SkBitmap;
+ } else {
+ decoded = bitmap;
+ }
+ SkAutoTDelete<SkBitmap> adb2(willScale ? decoded : NULL);
+
+ if (!decoder->decode(stream, decoded, prefConfig, decodeMode, javaBitmap != NULL)) {
return nullObjectReturn("decoder->decode returned false");
}
+ int scaledWidth = decoded->width();
+ int scaledHeight = decoded->height();
+
+ if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
+ scaledWidth = int(scaledWidth * scale + 0.5f);
+ scaledHeight = int(scaledHeight * scale + 0.5f);
+ }
+
// 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?
+ if (options != NULL) {
+ env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
+ env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
env->SetObjectField(options, gOptions_mimeFieldID,
- getMimeTypeString(env, decoder->getFormat()));
+ getMimeTypeString(env, decoder->getFormat()));
}
// if we're in justBounds mode, return now (skip the java bitmap)
- if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ if (mode == SkImageDecoder::kDecodeBounds_Mode) {
return NULL;
}
jbyteArray ninePatchChunk = NULL;
if (peeker.fPatchIsValid) {
+ if (willScale) {
+ scaleNinePatchChunk(peeker.fPatch, scale);
+ }
+
size_t ninePatchArraySize = peeker.fPatch->serializedSize();
ninePatchChunk = env->NewByteArray(ninePatchArraySize);
- if (NULL == ninePatchChunk) {
+ if (ninePatchChunk == NULL) {
return nullObjectReturn("ninePatchChunk == null");
}
- jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk,
- NULL);
- if (NULL == array) {
+
+ jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL);
+ if (array == NULL) {
return nullObjectReturn("primitive array == null");
}
+
peeker.fPatch->serialize(array);
env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
}
@@ -223,13 +299,32 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
// detach bitmap from its autodeleter, since we want to own it now
adb.detach();
+ if (willScale) {
+ // This is weird so let me explain: we could use the scale parameter
+ // directly, but for historical reasons this is how the corresponding
+ // Dalvik code has always behaved. We simply recreate the behavior here.
+ // The result is slightly different from simply using scale because of
+ // the 0.5f rounding bias applied when computing the target image size
+ const float sx = scaledWidth / float(decoded->width());
+ const float sy = scaledHeight / float(decoded->height());
+
+ bitmap->setConfig(decoded->getConfig(), scaledWidth, scaledHeight);
+ bitmap->allocPixels(&javaAllocator, NULL);
+ bitmap->eraseColor(0);
+
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+
+ SkCanvas canvas(*bitmap);
+ canvas.scale(sx, sy);
+ canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint);
+ }
+
if (padding) {
if (peeker.fPatchIsValid) {
GraphicsJNI::set_jrect(env, padding,
- peeker.fPatch->paddingLeft,
- peeker.fPatch->paddingTop,
- peeker.fPatch->paddingRight,
- peeker.fPatch->paddingBottom);
+ peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
+ peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
} else {
GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
}
@@ -258,22 +353,26 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
isMutable, ninePatchChunk);
}
-static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
- jobject is, // InputStream
- jbyteArray storage, // byte[]
- jobject padding,
- jobject options) { // BitmapFactory$Options
+static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
+ jobject padding, jobject options, jboolean applyScale, jfloat scale) {
+
jobject bitmap = NULL;
SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
- bitmap = doDecode(env, stream, padding, options, false);
+ bitmap = doDecode(env, stream, padding, options, false, false, applyScale, scale);
stream->unref();
}
return bitmap;
}
+static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
+ jobject padding, jobject options) {
+
+ return nativeDecodeStreamScaled(env, clazz, is, storage, padding, options, false, 1.0f);
+}
+
static ssize_t getFDSize(int fd) {
off64_t curr = ::lseek64(fd, 0, SEEK_CUR);
if (curr < 0) {
@@ -284,10 +383,9 @@ static ssize_t getFDSize(int fd) {
return size;
}
-static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
- jobject fileDescriptor,
- jobject padding,
- jobject bitmapFactoryOptions) {
+static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
+ jobject padding, jobject bitmapFactoryOptions) {
+
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
@@ -349,10 +447,9 @@ static SkStream* copyAssetToStream(Asset* asset) {
return stream;
}
-static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
- jint native_asset, // Asset
- jobject padding, // Rect
- jobject options) { // BitmapFactory$Options
+static jobject nativeDecodeAssetScaled(JNIEnv* env, jobject clazz, jint native_asset,
+ jobject padding, jobject options, jboolean applyScale, jfloat scale) {
+
SkStream* stream;
Asset* asset = reinterpret_cast<Asset*>(native_asset);
bool forcePurgeable = optionsPurgeable(env, options);
@@ -360,7 +457,7 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
// 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) {
+ if (stream == NULL) {
return NULL;
}
} else {
@@ -369,18 +466,24 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz,
stream = new AssetStreamAdaptor(asset);
}
SkAutoUnref aur(stream);
- return doDecode(env, stream, padding, options, true, forcePurgeable);
+ return doDecode(env, stream, padding, options, true, forcePurgeable, applyScale, scale);
+}
+
+static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset,
+ jobject padding, jobject options) {
+
+ return nativeDecodeAssetScaled(env, clazz, native_asset, padding, options, false, 1.0f);
}
static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
- int offset, int length, jobject options) {
+ 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(), unless we're just decoding bounds.
*/
- bool purgeable = optionsPurgeable(env, options)
- && !optionsJustBounds(env, options);
+ bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
AutoJavaByteArray ar(env, byteArray);
SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
SkAutoUnref aur(stream);
@@ -391,48 +494,6 @@ static void nativeRequestCancel(JNIEnv*, jobject joptions) {
(void)AutoDecoderCancel::RequestCancel(joptions);
}
-static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
- jobject padding) {
-
- jbyte* array = env->GetByteArrayElements(chunkObject, 0);
- if (array != NULL) {
- size_t chunkSize = env->GetArrayLength(chunkObject);
- void* storage = alloca(chunkSize);
- android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
- memcpy(chunk, array, chunkSize);
- android::Res_png_9patch::deserialize(chunk);
-
- chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
- chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
- chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
- chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
-
- for (int i = 0; i < chunk->numXDivs; i++) {
- chunk->xDivs[i] = int(chunk->xDivs[i] * scale + 0.5f);
- if (i > 0 && chunk->xDivs[i] == chunk->xDivs[i - 1]) {
- chunk->xDivs[i]++;
- }
- }
-
- for (int i = 0; i < chunk->numYDivs; i++) {
- chunk->yDivs[i] = int(chunk->yDivs[i] * scale + 0.5f);
- if (i > 0 && chunk->yDivs[i] == chunk->yDivs[i - 1]) {
- chunk->yDivs[i]++;
- }
- }
-
- memcpy(array, chunk, chunkSize);
-
- if (padding) {
- GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
- chunk->paddingRight, chunk->paddingBottom);
- }
-
- env->ReleaseByteArrayElements(chunkObject, array, 0);
- }
- return chunkObject;
-}
-
static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
@@ -445,6 +506,10 @@ static JNINativeMethod gMethods[] = {
"(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeStream
},
+ { "nativeDecodeStream",
+ "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeStreamScaled
+ },
{ "nativeDecodeFileDescriptor",
"(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
@@ -456,6 +521,11 @@ static JNINativeMethod gMethods[] = {
(void*)nativeDecodeAsset
},
+ { "nativeDecodeAsset",
+ "(ILandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;ZF)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeAssetScaled
+ },
+
{ "nativeDecodeByteArray",
"([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
(void*)nativeDecodeByteArray
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index f0d1643..c5705f6 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -32,6 +32,8 @@ import java.io.InputStream;
* and byte-arrays.
*/
public class BitmapFactory {
+ private static final int DECODE_BUFFER_SIZE = 16 * 1024;
+
public static class Options {
/**
* Create a default Options object, which if left unchanged will give
@@ -469,7 +471,7 @@ public class BitmapFactory {
// we need mark/reset to work properly
if (!is.markSupported()) {
- is = new BufferedInputStream(is, 16 * 1024);
+ is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
}
// so we can call reset() if a given codec gives up after reading up to
@@ -477,11 +479,30 @@ public class BitmapFactory {
// value should be.
is.mark(1024);
- Bitmap bm;
+ Bitmap bm;
+ boolean finish = true;
if (is instanceof AssetManager.AssetInputStream) {
- bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(),
- outPadding, opts);
+ final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
+
+ if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
+ float scale = 1.0f;
+ int targetDensity = 0;
+ if (opts != null) {
+ final int density = opts.inDensity;
+ targetDensity = opts.inTargetDensity;
+ if (density != 0 && targetDensity != 0) {
+ scale = targetDensity / (float) density;
+ }
+ }
+
+ bm = nativeDecodeAsset(asset, outPadding, opts, true, scale);
+ if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
+
+ finish = false;
+ } else {
+ bm = nativeDecodeAsset(asset, outPadding, opts);
+ }
} else {
// pass some temp storage down to the native code. 1024 is made up,
// but should be large enough to avoid too many small calls back
@@ -490,13 +511,32 @@ public class BitmapFactory {
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
if (tempStorage == null) tempStorage = new byte[16 * 1024];
- bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
+
+ if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
+ float scale = 1.0f;
+ int targetDensity = 0;
+ if (opts != null) {
+ final int density = opts.inDensity;
+ targetDensity = opts.inTargetDensity;
+ if (density != 0 && targetDensity != 0) {
+ scale = targetDensity / (float) density;
+ }
+ }
+
+ bm = nativeDecodeStream(is, tempStorage, outPadding, opts, true, scale);
+ if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
+
+ finish = false;
+ } else {
+ bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
+ }
}
+
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
- return finishDecode(bm, outPadding, opts);
+ return finish ? finishDecode(bm, outPadding, opts) : bm;
}
private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
@@ -524,12 +564,13 @@ public class BitmapFactory {
bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
(int) (bm.getHeight() * scale + 0.5f), true);
if (bm != oldBitmap) oldBitmap.recycle();
- }
- if (isNinePatch) {
- if (scale != 1.0f) np = nativeScaleNinePatch(np, scale, outPadding);
- bm.setNinePatchChunk(np);
+ if (isNinePatch) {
+ np = nativeScaleNinePatch(np, scale, outPadding);
+ bm.setNinePatchChunk(np);
+ }
}
+
bm.setDensity(targetDensity);
}
@@ -597,9 +638,13 @@ public class BitmapFactory {
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
+ private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
+ Rect padding, Options opts, boolean applyScale, float scale);
private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
Rect padding, Options opts);
private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts);
+ private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts,
+ boolean applyScale, float scale);
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);