Qualify the return value of SkImageDecoder::decode

Add a new enum to differentiate between a complete decode and a
partial decode (with the third value being failure). Return this
value from SkImageDecoder::onDecode (in all subclasses, plus
SkImageDecoder_empty) and ::decode.

For convenience, if the enum is treated as a boolean, success and
partial success are both considered true.

Note that the static helper functions (DecodeFile etc) still return
true and false (for one thing, this allows us to continue to use
SkImageDecoder::DecodeMemory as an SkPicture::InstallPixelRefProc in
SkPicture::CreateFromStream).

Also correctly report failure in SkASTCImageDecoder::onDecode when
SkTextureCompressor::DecompressBufferFromFormat fails.

BUG=skia:3037
BUG:b/17419670

Review URL: https://codereview.chromium.org/647023006
diff --git a/bench/SkipZeroesBench.cpp b/bench/SkipZeroesBench.cpp
index fc25f8b..5d18264 100644
--- a/bench/SkipZeroesBench.cpp
+++ b/bench/SkipZeroesBench.cpp
@@ -83,15 +83,15 @@
         // Decode a bunch of times
         SkBitmap bm;
         for (int i = 0; i < loops; ++i) {
-            SkDEBUGCODE(bool success =) fDecoder->decode(&fStream, &bm,
-                                                         SkImageDecoder::kDecodePixels_Mode);
+            SkDEBUGCODE(SkImageDecoder::Result result =) fDecoder->decode(&fStream, &bm,
+                    SkImageDecoder::kDecodePixels_Mode);
 #ifdef SK_DEBUG
-            if (!success) {
+            if (SkImageDecoder::kFailure == result) {
                 SkDebugf("failed to decode %s\n", fFilename.c_str());
                 return;
             }
 #endif
-            SkDEBUGCODE(success =) fStream.rewind();
+            SkDEBUGCODE(bool success =) fStream.rewind();
 #ifdef SK_DEBUG
             if (!success) {
                 SkDebugf("failed to rewind %s\n", fFilename.c_str());
diff --git a/include/core/SkImageDecoder.h b/include/core/SkImageDecoder.h
index 9483176..e4ea5e2 100644
--- a/include/core/SkImageDecoder.h
+++ b/include/core/SkImageDecoder.h
@@ -225,13 +225,26 @@
         kDecodePixels_Mode  //!< return entire bitmap (including pixels)
     };
 
+    /** Result of a decode. If read as a boolean, a partial success is
+        considered a success (true).
+    */
+    enum Result {
+        kFailure        = 0,    //!< Image failed to decode. bitmap will be
+                                //   unchanged.
+        kPartialSuccess = 1,    //!< Part of the image decoded. The rest is
+                                //   filled in automatically
+        kSuccess        = 2     //!< The entire image was decoded, if Mode is
+                                //   kDecodePixels_Mode, or the bounds were
+                                //   decoded, in kDecodeBounds_Mode.
+    };
+
     /** Given a stream, decode it into the specified bitmap.
         If the decoder can decompress the image, it calls bitmap.setInfo(),
         and then if the Mode is kDecodePixels_Mode, call allocPixelRef(),
         which will allocated a pixelRef. To access the pixel memory, the codec
         needs to call lockPixels/unlockPixels on the
         bitmap. It can then set the pixels with the decompressed image.
-    *   If the image cannot be decompressed, return false. After the
+    *   If the image cannot be decompressed, return kFailure. After the
     *   decoding, the function converts the decoded colortype in bitmap
     *   to pref if possible. Whether a conversion is feasible is
     *   tested by Bitmap::canCopyTo(pref).
@@ -244,8 +257,8 @@
         If a Peeker is installed via setPeeker, it may be used to peek into
         meta data during the decode.
     */
-    bool decode(SkStream*, SkBitmap* bitmap, SkColorType pref, Mode);
-    bool decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
+    Result decode(SkStream*, SkBitmap* bitmap, SkColorType pref, Mode);
+    Result decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
         return this->decode(stream, bitmap, kUnknown_SkColorType, mode);
     }
 
@@ -334,7 +347,7 @@
 
 protected:
     // must be overridden in subclasses. This guy is called by decode(...)
-    virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
+    virtual Result onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
 
     // If the decoder wants to support tiled based decoding,
     // this method must be overridden. This guy is called by buildTileIndex(...)
diff --git a/samplecode/SampleUnpremul.cpp b/samplecode/SampleUnpremul.cpp
index 992444d..ffe4428 100644
--- a/samplecode/SampleUnpremul.cpp
+++ b/samplecode/SampleUnpremul.cpp
@@ -182,7 +182,7 @@
             decoder->setRequireUnpremultipliedColors(true);
         }
         fDecodeSucceeded = decoder->decode(&stream, &fBitmap, kN32_SkColorType,
-                                           SkImageDecoder::kDecodePixels_Mode);
+                SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
         this->inval(NULL);
     }
 
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
index a90c1cf..cdee7c1 100644
--- a/src/images/SkDecodingImageGenerator.cpp
+++ b/src/images/SkDecodingImageGenerator.cpp
@@ -171,7 +171,7 @@
     TargetAllocator allocator(fInfo, pixels, rowBytes);
     decoder->setAllocator(&allocator);
     bool success = decoder->decode(fStream, &bitmap, info.colorType(),
-                                   SkImageDecoder::kDecodePixels_Mode);
+                                   SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
     decoder->setAllocator(NULL);
     if (!success) {
         return false;
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index f9b04e3..c5e9739 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -156,7 +156,8 @@
     return ct;
 }
 
-bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref, Mode mode) {
+SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref,
+                                              Mode mode) {
     // we reset this to false before calling onDecode
     fShouldCancelDecode = false;
     // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
@@ -164,12 +165,12 @@
 
     // pass a temporary bitmap, so that if we return false, we are assured of
     // leaving the caller's bitmap untouched.
-    SkBitmap    tmp;
-    if (!this->onDecode(stream, &tmp, mode)) {
-        return false;
+    SkBitmap tmp;
+    const Result result = this->onDecode(stream, &tmp, mode);
+    if (kFailure != result) {
+        bm->swap(tmp);
     }
-    bm->swap(tmp);
-    return true;
+    return result;
 }
 
 bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, SkColorType pref) {
@@ -272,7 +273,7 @@
     SkImageDecoder* codec = SkImageDecoder::Factory(stream);
 
     if (codec) {
-        success = codec->decode(stream, bm, pref, mode);
+        success = codec->decode(stream, bm, pref, mode) != kFailure;
         if (success && format) {
             *format = codec->getFormat();
             if (kUnknown_Format == *format) {
diff --git a/src/images/SkImageDecoder_astc.cpp b/src/images/SkImageDecoder_astc.cpp
index 79e9804..30f42f0 100644
--- a/src/images/SkImageDecoder_astc.cpp
+++ b/src/images/SkImageDecoder_astc.cpp
@@ -24,7 +24,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -42,11 +42,11 @@
         (static_cast<int>(buf[2]) << 16);
 }
 
-bool SkASTCImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkASTCImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     SkAutoMalloc autoMal;
     const size_t length = SkCopyStreamToStorage(&autoMal, stream);
     if (0 == length) {
-        return false;
+        return kFailure;
     }
 
     unsigned char* buf = (unsigned char*)autoMal.get();
@@ -63,7 +63,7 @@
 
     if (1 != blockDimZ) {
         // We don't support decoding 3D
-        return false;
+        return kFailure;
     }
 
     // Choose the proper ASTC format
@@ -98,7 +98,7 @@
         astcFormat = SkTextureCompressor::kASTC_12x12_Format;
     } else {
         // We don't support any other block dimensions..
-        return false;
+        return kFailure;
     }
 
     // Advance buf past the block dimensions
@@ -111,7 +111,7 @@
 
     if (1 != depth) {
         // We don't support decoding 3D.
-        return false;
+        return kFailure;
     }
 
     // Advance the buffer past the image dimensions
@@ -120,7 +120,7 @@
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     // should we allow the Chooser (if present) to pick a config for us???
     if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) {
-        return false;
+        return kFailure;
     }
 #endif
 
@@ -139,18 +139,18 @@
     bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(), alphaType));
 
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(bm, NULL)) {
-        return false;
+        return kFailure;
     }
 
     // Lock the pixels, since we're about to write to them...
     SkAutoLockPixels alp(*bm);
 
     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, *this)) {
-        return false;
+        return kFailure;
     }
 
     // ASTC Data is encoded as RGBA pixels, so we should extract it as such
@@ -161,6 +161,7 @@
     // Decode ASTC
     if (!SkTextureCompressor::DecompressBufferFromFormat(
             outRGBADataPtr, width*4, buf, width, height, astcFormat)) {
+        return kFailure;
     }
 
     // Set each of the pixels...
@@ -173,7 +174,7 @@
         srcRow += sampler.srcDY() * srcRowBytes;
     }
 
-    return true;
+    return kSuccess;
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_ktx.cpp b/src/images/SkImageDecoder_ktx.cpp
index 20c8bbc..8566375 100644
--- a/src/images/SkImageDecoder_ktx.cpp
+++ b/src/images/SkImageDecoder_ktx.cpp
@@ -41,22 +41,22 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
 };
 
-bool SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams
     SkAutoDataUnref data(SkCopyStreamToData(stream));
     if (NULL == data) {
-        return false;
+        return kFailure;
     }
 
     SkKTXFile ktxFile(data);
     if (!ktxFile.valid()) {
-        return false;
+        return kFailure;
     }
 
     const unsigned short width = ktxFile.width();
