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/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()));
+}