Scanline decoding for wbmp

We are also changing the wbmp to use SkSwizzler.  This
will allow us to take advantage of the sampling routines
that are being implemented in SkSwizzler.

The image in this upload came from:
https://commons.wikimedia.org/wiki/File:Android_robot.png

It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.

BUG=skia:

Review URL: https://codereview.chromium.org/1254483004
diff --git a/resources/index8.png b/resources/index8.png
new file mode 100644
index 0000000..2ff47a3
--- /dev/null
+++ b/resources/index8.png
Binary files differ
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 6ef61b2..ffe90af 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -133,10 +133,5 @@
 }
 
 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
-    SkASSERT(kIndex_8_SkColorType != info.colorType());
-    if (kIndex_8_SkColorType == info.colorType()) {
-        return kInvalidConversion;
-    }
     return this->getPixels(info, pixels, rowBytes, NULL, NULL, NULL);
 }
-
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 90b1632..0c45fdd 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -31,6 +31,13 @@
     SkSwizzler::GetResult(zeroAlpha, maxAlpha);
 
 /*
+ * If there is a color table, get a pointer to the colors, otherwise return NULL
+ */
+static const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
+     return NULL != colorTable ? colorTable->readColors() : NULL;
+}
+
+/*
  *
  * Copy the codec color table back to the client when kIndex8 color type is requested
  *
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index d29ca8a..2b9603f 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -471,7 +471,7 @@
     copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
 
     // Create the swizzler.  SkPngCodec retains ownership of the color table.
-    const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
+    const SkPMColor* colors = get_color_ptr(fColorTable.get());
     fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
             options.fZeroInitialized));
     if (!fSwizzler) {
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
index 35ac808..86dce5c 100644
--- a/src/codec/SkCodec_wbmp.cpp
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -6,10 +6,29 @@
  */
 
 #include "SkCodec.h"
+#include "SkCodecPriv.h"
 #include "SkColorPriv.h"
+#include "SkColorTable.h"
 #include "SkStream.h"
 #include "SkCodec_wbmp.h"
 
+// Each bit represents a pixel, so width is actually a number of bits.
+// A row will always be stored in bytes, so we round width up to the
+// nearest multiple of 8 to get the number of bits actually in the row.
+// We then divide by 8 to convert to bytes.
+static inline size_t get_src_row_bytes(int width) {
+    return SkAlign8(width) >> 3;
+}
+
+static inline void setup_color_table(SkColorType colorType,
+        SkPMColor* colorPtr, int* colorCount) {
+    if (kIndex_8_SkColorType == colorType) {
+        colorPtr[0] = SK_ColorBLACK;
+        colorPtr[1] = SK_ColorWHITE;
+        *colorCount = 2;
+    }
+}
+
 // http://en.wikipedia.org/wiki/Variable-length_quantity
 static bool read_mbf(SkStream* stream, uint64_t* value) {
     uint64_t n = 0;
@@ -38,7 +57,7 @@
     if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
         return false;
     }