@@ -65,7 +65,7 @@
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     // should we allow the Chooser (if present) to pick a config for us???
     if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) {
-        return false;
+        return kFailure;
     }
 #endif
 
@@ -84,7 +84,7 @@
             // If the client wants unpremul colors and we only have
             // premul, then we cannot honor their wish.
             if (bSrcIsPremul) {
-                return false;
+                return kFailure;
             }
         } else {
             alphaType = kPremul_SkAlphaType;
@@ -121,12 +121,12 @@
     }
     
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     // If we've made it this far, then we know how to grok the data.
     if (!this->allocPixelRef(bm, NULL)) {
-        return false;
+        return kFailure;
     }
 
     // Lock the pixels, since we're about to write to them...
@@ -134,7 +134,7 @@
 
     if (isCompressedAlpha) {
         if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) {
-            return false;
+            return kFailure;
         }
 
         // Alpha data is only a single byte per pixel.
@@ -146,7 +146,7 @@
         const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
         if (!SkTextureCompressor::DecompressBufferFromFormat(
                 outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) {
-            return false;
+            return kFailure;
         }
 
         // Set each of the pixels...
@@ -159,11 +159,11 @@
             srcRow += sampler.srcDY() * srcRowBytes;
         }
 
-        return true;
+        return kSuccess;
 
     } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) {
         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
-            return false;
+            return kFailure;
         }
 
         // ETC1 Data is encoded as RGB pixels, so we should extract it as such
@@ -175,7 +175,7 @@
         const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData());
         if (!SkTextureCompressor::DecompressBufferFromFormat(
                 outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
-            return false;
+            return kFailure;
         }
 
         // Set each of the pixels...
@@ -188,13 +188,13 @@
             srcRow += sampler.srcDY() * srcRowBytes;
         }
 
-        return true;
+        return kSuccess;
 
     } else if (ktxFile.isRGB8()) {
 
         // Uncompressed RGB data (without alpha)
         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
-            return false;
+            return kFailure;
         }
 
         // Just need to read RGB pixels
@@ -207,7 +207,7 @@
             srcRow += sampler.srcDY() * srcRowBytes;
         }
 
-        return true;
+        return kSuccess;
 
     } else if (ktxFile.isRGBA8()) {
 
@@ -224,7 +224,7 @@
         } 
 
         if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) {
-            return false;
+            return kFailure;
         }
 
         // Just need to read RGBA pixels
@@ -237,10 +237,10 @@
             srcRow += sampler.srcDY() * srcRowBytes;
         }
 
-        return true;
+        return kSuccess;
     }
 
-    return false;
+    return kFailure;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
index 7b87e40..af868e3 100644
--- a/src/images/SkImageDecoder_libbmp.cpp
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -24,7 +24,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -92,7 +92,7 @@
     bool fJustBounds;
 };
 
-bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     // First read the entire stream, so that all of the data can be passed to
     // the BmpDecoderHelper.
 
@@ -101,7 +101,7 @@
     // Byte length of all of the data.
     const size_t length = SkCopyStreamToStorage(&storage, stream);
     if (0 == length) {
-        return 0;
+        return kFailure;
     }
 
     const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
@@ -113,7 +113,7 @@
         const int max_pixels = 16383*16383; // max width*height
         if (!helper.DecodeImage((const char*)storage.get(), length,
                                 max_pixels, &callback)) {
-            return false;
+            return kFailure;
         }
     }
 
@@ -136,17 +136,17 @@
                                   colorType, kOpaque_SkAlphaType));
 
     if (justBounds) {
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(bm, NULL)) {
-        return false;
+        return kFailure;
     }
 
     SkAutoLockPixels alp(*bm);
 
     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
-        return false;
+        return kFailure;
     }
 
     const int srcRowBytes = width * 3;
@@ -158,5 +158,5 @@
         sampler.next(srcRow);
         srcRow += sampler.srcDY() * srcRowBytes;
     }
-    return true;
+    return kSuccess;
 }
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index 81071c9..fb5d18f 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -24,7 +24,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -152,14 +152,15 @@
     return transpIndex;
 }
 
-static bool error_return(const SkBitmap& bm, const char msg[]) {
+static SkImageDecoder::Result error_return(const SkBitmap& bm, const char msg[]) {
     if (!c_suppressGIFImageDecoderWarnings) {
         SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n",
                  msg, bm.width(), bm.height(), bm.getPixels(),
                  bm.getColorTable());
     }
-    return false;
+    return SkImageDecoder::kFailure;
 }
+
 static void gif_warning(const SkBitmap& bm, const char msg[]) {
     if (!c_suppressGIFImageDecoderWarnings) {
         SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n",
@@ -228,7 +229,7 @@
     }
 }
 
-bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
 #if GIFLIB_MAJOR < 5
     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
 #else
@@ -322,7 +323,7 @@
                                           kIndex_8_SkColorType, kPremul_SkAlphaType));
 
             if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-                return true;
+                return kSuccess;
             }
 
 
@@ -423,7 +424,7 @@
                             sampler.sampleInterlaced(scanline, iter.currY());
                             iter.next();
                         }
-                        return true;
+                        return kPartialSuccess;
                     }
                     sampler.sampleInterlaced(scanline, iter.currY());
                     iter.next();
@@ -439,7 +440,7 @@
                         for (; y < outHeight; y++) {
                             sampler.next(scanline);
                         }
-                        return true;
+                        return kPartialSuccess;
                     }
                     // scanline now contains the raw data. Sample it.
                     sampler.next(scanline);
@@ -453,7 +454,7 @@
                 skip_src_rows(gif, scanline, innerWidth, innerHeight - read);
             }
             sanitize_indexed_bitmap(bm);
-            return true;
+            return kSuccess;
             } break;
 
         case EXTENSION_RECORD_TYPE:
@@ -498,7 +499,7 @@
     } while (recType != TERMINATE_RECORD_TYPE);
 
     sanitize_indexed_bitmap(bm);
-    return true;
+    return kSuccess;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index 7855546..4d19714 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -20,7 +20,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -72,12 +72,12 @@
     return 0;
 }
 
-bool SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode)
+SkImageDecoder::Result SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode)
 {
     SkAutoMalloc autoMal;
     const size_t length = SkCopyStreamToStorage(&autoMal, stream);
     if (0 == length) {
-        return false;
+        return kFailure;
     }
 
     unsigned char* buf = (unsigned char*)autoMal.get();
@@ -86,13 +86,16 @@
     //incorrect values, but still decode properly?
     int reserved = read2Bytes(buf, 0);    // 0
     int type = read2Bytes(buf, 2);        // 1
-    if (reserved != 0 || type != 1)
-        return false;
+    if (reserved != 0 || type != 1) {
+        return kFailure;
+    }
+
     int count = read2Bytes(buf, 4);
 
     //need to at least have enough space to hold the initial table of info
-    if (length < (size_t)(6 + count*16))
-        return false;
+    if (length < (size_t)(6 + count*16)) {
+        return kFailure;
+    }
 
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     int choice;
@@ -137,8 +140,9 @@
     }
 
     //you never know what the chooser is going to supply
-    if (choice >= count || choice < 0)
-        return false;
+    if (choice >= count || choice < 0) {
+        return kFailure;
+    }
 #else
     const int choice = 0;   // TODO: fold this value into the expressions below
 #endif
@@ -156,7 +160,7 @@
     const size_t offset = read4Bytes(buf, 18 + choice*16);
     // promote the sum to 64-bits to avoid overflow
     if (((uint64_t)offset + size) > length) {
-        return false;
+        return kFailure;
     }
 
     // Check to see if this is a PNG image inside the ICO
@@ -165,13 +169,18 @@
         SkAutoTDelete<SkImageDecoder> otherDecoder(SkImageDecoder::Factory(&subStream));
         if (otherDecoder.get() != NULL) {
             // Disallow nesting ICO files within one another
+            // FIXME: Can ICO files contain other formats besides PNG?
             if (otherDecoder->getFormat() == SkImageDecoder::kICO_Format) {
-                return false;
+                return kFailure;
             }
             // Set fields on the other decoder to be the same as this one.
             this->copyFieldsToOther(otherDecoder.get());
-            if(otherDecoder->decode(&subStream, bm, this->getDefaultPref(), mode)) {
-                return true;
+            const Result result = otherDecoder->decode(&subStream, bm, this->getDefaultPref(),
+                                                       mode);
+            // FIXME: Should we just return result here? Is it possible that data that looked like
+            // a subimage was not, but was actually a valid ICO?
+            if (result != kFailure) {
+                return result;
             }
         }
     }
@@ -209,7 +218,7 @@
             break;
         default:
             SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount));
-            return false;
+            return kFailure;
     }
 
     //these should all be zero, but perhaps are not - need to check
@@ -260,13 +269,13 @@
 
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         delete[] colors;
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(bm, NULL))
     {
         delete[] colors;
-        return false;
+        return kFailure;
     }
 
     SkAutoLockPixels alp(*bm);
@@ -296,7 +305,7 @@
     //ensure we haven't read off the end?
     //of course this doesn't help us if the andOffset was a lie...
     //return andOffset + (andLineWidth >> 3) <= length;
