ARGB image encoder for checksums.
https://codereview.chromium.org/14267031/


git-svn-id: http://skia.googlecode.com/svn/trunk@8831 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/images/SkImageEncoder_argb.cpp b/src/images/SkImageEncoder_argb.cpp
new file mode 100644
index 0000000..5abc23c
--- /dev/null
+++ b/src/images/SkImageEncoder_argb.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkImageEncoder.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+class SkARGBImageEncoder : public SkImageEncoder {
+protected:
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+
+private:
+    typedef SkImageEncoder INHERITED;
+};
+
+typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* argb, int width,
+                                 const SkPMColor* SK_RESTRICT ctable);
+
+static void ARGB_8888_To_ARGB(const uint8_t* in, uint8_t* argb, int width, const SkPMColor*) {
+  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = *src++;
+      argb[0] = SkGetPackedA32(c);
+      argb[1] = SkGetPackedR32(c);
+      argb[2] = SkGetPackedG32(c);
+      argb[3] = SkGetPackedB32(c);
+      argb += 4;
+  }
+}
+
+static void RGB_565_To_ARGB(const uint8_t* in, uint8_t* argb, int width, const SkPMColor*) {
+  const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint16_t c = *src++;
+      argb[0] = 0xFF;
+      argb[1] = SkPacked16ToR32(c);
+      argb[2] = SkPacked16ToG32(c);
+      argb[3] = SkPacked16ToB32(c);
+      argb += 4;
+  }
+}
+
+static void ARGB_4444_To_ARGB(const uint8_t* in, uint8_t* argb, int width, const SkPMColor*) {
+  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
+  for (int i = 0; i < width; ++i) {
+      const SkPMColor16 c = *src++;
+      argb[0] = SkPacked4444ToA32(c);
+      argb[1] = SkPacked4444ToR32(c);
+      argb[2] = SkPacked4444ToG32(c);
+      argb[3] = SkPacked4444ToB32(c);
+      argb += 4;
+  }
+}
+
+static void Index8_To_ARGB(const uint8_t* in, uint8_t* argb, int width,
+                           const SkPMColor* SK_RESTRICT ctable) {
+  const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
+  for (int i = 0; i < width; ++i) {
+      const uint32_t c = ctable[*src++];
+      argb[0] = SkGetPackedA32(c);
+      argb[1] = SkGetPackedR32(c);
+      argb[2] = SkGetPackedG32(c);
+      argb[3] = SkGetPackedB32(c);
+      argb += 4;
+  }
+}
+
+static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return ARGB_8888_To_ARGB;
+        case SkBitmap::kRGB_565_Config:
+            return RGB_565_To_ARGB;
+        case SkBitmap::kARGB_4444_Config:
+            return ARGB_4444_To_ARGB;
+        case SkBitmap::kIndex8_Config:
+            return Index8_To_ARGB;
+        default:
+            return NULL;
+    }
+}
+
+bool SkARGBImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int) {
+    const SkBitmap::Config config = bitmap.getConfig();
+    const ScanlineImporter scanline_import = ChooseImporter(config);
+    if (NULL == scanline_import) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(bitmap);
+    const uint8_t* src = (uint8_t*)bitmap.getPixels();
+    if (NULL == bitmap.getPixels()) {
+        return false;
+    }
+
+    SkAutoLockColors ctLocker;
+    const SkPMColor* colors = ctLocker.lockColors(bitmap);
+
+    const int argbStride = bitmap.width() * 4;
+    SkAutoTDeleteArray<uint8_t> ada(new uint8_t[argbStride]);
+    uint8_t* argb = ada.get();
+    for (int y = 0; y < bitmap.height(); ++y) {
+        scanline_import(src + y * bitmap.rowBytes(), argb, bitmap.width(), colors);
+        stream->write(argb, argbStride);
+    }
+
+    return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_ENCODER_CREATOR(ARGBImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/utils/SkBitmapHasher.cpp b/src/utils/SkBitmapHasher.cpp
index 6df3ab9..e8352e5 100644
--- a/src/utils/SkBitmapHasher.cpp
+++ b/src/utils/SkBitmapHasher.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -8,63 +7,55 @@
 
 #include "SkBitmap.h"
 #include "SkBitmapHasher.h"
-#include "SkBitmapTransformer.h"
 #include "SkCityHash.h"
 #include "SkEndian.h"
+#include "SkImageEncoder.h"
+#include "SkStream.h"
 
 /**
- * Write an integer value into a bytebuffer in little-endian order.
+ * Write an integer value to a stream in little-endian order.
  */
-static void write_int_to_buffer(int val, char* buf) {
+static void write_int_to_buffer(uint32_t val, SkWStream* out) {
     val = SkEndian_SwapLE32(val);
-    for (int byte=0; byte<4; byte++) {
-        *buf++ = (char)(val & 0xff);
+    for (size_t byte = 0; byte < 4; ++byte) {
+        out->write8((uint8_t)(val & 0xff));
         val = val >> 8;
     }
 }
 
-/*static*/ bool SkBitmapHasher::ComputeDigestInternal(
-        const SkBitmap& bitmap, const SkBitmapTransformer& transformer, SkHashDigest *result) {
-    size_t pixelBufferSize = transformer.bytesNeededTotal();
-    size_t totalBufferSize = pixelBufferSize + 8; // leave room for x/y dimensions
+/*static*/ bool SkBitmapHasher::ComputeDigestInternal(const SkBitmap& bitmap,
+                                                      SkHashDigest *result) {
+    size_t pixelBufferSize = bitmap.width() * bitmap.height() * 4;
+    size_t totalBufferSize = pixelBufferSize + 2 * sizeof(uint32_t);
 
     SkAutoMalloc bufferManager(totalBufferSize);
     char *bufferStart = static_cast<char *>(bufferManager.get());
-    char *bufPtr = bufferStart;
+    SkMemoryWStream out(bufferStart, totalBufferSize);
+
     // start with the x/y dimensions
-    write_int_to_buffer(bitmap.width(), bufPtr);
-    bufPtr += 4;
-    write_int_to_buffer(bitmap.height(), bufPtr);
-    bufPtr += 4;
+    write_int_to_buffer(SkToU32(bitmap.width()), &out);
+    write_int_to_buffer(SkToU32(bitmap.height()), &out);
 
     // add all the pixel data
-    if (!transformer.copyBitmapToPixelBuffer(bufPtr, pixelBufferSize)) {
+    SkAutoTDelete<SkImageEncoder> enc(CreateARGBImageEncoder());
+    if (!enc->encodeStream(&out, bitmap, SkImageEncoder::kDefaultQuality)) {
         return false;
     }
+
     *result = SkCityHash::Compute64(bufferStart, totalBufferSize);
     return true;
 }
 
 /*static*/ bool SkBitmapHasher::ComputeDigest(const SkBitmap& bitmap, SkHashDigest *result) {
-    const SkBitmapTransformer::PixelFormat kPixelFormat =
-        SkBitmapTransformer::kARGB_8888_Premul_PixelFormat;
-
-    // First, try to transform the existing bitmap.
-    const SkBitmapTransformer transformer =
-        SkBitmapTransformer(bitmap, kPixelFormat);
-    if (transformer.isValid(false)) {
-        return ComputeDigestInternal(bitmap, transformer, result);
+    if (ComputeDigestInternal(bitmap, result)) {
+        return true;
     }
 
     // Hmm, that didn't work. Maybe if we create a new
     // kARGB_8888_Config version of the bitmap it will work better?
     SkBitmap copyBitmap;
-    bitmap.copyTo(&copyBitmap, SkBitmap::kARGB_8888_Config);
-    const SkBitmapTransformer copyTransformer =
-        SkBitmapTransformer(copyBitmap, kPixelFormat);
-    if (copyTransformer.isValid(true)) {
-        return ComputeDigestInternal(copyBitmap, copyTransformer, result);
-    } else {
+    if (!bitmap.copyTo(&copyBitmap, SkBitmap::kARGB_8888_Config)) {
         return false;
     }
+    return ComputeDigestInternal(copyBitmap, result);
 }
diff --git a/src/utils/SkBitmapHasher.h b/src/utils/SkBitmapHasher.h
index dc8685b..165da3d 100644
--- a/src/utils/SkBitmapHasher.h
+++ b/src/utils/SkBitmapHasher.h
@@ -10,7 +10,6 @@
 #define SkBitmapHasher_DEFINED
 
 #include "SkBitmap.h"
-#include "SkBitmapTransformer.h"
 
 // TODO(epoger): Soon, SkHashDigest will become a real class of its own,
 // and callers won't be able to assume it converts to/from a uint64_t.
@@ -34,9 +33,7 @@
     static bool ComputeDigest(const SkBitmap& bitmap, SkHashDigest *result);
 
 private:
-    static bool ComputeDigestInternal(const SkBitmap& bitmap,
-                                      const SkBitmapTransformer& transformer,
-                                      SkHashDigest *result);
+    static bool ComputeDigestInternal(const SkBitmap& bitmap, SkHashDigest *result);
 };
 
 #endif
diff --git a/src/utils/SkBitmapTransformer.cpp b/src/utils/SkBitmapTransformer.cpp
deleted file mode 100644
index c8356d4..0000000
--- a/src/utils/SkBitmapTransformer.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkBitmapTransformer.h"
-#include "SkColorPriv.h"
-#include "SkTypes.h"
-
-bool SkBitmapTransformer::isValid(bool logReason) const {
-    bool retval = true;
-
-    switch(fPixelFormat) {
-    case kARGB_8888_Premul_PixelFormat:
-        break;
-    default:
-        if (logReason) {
-            SkDEBUGF(("PixelFormat %d not supported\n", fPixelFormat));
-        }
-        retval = false;
-    }
-
-    SkBitmap::Config bitmapConfig = fBitmap.config();
-    switch(bitmapConfig) {
-    case SkBitmap::kARGB_8888_Config:
-        break;
-    default:
-        if (logReason) {
-            SkDEBUGF(("SkBitmap::Config %d not supported\n", bitmapConfig));
-        }
-        retval = false;
-    }
-
-    return retval;
-}
-
-/**
- * Transform from kARGB_8888_Config to kARGB_8888_Premul_PixelFormat.
- *
- * Similar to the various scanline transformers in
- * src/images/transform_scanline.h .
- */
-static void transform_scanline(const char* SK_RESTRICT src, int width,
-                               char* SK_RESTRICT dst) {
-    const SkPMColor* SK_RESTRICT srcP = reinterpret_cast<const SkPMColor*>(src);
-    for (int i = 0; i < width; i++) {
-        SkPMColor c = *srcP++;
-        unsigned a = SkGetPackedA32(c);
-        unsigned r = SkGetPackedR32(c);
-        unsigned g = SkGetPackedG32(c);
-        unsigned b = SkGetPackedB32(c);
-        *dst++ = a;
-        *dst++ = r;
-        *dst++ = g;
-        *dst++ = b;
-    }
-}
-
-bool SkBitmapTransformer::copyBitmapToPixelBuffer(void *dstBuffer,
-                                                  size_t dstBufferSize) const {
-    if (!this->isValid(true)) {
-        return false;
-    }
-    size_t bytesNeeded = this->bytesNeededTotal();
-    if (dstBufferSize < bytesNeeded) {
-        SkDEBUGF(("dstBufferSize %d must be >= %d\n", dstBufferSize, bytesNeeded));
-        return false;
-    }
-
-    fBitmap.lockPixels();
-    int width = fBitmap.width();
-    size_t srcRowBytes = fBitmap.rowBytes();
-    size_t dstRowBytes = this->bytesNeededPerRow();
-    const char *srcBytes = const_cast<const char *>(static_cast<char*>(fBitmap.getPixels()));
-    char *dstBytes = static_cast<char *>(dstBuffer);
-    for (int y = 0; y < fBitmap.height(); y++) {
-        transform_scanline(srcBytes, width, dstBytes);
-        srcBytes += srcRowBytes;
-        dstBytes += dstRowBytes;
-    }
-    fBitmap.unlockPixels();
-    return true;
-}
diff --git a/src/utils/SkBitmapTransformer.h b/src/utils/SkBitmapTransformer.h
deleted file mode 100644
index 70971ac..0000000
--- a/src/utils/SkBitmapTransformer.h
+++ /dev/null
@@ -1,104 +0,0 @@
-
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkBitmapTransformer_DEFINED
-#define SkBitmapTransformer_DEFINED
-
-#include "SkBitmap.h"
-
-/**
- * Class that can copy pixel data out of an SkBitmap, transforming it
- * into the appropriate PixelFormat.
- *
- * As noted in https://codereview.appspot.com/6849119/#msg6 and
- * https://codereview.appspot.com/6900047 , at some point we might want
- * to make this more general purpose:
- * - support more PixelFormats
- * - use existing SkCanvas::Config8888 enum instead of new PixelFormat enum
- * - add method to copy pixel data for a single row, instead of the whole bitmap
- * - add methods to copy pixel data INTO an SkBitmap
- *
- * That would allow us to replace SkCopyConfig8888ToBitmap() in
- * src/core/SkConfig8888.h , as well as the transformations used by
- * src/images/SkImageDecoder_libpng.cpp , with this common code.
- *
- * But for now, we want something more narrowly targeted, just
- * supplying what is needed by SkBitmapChecksummer.
- */
-class SkBitmapTransformer {
-public:
-    enum PixelFormat {
-        // 32 bits per pixel, ARGB byte order, with the alpha-channel
-        // value premultiplied into the R/G/B channel values.
-        kARGB_8888_Premul_PixelFormat,
-
-        // marks the end of the list
-        kLast_PixelFormat = kARGB_8888_Premul_PixelFormat,
-    };
-
-    /**
-     * Creates an SkBitmapTransformer instance that can transform between
-     * the given bitmap and a pixel buffer with given pixelFormat.
-     *
-     * Call IsValid() before using, to confirm that this particular
-     * bitmap/pixelFormat combination is supported!
-     */
-    SkBitmapTransformer(const SkBitmap& bitmap, PixelFormat pixelFormat) :
-        fBitmap(bitmap), fPixelFormat(pixelFormat) {}
-
-    /**
-     * Returns true iff we can convert between fBitmap and fPixelFormat.
-     * If this returns false, the return values of any other methods will
-     * be meaningless!
-     *
-     * @param logReason whether to log the reason why this combination
-     *                  is unsupported (only applies in debug mode)
-     */
-    bool isValid(bool logReason=false) const;
-
-    /**
-     * Returns the number of bytes needed to store a single row of the
-     * bitmap's pixels if converted to pixelFormat.
-     */
-    size_t bytesNeededPerRow() const {
-        // This is hard-coded for the single supported PixelFormat.
-        return fBitmap.width() * 4;
-    }
-
-    /**
-     * Returns the number of bytes needed to store the entire bitmap
-     * if converted to pixelFormat, ASSUMING that it is written
-     * out as a single contiguous blob of pixels (no leftover bytes
-     * at the end of each row).
-     */
-    size_t bytesNeededTotal() const {
-        return this->bytesNeededPerRow() * fBitmap.height();
-    }
-
-    /**
-     * Writes the entire bitmap into dstBuffer, using the already-specified
-     * pixelFormat. Returns true if successful.
-     *
-     * dstBufferSize is the maximum allowable bytes to write into dstBuffer;
-     * if that is not large enough to hold the entire bitmap, then this
-     * will fail immediately and return false.
-     * We force the caller to pass this in to avoid buffer overruns in
-     * unanticipated cases.
-     *
-     * All pixels for all rows will be written into dstBuffer as a
-     * single contiguous blob (no skipped pixels at the end of each
-     * row).
-     */
-    bool copyBitmapToPixelBuffer (void *dstBuffer, size_t dstBufferSize) const;
-
-private:
-    const SkBitmap& fBitmap;
-    const PixelFormat fPixelFormat;
-};
-
-#endif
diff --git a/src/utils/SkMD5.h b/src/utils/SkMD5.h
index 6fbdb46..6b4fc53 100644
--- a/src/utils/SkMD5.h
+++ b/src/utils/SkMD5.h
@@ -17,7 +17,7 @@
 //SK_CPU_LENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
 //SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_LENDIAN.
 
-class SkMD5 : SkWStream {
+class SkMD5 : public SkWStream {
 public:
     SkMD5();
 
diff --git a/src/utils/SkSHA1.h b/src/utils/SkSHA1.h
index 10bbc43..cf2cb8c 100644
--- a/src/utils/SkSHA1.h
+++ b/src/utils/SkSHA1.h
@@ -17,7 +17,7 @@
 //SK_CPU_BENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
 //SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_BENDIAN.
 
-class SkSHA1 : SkWStream {
+class SkSHA1 : public SkWStream {
 public:
     SkSHA1();