-    if (!read_mbf(stream, &height) || width > 0xFFFF || !height) {
+    if (!read_mbf(stream, &height) || height > 0xFFFF || !height) {
         return false;
     }
     if (size) {
@@ -47,70 +66,59 @@
     return true;
 }
 
-#define BLACK SkPackARGB32NoCheck(0xFF, 0, 0, 0)
-#define WHITE SkPackARGB32NoCheck(0xFF, 0xFF, 0xFF, 0xFF)
-
-#define GRAYSCALE_BLACK 0
-#define GRAYSCALE_WHITE 0xFF
-
-#define RGB565_BLACK 0
-#define RGB565_WHITE 0xFFFF
-
-static SkPMColor bit_to_pmcolor(U8CPU bit) { return bit ? WHITE : BLACK; }
-
-static uint8_t bit_to_bit(U8CPU bit) { return bit; }
-
-static uint8_t bit_to_grayscale(U8CPU bit) {
-    return bit ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
-}
-
-static uint16_t bit_to_rgb565(U8CPU bit) {
-    return bit ? RGB565_WHITE : RGB565_BLACK;
-}
-
-typedef void (*ExpandProc)(uint8_t*, const uint8_t*, int);
-
-// TODO(halcanary): Add this functionality (grayscale and indexed output) to
-//                  SkSwizzler and use it here.
-template <typename T, T (*TRANSFORM)(U8CPU)>
-static void expand_bits_to_T(uint8_t* dstptr, const uint8_t* src, int bits) {
-    T* dst = reinterpret_cast<T*>(dstptr);
-    int bytes = bits >> 3;
-    for (int i = 0; i < bytes; i++) {
-        U8CPU mask = *src++;
-        for (int j = 0; j < 8; j++) {
-            dst[j] = TRANSFORM((mask >> (7 - j)) & 1);
+bool SkWbmpCodec::handleRewind() {
+    SkCodec::RewindState rewindState = this->rewindIfNeeded();
+    if (rewindState == kCouldNotRewind_RewindState) {
+        return false;
+    } else if (rewindState == kRewound_RewindState) {
+        if (!read_header(this->stream(), NULL)) {
+            return false;
         }
-        dst += 8;
     }
-    bits &= 7;
-    if (bits > 0) {
-        U8CPU mask = *src;
-        do {
-            *dst++ = TRANSFORM((mask >> 7) & 1);
-            mask <<= 1;
-        } while (--bits != 0);
+    return true;
+}
+
+SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info,
+        const SkPMColor* ctable, const Options& opts) {
+    // TODO (msarett): Reenable support for 565 if it is desired
+    //                 skbug.com/3683
+
+    // Create the swizzler based on the desired color type
+    switch (info.colorType()) {
+        case kIndex_8_SkColorType:
+        case kN32_SkColorType:
+        case kGray_8_SkColorType:
+            return SkSwizzler::CreateSwizzler(
+                    SkSwizzler::kBit, ctable, info, opts.fZeroInitialized);
+        default:
+            return NULL;
     }
 }
 
+SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) {
+    if (this->stream()->read(row, fSrcRowBytes) != fSrcRowBytes) {
+        return kIncompleteInput;
+    }
+    return kSuccess;
+}
+
 SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
-    : INHERITED(info, stream) {}
+    : INHERITED(info, stream)
+    , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
+{}
 
 SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
     return kWBMP_SkEncodedFormat;
 }
 
 SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
-                                         void* pixels,
+                                         void* dst,
                                          size_t rowBytes,
                                          const Options& options,
                                          SkPMColor ctable[],
                                          int* ctableCount) {
-    SkCodec::RewindState rewindState = this->rewindIfNeeded();
-    if (rewindState == kCouldNotRewind_RewindState) {
+    if (!this->handleRewind()) {
         return kCouldNotRewind;
-    } else if (rewindState == kRewound_RewindState) {
-        (void)read_header(this->stream(), NULL);
     }
     if (options.fSubset) {
         // Subsets are not supported.
@@ -119,36 +127,28 @@
     if (info.dimensions() != this->getInfo().dimensions()) {
         return kInvalidScale;
     }
-    ExpandProc proc = NULL;
-    switch (info.colorType()) {
-        case kGray_8_SkColorType:
-            proc = expand_bits_to_T<uint8_t, bit_to_grayscale>;
-            break;
-        case kN32_SkColorType:
-            proc = expand_bits_to_T<SkPMColor, bit_to_pmcolor>;
-            break;
-        case kIndex_8_SkColorType:
-            ctable[0] = BLACK;
-            ctable[1] = WHITE;
-            *ctableCount = 2;
-            proc = expand_bits_to_T<uint8_t, bit_to_bit>;
-            break;
-        case kRGB_565_SkColorType:
-            proc = expand_bits_to_T<uint16_t, bit_to_rgb565>;
-            break;
-        default:
-            return kInvalidConversion;
+
+    // Prepare a color table if necessary
+    setup_color_table(info.colorType(), ctable, ctableCount);
+
+
+    // Initialize the swizzler
+    SkAutoTDelete<SkSwizzler> swizzler(this->initializeSwizzler(info, ctable, options));
+    if (NULL == swizzler.get()) {
+        return kInvalidConversion;
     }
+
+    // Perform the decode
     SkISize size = info.dimensions();
-    uint8_t* dst = static_cast<uint8_t*>(pixels);
-    size_t srcRowBytes = SkAlign8(size.width()) >> 3;
-    SkAutoTMalloc<uint8_t> src(srcRowBytes);
+    SkAutoTMalloc<uint8_t> src(fSrcRowBytes);
+    void* dstRow = dst;
     for (int y = 0; y < size.height(); ++y) {
-        if (this->stream()->read(src.get(), srcRowBytes) != srcRowBytes) {
-            return kIncompleteInput;
+        Result rowResult = this->readRow(src.get());
+        if (kSuccess != rowResult) {
+            return rowResult;
         }
-        proc(dst, src.get(), size.width());
-        dst += rowBytes;
+        swizzler->swizzle(dstRow, src.get());
+        dstRow = SkTAddOffset<void>(dstRow, rowBytes);
     }
     return kSuccess;
 }
@@ -163,8 +163,85 @@
     if (!read_header(stream, &size)) {
         return NULL;
     }
-    SkImageInfo info =
-            SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType,
-                              kOpaque_SkAlphaType);
+    SkImageInfo info = SkImageInfo::Make(size.width(), size.height(),
+            kGray_8_SkColorType, kOpaque_SkAlphaType);
     return SkNEW_ARGS(SkWbmpCodec, (info, streamDeleter.detach()));
 }
+
+class SkWbmpScanlineDecoder : public SkScanlineDecoder {
+public:
+    /*
+     * Takes ownership of all pointer paramters.
+     */
+    SkWbmpScanlineDecoder(SkWbmpCodec* codec)
+        : INHERITED(codec->getInfo())
+        , fCodec(codec)
+        , fColorTable(NULL)
+        , fSwizzler(NULL)
+        , fSrcBuffer(codec->fSrcRowBytes)
+    {}
+
+    SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
+        void* dstRow = dst;
+        for (int y = 0; y < count; ++y) {
+            SkCodec::Result rowResult = fCodec->readRow(fSrcBuffer.get());
+            if (SkCodec::kSuccess != rowResult) {
+                return rowResult;
+            }
+            fSwizzler->swizzle(dstRow, fSrcBuffer.get());
+            dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
+        }
+        return SkCodec::kSuccess;
+    }
+
+    SkCodec::Result onStart(const SkImageInfo& dstInfo,
+            const SkCodec::Options& options, SkPMColor inputColorTable[],
+            int* inputColorCount) {
+        if (!fCodec->handleRewind()) {
+            return SkCodec::kCouldNotRewind;
+        }
+        if (options.fSubset) {
+            // Subsets are not supported.
+            return SkCodec::kUnimplemented;
+        }
+        if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+            return SkCodec::kInvalidScale;
+        }
+
+        // Fill in the color table
+        setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount);
+
+        // Copy the color table to a pointer that can be owned by the scanline decoder
+        if (kIndex_8_SkColorType == dstInfo.colorType()) {
+            fColorTable.reset(SkNEW_ARGS(SkColorTable, (inputColorTable, 2)));
+        }
+
+        // Initialize the swizzler
+        fSwizzler.reset(fCodec->initializeSwizzler(dstInfo,
+                get_color_ptr(fColorTable.get()), options));
+        if (NULL == fSwizzler.get()) {
+            return SkCodec::kInvalidInput;
+        }
+
+        return SkCodec::kSuccess;
+    }
+
+private:
+    SkAutoTDelete<SkWbmpCodec>   fCodec;
+    SkAutoTUnref<SkColorTable>   fColorTable;
+    SkAutoTDelete<SkSwizzler>    fSwizzler;
+    SkAutoTMalloc<uint8_t>       fSrcBuffer;
+
+    typedef SkScanlineDecoder INHERITED;
+};
+
+SkScanlineDecoder* SkWbmpCodec::NewSDFromStream(SkStream* stream) {
+    SkAutoTDelete<SkWbmpCodec> codec(static_cast<SkWbmpCodec*>(
+            SkWbmpCodec::NewFromStream(stream)));
+    if (!codec) {
+        return NULL;
+    }
+
+    // Return the new scanline decoder
+    return SkNEW_ARGS(SkWbmpScanlineDecoder, (codec.detach()));
+}
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
index fece249..cec398d 100644
--- a/src/codec/SkCodec_wbmp.h
+++ b/src/codec/SkCodec_wbmp.h
@@ -9,17 +9,53 @@
 #define SkCodec_wbmp_DEFINED
 
 #include "SkCodec.h"
