Merge "Support all formats for BitmapFactory.Options.inBitmap"
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index d6549a1..38dee1b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -152,6 +152,33 @@
     return pr;
 }
 
+class RecyclingPixelAllocator : public SkBitmap::Allocator {
+public:
+    RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
+        : mPixelRef(pixelRef), mSize(size) {
+        SkSafeRef(mPixelRef);
+    }
+
+    ~RecyclingPixelAllocator() {
+        SkSafeUnref(mPixelRef);
+    }
+
+    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+        if (!bitmap->getSize64().is32() || bitmap->getSize() > mSize) {
+            ALOGW("bitmap marked for reuse (%d bytes) too small to contain new bitmap (%d bytes)",
+                    bitmap->getSize(), mSize);
+            return false;
+        }
+        bitmap->setPixelRef(mPixelRef);
+        bitmap->lockPixels();
+        return true;
+    }
+
+private:
+    SkPixelRef* const mPixelRef;
+    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.
@@ -193,6 +220,7 @@
         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
     }
 
+    // TODO: allow scaling with reuse, ideally avoiding decode in not-enough-space condition
     if (willScale && javaBitmap != NULL) {
         return nullObjectReturn("Cannot pre-scale a reused bitmap");
     }
@@ -206,34 +234,35 @@
     decoder->setDitherImage(doDither);
     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
 
-    NinePatchPeeker peeker(decoder);
-    JavaPixelAllocator javaAllocator(env);
-
-    SkBitmap* bitmap;
-    bool useExistingBitmap = false;
+    SkBitmap* outputBitmap = NULL;
     unsigned int existingBufferSize = 0;
-    if (javaBitmap == NULL) {
-        bitmap = new SkBitmap;
-    } else {
-        bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
-        // only reuse the provided bitmap if it is mutable
-        if (!bitmap->isImmutable()) {
-            useExistingBitmap = true;
-            // config of supplied bitmap overrules config set in options
-            prefConfig = bitmap->getConfig();
-            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
-        } else {
+    if (javaBitmap != NULL) {
+        outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
+        if (outputBitmap->isImmutable()) {
             ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
-            bitmap = new SkBitmap;
+            javaBitmap = NULL;
+            outputBitmap = NULL;
+        } else {
+            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
         }
     }
 
-    SkAutoTDelete<SkImageDecoder> add(decoder);
-    SkAutoTDelete<SkBitmap> adb(!useExistingBitmap ? bitmap : NULL);
+    SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
+    if (outputBitmap == NULL) outputBitmap = adb.get();
 
+    SkAutoTDelete<SkImageDecoder> add(decoder);
+
+    NinePatchPeeker peeker(decoder);
     decoder->setPeeker(&peeker);
-    if (!isPurgeable) {
-        decoder->setAllocator(&javaAllocator);
+    JavaPixelAllocator javaAllocator(env);
+    RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
+
+    // allocator to be used for final allocation associated with output
+    SkBitmap::Allocator* allocator = (javaBitmap != NULL) ?
+            (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
+
+    if (!isPurgeable && !willScale) {
+        decoder->setAllocator(allocator);
     }
 
     AutoDecoderCancel adc(options, decoder);
@@ -245,45 +274,15 @@
         return nullObjectReturn("gOptions_mCancelID");
     }
 
-    SkImageDecoder::Mode decodeMode = mode;
-    if (isPurgeable) {
-        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
-    }
+    SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
 
-    if (javaBitmap != NULL) {
-        // If we're reusing the pixelref from an existing bitmap, decode the bounds and
-        // reinitialize the native object for the new content, keeping the pixelRef
-        SkPixelRef* pixelRef = bitmap->pixelRef();
-        SkSafeRef(pixelRef);
-
-        SkBitmap boundsBitmap;
-        decoder->decode(stream, &boundsBitmap, prefConfig, SkImageDecoder::kDecodeBounds_Mode);
-        stream->rewind();
-
-        if (boundsBitmap.getSize() > existingBufferSize) {
-            return nullObjectReturn("bitmap marked for reuse too small to contain decoded data");
-        }
-
-        bitmap->setConfig(boundsBitmap.config(), boundsBitmap.width(), boundsBitmap.height(), 0);
-        bitmap->setPixelRef(pixelRef);
-        SkSafeUnref(pixelRef);
-        GraphicsJNI::reinitBitmap(env, javaBitmap);
-    }
-
-    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)) {
+    SkBitmap decodingBitmap;
+    if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) {
         return nullObjectReturn("decoder->decode returned false");
     }
 
-    int scaledWidth = decoded->width();
-    int scaledHeight = decoded->height();
+    int scaledWidth = decodingBitmap.width();
+    int scaledHeight = decodingBitmap.height();
 
     if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
         scaledWidth = int(scaledWidth * scale + 0.5f);
@@ -351,10 +350,10 @@
         // 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());
+        const float sx = scaledWidth / float(decodingBitmap.width());
+        const float sy = scaledHeight / float(decodingBitmap.height());
 
-        SkBitmap::Config config = decoded->config();
+        SkBitmap::Config config = decodingBitmap.config();
         switch (config) {
             case SkBitmap::kNo_Config:
             case SkBitmap::kIndex8_Config:
@@ -365,19 +364,21 @@
                 break;
         }
 
-        bitmap->setConfig(config, scaledWidth, scaledHeight);
-        bitmap->setIsOpaque(decoded->isOpaque());
-        if (!bitmap->allocPixels(&javaAllocator, NULL)) {
+        outputBitmap->setConfig(config, scaledWidth, scaledHeight);
+        outputBitmap->setIsOpaque(decodingBitmap.isOpaque());
+        if (!outputBitmap->allocPixels(allocator, NULL)) {
             return nullObjectReturn("allocation failed for scaled bitmap");
         }
-        bitmap->eraseColor(0);
+        outputBitmap->eraseColor(0);
 
         SkPaint paint;
         paint.setFilterBitmap(true);
 
-        SkCanvas canvas(*bitmap);
+        SkCanvas canvas(*outputBitmap);
         canvas.scale(sx, sy);
-        canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint);
+        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
+    } else {
+        outputBitmap->swap(decodingBitmap);
     }
 
     if (padding) {
@@ -392,17 +393,17 @@
 
     SkPixelRef* pr;
     if (isPurgeable) {
-        pr = installPixelRef(bitmap, stream, sampleSize, doDither);
+        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 = bitmap->pixelRef();
+        pr = outputBitmap->pixelRef();
     }
     if (pr == NULL) {
         return nullObjectReturn("Got null SkPixelRef");
     }
 
-    if (!isMutable && !useExistingBitmap) {
+    if (!isMutable && javaBitmap == NULL) {
         // promise we will never change our pixels (great for sharing and pictures)
         pr->setImmutable();
     }
@@ -410,12 +411,14 @@
     // detach bitmap from its autodeleter, since we want to own it now
     adb.detach();
 
-    if (useExistingBitmap) {
+    if (javaBitmap != NULL) {
+        GraphicsJNI::reinitBitmap(env, javaBitmap);
+        outputBitmap->notifyPixelsChanged();
         // If a java bitmap was passed in for reuse, pass it back
         return javaBitmap;
     }
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
+    return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
             isMutable, ninePatchChunk, layoutBounds, -1);
 }
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 78ee7c6..b89d042 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -552,6 +552,7 @@
         return finish ? finishDecode(bm, outPadding, opts) : bm;
     }
 
+    // TODO: remove this path, implement any needed functionality in native decode
     private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
         if (bm == null || opts == null) {
             return bm;