From 4e5ec34e98faa8338ca04282a160ef2b0d13bc9d Mon Sep 17 00:00:00 2001 From: Anton Daubert Date: Mon, 7 Mar 2016 17:30:20 +0100 Subject: [PATCH] Fine scale the decoded result to match the desired sampleSize. Bug: 27097032 Change-Id: Ie15a3116cdd6988524977e5390f6edbac224e502 --- core/jni/android/graphics/BitmapFactory.cpp | 54 ++++++++++++++++----- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 29c1075a3420d..fb9b1e53aa8a9 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -201,6 +201,26 @@ private: const unsigned int mSize; }; +// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize +// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the +// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how +// best to round. +static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) { + if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) { + return true; + } else if ((fullSize / sampleSize + 1) != decodedSize && + (fullSize / sampleSize) != decodedSize) { + return true; + } + return false; +} + +static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, + const int sampleSize) { + return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) || + needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); +} + static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { // This function takes ownership of the input stream. Since the SkAndroidCodec // will take ownership of the stream, we don't necessarily need to take ownership @@ -250,7 +270,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } } } - const bool willScale = scale != 1.0f; // Create the codec. NinePatchPeeker peeker; @@ -269,15 +288,28 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding prefColorType = kN32_SkColorType; } - // Determine the output size and return if the client only wants the size. + // Determine the output size. SkISize size = codec->getSampledDimensions(sampleSize); + + int scaledWidth = size.width(); + int scaledHeight = size.height(); + bool willScale = false; + + // Apply a fine scaling step if necessary. + if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) { + willScale = true; + scaledWidth = codec->getInfo().width() / sampleSize; + scaledHeight = codec->getInfo().height() / sampleSize; + } + + // Set the options and return if the client only wants the size. if (options != NULL) { jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat()); if (env->ExceptionCheck()) { return nullObjectReturn("OOM in encodedFormatToString()"); } - env->SetIntField(options, gOptions_widthFieldID, size.width()); - env->SetIntField(options, gOptions_heightFieldID, size.height()); + env->SetIntField(options, gOptions_widthFieldID, scaledWidth); + env->SetIntField(options, gOptions_heightFieldID, scaledHeight); env->SetObjectField(options, gOptions_mimeFieldID, mimeType); if (onlyDecodeSize) { @@ -285,6 +317,13 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding } } + // Scale is necessary due to density differences. + if (scale != 1.0f) { + willScale = true; + scaledWidth = static_cast(scaledWidth * scale + 0.5f); + scaledHeight = static_cast(scaledHeight * scale + 0.5f); + } + android::Bitmap* reuseBitmap = nullptr; unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { @@ -381,13 +420,6 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding return nullObjectReturn("codec->getAndroidPixels() failed."); } - int scaledWidth = size.width(); - int scaledHeight = size.height(); - if (willScale) { - scaledWidth = int(scaledWidth * scale + 0.5f); - scaledHeight = int(scaledHeight * scale + 0.5f); - } - jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { if (willScale) {