+#include "SkScanlineDecoder.h"
+#include "SkSwizzler.h"
 
 class SkWbmpCodec final : public SkCodec {
 public:
     static bool IsWbmp(SkStream*);
+
+    /*
+     * Assumes IsWbmp was called and returned true
+     * Creates a wbmp codec
+     * Takes ownership of the stream
+     */
     static SkCodec* NewFromStream(SkStream*);
+
+    /*
+     * Assumes IsWbmp was called and returned true
+     * Creates a wbmp scanline decoder
+     * Takes ownership of the stream
+     */
+    static SkScanlineDecoder* NewSDFromStream(SkStream*);
 protected:
     SkEncodedFormat onGetEncodedFormat() const override;
     Result onGetPixels(const SkImageInfo&, void*, size_t,
                        const Options&, SkPMColor[], int*) override;
 private:
+
+    /*
+     * Calls rewindIfNeeded and returns true if the decoder can continue
+     */
+    bool handleRewind();
+
+    /*
+     * Returns a swizzler on success, NULL on failure
+     */
+    SkSwizzler* initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
+                                   const Options& opts);
+
+    /*
+     * Read a src row from the encoded stream
+     */
+    Result readRow(uint8_t* row);
+
     SkWbmpCodec(const SkImageInfo&, SkStream*);