-    return true;
+    return kSuccess;
 }   //onDecode
 
 //function to place the pixel, determined by the bitCount
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 4103eef..082adca 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -238,7 +238,7 @@
     virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
     virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
 #endif
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
     virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
                                     void* planes[3], size_t rowBytes[3],
                                     SkYUVColorSpace* colorSpace) SK_OVERRIDE;
@@ -325,9 +325,11 @@
 }
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+
 // This guy exists just to aid in debugging, as it allows debuggers to just
 // set a break-point in one place to see all error exists.
-static bool return_false(const jpeg_decompress_struct& cinfo,
+static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo,
                          int width, int height, const char caller[]) {
     if (!(c_suppressJPEGImageDecoderErrors)) {
         char buffer[JMSG_LENGTH_MAX];
@@ -335,14 +337,28 @@
         SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n",
                  cinfo.err->msg_code, buffer, caller, width, height);
     }
-    return false;   // must always return false
+}
+
+static bool return_false(const jpeg_decompress_struct& cinfo,
+                         const char caller[]) {
+    print_jpeg_decoder_errors(cinfo, 0, 0, caller);
+    return false;
 }
 
 static bool return_false(const jpeg_decompress_struct& cinfo,
                          const SkBitmap& bm, const char caller[]) {
-    return return_false(cinfo, bm.width(), bm.height(), caller);
+    print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
+    return false;
 }
 
+static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
+                                             const SkBitmap& bm, const char caller[]) {
+    print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
+    return SkImageDecoder::kFailure;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 // Convert a scanline of CMYK samples to RGBX in place. Note that this
 // method moves the "scanline" pointer in its processing
 static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
@@ -491,7 +507,6 @@
 #endif
 }
 
-
 /**
    Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
    Used when decoding fails partway through reading scanlines to fill
@@ -537,7 +552,7 @@
     return true;
 }
 
-bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 #ifdef TIME_DECODE
     SkAutoTime atm("JPEG Decode");
 #endif
@@ -553,7 +568,7 @@
     // All objects need to be instantiated before this setjmp call so that
     // they will be cleaned up properly if an error occurs.
     if (setjmp(errorManager.fJmpBuf)) {
-        return return_false(cinfo, *bm, "setjmp");
+        return return_failure(cinfo, *bm, "setjmp");
     }
 
     initialize_info(&cinfo, &srcManager);
@@ -561,7 +576,7 @@
 
     int status = jpeg_read_header(&cinfo, true);
     if (status != JPEG_HEADER_OK) {
-        return return_false(cinfo, *bm, "read_header");
+        return return_failure(cinfo, *bm, "read_header");
     }
 
     /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it
@@ -588,8 +603,9 @@
         // individual pixel. It is very unlikely to be opaque, since
         // an opaque A8 bitmap would not be very interesting.
         // Otherwise, a jpeg image is opaque.
-        return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
-                                             colorType, alphaType));
+        bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,
+                                                     colorType, alphaType));
+        return success ? kSuccess : kFailure;
     }
 
     /*  image_width and image_height are the original dimensions, available
@@ -613,10 +629,11 @@
             // individual pixel. It is very unlikely to be opaque, since
             // an opaque A8 bitmap would not be very interesting.
             // Otherwise, a jpeg image is opaque.
-            return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
-                                                 colorType, alphaType));
+            bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),
+                                                         colorType, alphaType));
+            return success ? kSuccess : kFailure;
         } else {
-            return return_false(cinfo, *bm, "start_decompress");
+            return return_failure(cinfo, *bm, "start_decompress");
         }
     }
     sampleSize = recompute_sampleSize(sampleSize, cinfo);
@@ -624,7 +641,7 @@
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     // should we allow the Chooser (if present) to pick a colortype for us???
     if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {
-        return return_false(cinfo, *bm, "chooseFromOneChoice");
+        return return_failure(cinfo, *bm, "chooseFromOneChoice");
     }
 #endif
 
@@ -636,10 +653,10 @@
     bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
                                   colorType, alphaType));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
     if (!this->allocPixelRef(bm, NULL)) {
-        return return_false(cinfo, *bm, "allocPixelRef");
+        return return_failure(cinfo, *bm, "allocPixelRef");
     }
 
     SkAutoLockPixels alp(*bm);
@@ -662,15 +679,16 @@
                 // so return early.  We will return a partial image.
                 fill_below_level(cinfo.output_scanline, bm);
                 cinfo.output_scanline = cinfo.output_height;
-                break;  // Skip to jpeg_finish_decompress()
+                jpeg_finish_decompress(&cinfo);
+                return kPartialSuccess;
             }
             if (this->shouldCancelDecode()) {
-                return return_false(cinfo, *bm, "shouldCancelDecode");
+                return return_failure(cinfo, *bm, "shouldCancelDecode");
             }
             rowptr += bpr;
         }
         jpeg_finish_decompress(&cinfo);
-        return true;
+        return kSuccess;
     }
 #endif
 
@@ -679,11 +697,11 @@
     int srcBytesPerPixel;
 
     if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {
-        return return_false(cinfo, *bm, "jpeg colorspace");
+        return return_failure(cinfo, *bm, "jpeg colorspace");
     }
 
     if (!sampler.begin(bm, sc, *this)) {
-        return return_false(cinfo, *bm, "sampler.begin");
+        return return_failure(cinfo, *bm, "sampler.begin");
     }
 
     SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);
@@ -691,7 +709,7 @@
 
     //  Possibly skip initial rows [sampler.srcY0]
     if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {
-        return return_false(cinfo, *bm, "skip rows");
+        return return_failure(cinfo, *bm, "skip rows");
     }
 
     // now loop through scanlines until y == bm->height() - 1
@@ -703,10 +721,11 @@
             // so return early.  We will return a partial image.
             fill_below_level(y, bm);
             cinfo.output_scanline = cinfo.output_height;
-            break;  // Skip to jpeg_finish_decompress()
+            jpeg_finish_decompress(&cinfo);
+            return kSuccess;
         }
         if (this->shouldCancelDecode()) {
-            return return_false(cinfo, *bm, "shouldCancelDecode");
+            return return_failure(cinfo, *bm, "shouldCancelDecode");
         }
 
         if (JCS_CMYK == cinfo.out_color_space) {
@@ -720,20 +739,22 @@
         }
 
         if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {
-            return return_false(cinfo, *bm, "skip rows");
+            return return_failure(cinfo, *bm, "skip rows");
         }
     }
 
     // we formally skip the rest, so we don't get a complaint from libjpeg
     if (!skip_src_rows(&cinfo, srcRow,
                        cinfo.output_height - cinfo.output_scanline)) {
-        return return_false(cinfo, *bm, "skip rows");
+        return return_failure(cinfo, *bm, "skip rows");
     }
     jpeg_finish_decompress(&cinfo);
 
-    return true;
+    return kSuccess;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 enum SizeType {
     kSizeForMemoryAllocation_SizeType,
     kActualSize_SizeType
@@ -818,8 +839,9 @@
         }
         JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead);
 
-        if (scanlinesRead == 0)
+        if (scanlinesRead == 0) {
             return false;
+        }
 
         if (hasYLastRow) {
             memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth);
@@ -857,7 +879,7 @@
     // All objects need to be instantiated before this setjmp call so that
     // they will be cleaned up properly if an error occurs.
     if (setjmp(errorManager.fJmpBuf)) {
-        return return_false(cinfo, 0, 0, "setjmp YUV8");
+        return return_false(cinfo, "setjmp YUV8");
     }
 
     initialize_info(&cinfo, &srcManager);
@@ -865,7 +887,7 @@
 
     int status = jpeg_read_header(&cinfo, true);
     if (status != JPEG_HEADER_OK) {
-        return return_false(cinfo, 0, 0, "read_header YUV8");
+        return return_false(cinfo, "read_header YUV8");
     }
 
     if (cinfo.jpeg_color_space != JCS_YCbCr) {
@@ -897,11 +919,11 @@
         jpeg_start_decompress(), and then read output_width and output_height.
     */
     if (!jpeg_start_decompress(&cinfo)) {
-        return return_false(cinfo, 0, 0, "start_decompress YUV8");
+        return return_false(cinfo, "start_decompress YUV8");
     }
 
     if (!output_raw_data(cinfo, planes, rowBytes)) {
-        return return_false(cinfo, 0, 0, "output_raw_data");
+        return return_false(cinfo, "output_raw_data");
     }
 
     update_components_sizes(cinfo, componentSizes, kActualSize_SizeType);
@@ -914,6 +936,8 @@
     return true;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 #ifdef SK_BUILD_FOR_ANDROID
 bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
 
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index 9fe5fb9..0890eaa 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -91,7 +91,7 @@
     virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
     virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
 #endif
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     SkPNGImageIndex* fImageIndex;
@@ -302,19 +302,19 @@
     return true;
 }
 
-bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
-                                 Mode mode) {
+SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+                                                   Mode mode) {
     png_structp png_ptr;
     png_infop info_ptr;
 
     if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
-        return false;
+        return kFailure;
     }
 
     PNGAutoClean autoClean(png_ptr, info_ptr);
 
     if (setjmp(png_jmpbuf(png_ptr))) {
-        return false;
+        return kFailure;
     }
 
     png_uint_32 origWidth, origHeight;
@@ -327,7 +327,7 @@
     SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
 
     if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {
-        return false;
+        return kFailure;
     }
 
     SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ?