+
+    const size_t fSrcRowBytes;
+
+    friend class SkWbmpScanlineDecoder;
     typedef SkCodec INHERITED;
 };
 
diff --git a/src/codec/SkScanlineDecoder.cpp b/src/codec/SkScanlineDecoder.cpp
index 00c020c..851e7e7 100644
--- a/src/codec/SkScanlineDecoder.cpp
+++ b/src/codec/SkScanlineDecoder.cpp
@@ -7,6 +7,7 @@
 
 #include "SkScanlineDecoder.h"
 #include "SkCodec_libpng.h"
+#include "SkCodec_wbmp.h"
 #include "SkCodecPriv.h"
 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
 #include "SkJpegCodec.h"
@@ -22,6 +23,7 @@
 #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
     { SkJpegCodec::IsJpeg, SkJpegCodec::NewSDFromStream },
 #endif
+    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewSDFromStream },
 };
 
 SkScanlineDecoder* SkScanlineDecoder::NewFromStream(SkStream* stream) {
@@ -66,6 +68,19 @@
 
 SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo,
         const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
+    // Ensure that valid color ptrs are passed in for kIndex8 color type
+    if (kIndex_8_SkColorType == dstInfo.colorType()) {
+        if (NULL == ctable || NULL == ctableCount) {
+            return SkCodec::kInvalidParameters;
+        }
+    } else {
+        if (ctableCount) {
+            *ctableCount = 0;
+        }
+        ctableCount = NULL;
+        ctable = NULL;
+    }
+
     // Set options.
     SkCodec::Options optsStorage;
     if (NULL == options) {
@@ -83,10 +98,6 @@
 }
 
 SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo) {
-    SkASSERT(kIndex_8_SkColorType != dstInfo.colorType());
-    if (kIndex_8_SkColorType == dstInfo.colorType()) {
-        return SkCodec::kInvalidConversion;
-    }
     return this->start(dstInfo, NULL, NULL, NULL);
 }
 
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index 998fbeb..f8e89c7 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -19,6 +19,93 @@
     return (((uint16_t) maxAlpha) << 8) | zeroAlpha;
 }
 