@@ -338,7 +338,7 @@
                                              colorType, alphaType));
 
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     // from here down we are concerned with colortables and pixels
@@ -357,7 +357,7 @@
 
     if (!this->allocPixelRef(decodedBitmap,
                              kIndex_8_SkColorType == colorType ? colorTable : NULL)) {
-        return false;
+        return kFailure;
     }
 
     SkAutoLockPixels alp(*decodedBitmap);
@@ -415,7 +415,7 @@
          */
         SkAutoLockColors ctLock(colorTable);
         if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) {
-            return false;
+            return kFailure;
         }
         const int height = decodedBitmap->height();
 
@@ -472,7 +472,7 @@
                 // Fall through.
             case kARGB_4444_SkColorType:
                 // We have chosen not to support unpremul for these colortypes.
-                return false;
+                return kFailure;
             default: {
                 // Fall through to finish the decode. This colortype either
                 // supports unpremul or it is irrelevant because it has no
@@ -485,7 +485,7 @@
     if (!reallyHasAlpha) {
         decodedBitmap->setAlphaType(kOpaque_SkAlphaType);
     }
-    return true;
+    return kSuccess;
 }
 
 
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index f32587d..8bf15c9 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -111,7 +111,7 @@
 protected:
     virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
     virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     /**
@@ -162,11 +162,22 @@
 
 // This guy exists just to aid in debugging, as it allows debuggers to just
 // set a break-point in one place to see all error exists.
-static bool return_false(const SkBitmap& bm, const char msg[]) {
+static void print_webp_error(const SkBitmap& bm, const char msg[]) {
     SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
+}
+
+static bool return_false(const SkBitmap& bm, const char msg[]) {
+    print_webp_error(bm, msg);
     return false; // must always return false
 }
 
+static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) {
+    print_webp_error(bm, msg);
+    return SkImageDecoder::kFailure; // must always return kFailure
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) {
     WEBP_CSP_MODE mode = MODE_LAST;
     const SkColorType ct = decodedBitmap->colorType();
@@ -409,15 +420,15 @@
     return true;
 }
 
-bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
-                                  Mode mode) {
+SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                                    Mode mode) {
 #ifdef TIME_DECODE
     AutoTimeMillis atm("WEBP Decode");
 #endif
 
     int origWidth, origHeight, hasAlpha;
     if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
-        return false;
+        return kFailure;
     }
     this->fHasAlpha = hasAlpha;
 
@@ -425,16 +436,16 @@
     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
     if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
                          sampler.scaledHeight())) {
-        return false;
+        return kFailure;
     }
 
     // If only bounds are requested, done
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(decodedBitmap, NULL)) {
-        return return_false(*decodedBitmap, "allocPixelRef");
+        return return_failure(*decodedBitmap, "allocPixelRef");
     }
 
     SkAutoLockPixels alp(*decodedBitmap);
@@ -442,11 +453,11 @@
     WebPDecoderConfig config;
     if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
                                 this->shouldPremultiply())) {
-        return false;
+        return kFailure;
     }
 
     // Decode the WebP image data stream using WebP incremental decoding.
-    return webp_idecode(stream, &config);
+    return webp_idecode(stream, &config) ? kSuccess : kFailure;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_pkm.cpp b/src/images/SkImageDecoder_pkm.cpp
index f5fd4b3..746ae40 100644
--- a/src/images/SkImageDecoder_pkm.cpp
+++ b/src/images/SkImageDecoder_pkm.cpp
@@ -24,7 +24,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -32,11 +32,11 @@
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
-bool SkPKMImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkPKMImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     SkAutoMalloc autoMal;
     const size_t length = SkCopyStreamToStorage(&autoMal, stream);
     if (0 == length) {
-        return false;
+        return kFailure;
     }
 
     unsigned char* buf = (unsigned char*)autoMal.get();
@@ -50,7 +50,7 @@
 #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
     // should we allow the Chooser (if present) to pick a config for us???
     if (!this->chooseFromOneChoice(kN32_SkColorType, width, height)) {
-        return false;
+        return kFailure;
     }
 #endif
 
@@ -61,18 +61,18 @@
     bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(),
                                      kOpaque_SkAlphaType));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(bm, NULL)) {
-        return false;
+        return kFailure;
     }
 
     // Lock the pixels, since we're about to write to them...
     SkAutoLockPixels alp(*bm);
 
     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
-        return false;
+        return kFailure;
     }
 
     // Advance buffer past the header
@@ -86,7 +86,7 @@
     // Decode ETC1
     if (!SkTextureCompressor::DecompressBufferFromFormat(
             outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) {
-        return false;
+        return kFailure;
     }
 
     // Set each of the pixels...
@@ -99,7 +99,7 @@
         srcRow += sampler.srcDY() * srcRowBytes;
     }
 
-    return true;
+    return kSuccess;
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index 0bf1389..7a6f76e 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -22,7 +22,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -99,13 +99,13 @@
     }
 }
 
-bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
-                                  Mode mode)
+SkImageDecoder::Result SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
+                                                    Mode mode)
 {
     wbmp_head   head;
 
     if (!head.init(stream)) {
-        return false;
+        return kFailure;
     }
 
     int width = head.fWidth;
@@ -115,7 +115,7 @@
                                              kIndex_8_SkColorType, kOpaque_SkAlphaType));
 
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
@@ -123,7 +123,7 @@
     SkAutoUnref   aur(ct);
 
     if (!this->allocPixelRef(decodedBitmap, ct)) {
-        return false;
+        return kFailure;
     }
 
     SkAutoLockPixels alp(*decodedBitmap);
@@ -135,7 +135,7 @@
     size_t srcSize = height * srcRB;
     uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
     if (stream->read(src, srcSize) != srcSize) {
-        return false;
+        return kFailure;
     }
 
     for (int y = 0; y < height; y++)
@@ -145,7 +145,7 @@
         src += srcRB;
     }
 
-    return true;
+    return kSuccess;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
index d78502d..fe425d5 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -835,8 +835,8 @@
 public:
     DummyDecoder() {}
 protected:
-    virtual bool onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
-        return false;
+    virtual Result onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
+        return kFailure;
     }
 };
 
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index 3e3075c..0358aec 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -48,7 +48,7 @@
 
 class SkImageDecoder_CG : public SkImageDecoder {
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode);
 };
 
 static void argb_4444_force_opaque(void* row, int count) {
@@ -103,17 +103,17 @@
 
 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
 
-bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
 
     if (NULL == imageSrc) {
-        return false;
+        return kFailure;
     }
     SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
 
     CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
     if (NULL == image) {
-        return false;
+        return kFailure;
     }
     SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
 
@@ -122,17 +122,17 @@
 
     bm->setInfo(SkImageInfo::MakeN32Premul(width, height));
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return true;
+        return kSuccess;
     }
 
     if (!this->allocPixelRef(bm, NULL)) {
-        return false;
+        return kFailure;
     }
 
     SkAutoLockPixels alp(*bm);
 
     if (!SkCopyPixelsFromCGImage(bm->info(), bm->rowBytes(), bm->getPixels(), image)) {
-        return false;
+        return kFailure;
     }
 
     CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
@@ -162,7 +162,7 @@
         }
         bm->setAlphaType(kUnpremul_SkAlphaType);
     }
-    return true;
+    return kSuccess;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index 3309af4..0c109a2 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -67,7 +67,7 @@
     bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const;
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
 };
 
 struct FormatConversion {
@@ -92,7 +92,7 @@
     return SkImageDecoder::kUnknown_Format;
 }
 
-bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+SkImageDecoder::Result SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     WICModes wicMode;
     switch (mode) {
         case SkImageDecoder::kDecodeBounds_Mode:
@@ -102,7 +102,7 @@
             wicMode = kDecodePixels_WICMode;
             break;
     }
-    return this->decodeStream(stream, bm, wicMode, NULL);
+    return this->decodeStream(stream, bm, wicMode, NULL) ? kSuccess : kFailure;
 }
 
 bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode,
diff --git a/src/ports/SkImageDecoder_empty.cpp b/src/ports/SkImageDecoder_empty.cpp
index 7824732..722b7d4 100644
--- a/src/ports/SkImageDecoder_empty.cpp
+++ b/src/ports/SkImageDecoder_empty.cpp
@@ -32,8 +32,8 @@
     return false;
 }
 
-bool SkImageDecoder::decode(SkStream*, SkBitmap*, SkColorType, Mode) {
-    return false;
+SkImageDecoder::Result SkImageDecoder::decode(SkStream*, SkBitmap*, SkColorType, Mode) {
+    return kFailure;
 }
 
 bool SkImageDecoder::DecodeStream(SkStreamRewindable*, SkBitmap*, SkColorType, Mode, Format*) {
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp
index 30665a6..762852d 100644
--- a/tests/ImageDecodingTest.cpp
+++ b/tests/ImageDecodingTest.cpp
@@ -93,7 +93,7 @@
     }
 
     bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
-                                   SkImageDecoder::kDecodePixels_Mode);
+                                   SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
     if (!success) {
         return;
     }
@@ -106,7 +106,7 @@
 
     decoder->setRequireUnpremultipliedColors(true);
     success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
-                              SkImageDecoder::kDecodePixels_Mode);
+                              SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
     if (!success) {
         return;
     }
@@ -812,7 +812,7 @@
     decoder->setSampleSize(2);
     SkBitmap bitmap;
     bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
-                                   SkImageDecoder::kDecodePixels_Mode);
+                                   SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
     REPORTER_ASSERT(r, success);
     REPORTER_ASSERT(r, !allocator->ready());  // Decoder used correct memory
     REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);