+// kBit
+// These routines exclusively choose between white and black
+
+#define GRAYSCALE_BLACK 0
+#define GRAYSCALE_WHITE 0xFF
+
+static SkSwizzler::ResultAlpha swizzle_bit_to_grayscale(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
+    uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
+
+    // Determine how many full bytes are in the row
+    int bytesInRow = width >> 3;
+    int i;
+    for (i = 0; i < bytesInRow; i++) {
+        U8CPU currByte = src[i];
+        for (int j = 0; j < 8; j++) {
+            dst[j] = ((currByte >> (7 - j)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
+        }
+        dst += 8;
+    }
+
+    // Finish the remaining bits
+    width &= 7;
+    U8CPU currByte = src[i];
+    for (int j = 0; j < width; j++) {
+        dst[j] = ((currByte >> 7) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
+        currByte <<= 1;
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+#undef GRAYSCALE_BLACK
+#undef GRAYSCALE_WHITE
+
+static SkSwizzler::ResultAlpha swizzle_bit_to_index(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
+    uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
+
+    // Determine how many full bytes are in the row
+    int bytesInRow = width >> 3;
+    int i;
+    for (i = 0; i < bytesInRow; i++) {
+        U8CPU currByte = src[i];
+        for (int j = 0; j < 8; j++) {
+            dst[j] = (currByte >> (7 - j)) & 1;
+        }
+        dst += 8;
+    }
+
+    // Finish the remaining bits
+    width &= 7;
+    U8CPU currByte = src[i];
+    for (int j = 0; j < width; j++) {
+        dst[j] = ((currByte >> 7) & 1);
+        currByte <<= 1;
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+static SkSwizzler::ResultAlpha swizzle_bit_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow;
+
+    // Determine how many full bytes are in the row
+    int bytesInRow = width >> 3;
+    int i;
+    for (i = 0; i < bytesInRow; i++) {
+        U8CPU currByte = src[i];
+        for (int j = 0; j < 8; j++) {
+            dst[j] = ((currByte >> (7 - j)) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
+        }
+        dst += 8;
+    }
+
+    // Finish the remaining bits
+    width &= 7;
+    U8CPU currByte = src[i];
+    for (int j = 0; j < width; j++) {
+        dst[j] = ((currByte >> 7) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
+        currByte <<= 1;
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
 // kIndex1, kIndex2, kIndex4
 
 static SkSwizzler::ResultAlpha swizzle_small_index_to_index(
@@ -284,6 +371,21 @@
     }
     RowProc proc = NULL;
     switch (sc) {
+        case kBit:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_bit_to_n32;
+                    break;
+                case kIndex_8_SkColorType:
+                    proc = &swizzle_bit_to_index;
+                    break;
+                case kGray_8_SkColorType:
+                    proc = &swizzle_bit_to_grayscale;
+                    break;
+                default:
+                    break;
+            }
+            break;
         case kIndex1:
         case kIndex2:
         case kIndex4:
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
index 76ca748..2fab7f6 100644
--- a/src/codec/SkSwizzler.h
+++ b/src/codec/SkSwizzler.h
@@ -19,6 +19,7 @@
      */
     enum SrcConfig {
         kUnknown,  // Invalid type.
+        kBit,      // A single bit to distinguish between white and black
         kGray,
         kIndex1,
         kIndex2,
@@ -76,6 +77,7 @@
      */
     static int BitsPerPixel(SrcConfig sc) {
         switch (sc) {
+            case kBit:
             case kIndex1:
                 return 1;
             case kIndex2:
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index 1dd64d9..b7f1584 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -152,7 +152,7 @@
 
 DEF_TEST(Codec, r) {
     // WBMP
-    check(r, "mandrill.wbmp", SkISize::Make(512, 512), false, false);
+    check(r, "mandrill.wbmp", SkISize::Make(512, 512), true, false);
 
     // WEBP
     check(r, "baby_tux.webp", SkISize::Make(386, 395), false, true);
@@ -289,3 +289,38 @@
     test_empty(r, "empty_images/zero-width.wbmp");
     test_empty(r, "empty_images/zero-height.wbmp");
 }
+
+static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) {
+    SkAutoTDelete<SkStream> stream(resource(path));
+    if (!stream) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+    SkAutoTDelete<SkScanlineDecoder> decoder(SkScanlineDecoder::NewFromStream(
+        stream.detach()));
+    
+    // This should return kSuccess because kIndex8 is supported.
+    SkPMColor colorStorage[256];
+    int colorCount;
+    SkCodec::Result result = decoder->start(
+        decoder->getInfo().makeColorType(kIndex_8_SkColorType), NULL, colorStorage, &colorCount);
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    // The rest of the test is uninteresting if kIndex8 is not supported
+    if (SkCodec::kSuccess != result) {
+        return;
+    }
+
+    // This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid
+    // colorPtr and a valid colorCountPtr.
+    result = decoder->start(
+        decoder->getInfo().makeColorType(kIndex_8_SkColorType), NULL, NULL, NULL);
+    REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
+    result = decoder->start(
+        decoder->getInfo().makeColorType(kIndex_8_SkColorType));
+    REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
+}
+
+DEF_TEST(Codec_Params, r) {
+    test_invalid_parameters(r, "index8.png");
+    test_invalid_parameters(r, "mandrill.wbmp");
+}