SkCodec: add wbmp class
Review URL: https://codereview.chromium.org/1006583005
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index a49586f..33cc9e1 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -10,6 +10,7 @@
#include "SkCodec_libbmp.h"
#include "SkCodec_libico.h"
#include "SkCodec_libpng.h"
+#include "SkCodec_wbmp.h"
#include "SkStream.h"
struct DecoderProc {
@@ -20,7 +21,8 @@
static const DecoderProc gDecoderProcs[] = {
{ SkPngCodec::IsPng, SkPngCodec::NewFromStream },
{ SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
- { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream }
+ { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
+ { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
};
SkCodec* SkCodec::NewFromStream(SkStream* stream) {
@@ -56,12 +58,16 @@
, fNeedsRewind(false)
{}
-bool SkCodec::rewindIfNeeded() {
+SkCodec::RewindState SkCodec::rewindIfNeeded() {
// Store the value of fNeedsRewind so we can update it. Next read will
// require a rewind.
- const bool neededRewind = fNeedsRewind;
+ const bool needsRewind = fNeedsRewind;
fNeedsRewind = true;
- return !neededRewind || fStream->rewind();
+ if (!needsRewind) {
+ return kNoRewindNecessary_RewindState;
+ }
+ return fStream->rewind() ? kRewound_RewindState
+ : kCouldNotRewind_RewindState;
}
SkScanlineDecoder* SkCodec::getScanlineDecoder(const SkImageInfo& dstInfo) {
diff --git a/src/codec/SkCodec_libbmp.cpp b/src/codec/SkCodec_libbmp.cpp
index d0c9623..25f5459 100644
--- a/src/codec/SkCodec_libbmp.cpp
+++ b/src/codec/SkCodec_libbmp.cpp
@@ -519,8 +519,11 @@
const Options&,
SkPMColor*, int*) {
// Check for proper input and output formats
- if (!this->rewindIfNeeded()) {
+ SkCodec::RewindState rewindState = this->rewindIfNeeded();
+ if (rewindState == kCouldNotRewind_RewindState) {
return kCouldNotRewind;
+ } else if (rewindState == kRewound_RewindState) {
+ return kCouldNotRewind; // TODO(msarett): handle rewinds
}
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
SkCodecPrintf("Error: scaling not supported.\n");
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 4850235..9330ac5 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -420,7 +420,11 @@
SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
size_t rowBytes, const Options& options,
SkPMColor ctable[], int* ctableCount) {
- if (!this->rewindIfNeeded()) {
+ SkCodec::RewindState rewindState = this->rewindIfNeeded();
+ if (rewindState == kCouldNotRewind_RewindState) {
+ return kCouldNotRewind;
+ } else if (rewindState == kRewound_RewindState) {
+ // TODO(scroggo): handle rewinds
return kCouldNotRewind;
}
if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
new file mode 100644
index 0000000..465c76d
--- /dev/null
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkCodec_wbmp.h"
+
+// http://en.wikipedia.org/wiki/Variable-length_quantity
+static bool read_mbf(SkStream* stream, uint64_t* value) {
+ uint64_t n = 0;
+ uint8_t data;
+ const uint64_t kLimit = 0xFE00000000000000;
+ SkASSERT(kLimit == ~((~static_cast<uint64_t>(0)) >> 7));
+ do {
+ if (n & kLimit) { // Will overflow on shift by 7.
+ return false;
+ }
+ if (stream->read(&data, 1) != 1) {
+ return false;
+ }
+ n = (n << 7) | (data & 0x7F);
+ } while (data & 0x80);
+ *value = n;
+ return true;
+}
+
+static bool read_header(SkStream* stream, SkISize* size) {
+ uint64_t width, height;
+ uint16_t data;
+ if (stream->read(&data, 2) != 2 || data != 0) {
+ return false;
+ }
+ if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
+ return false;
+ }
+ if (!read_mbf(stream, &height) || width > 0xFFFF || !height) {
+ return false;
+ }
+ if (size) {
+ *size = SkISize::Make(SkToS32(width), SkToS32(height));
+ }
+ 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);
+ }
+ dst += 8;
+ }
+ bits &= 7;
+ if (bits > 0) {
+ U8CPU mask = *src;
+ do {
+ *dst++ = TRANSFORM((mask >> 7) & 1);
+ mask <<= 1;
+ } while (--bits != 0);
+ }
+}
+
+SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
+ : INHERITED(info, stream) {}
+
+SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
+ return kWBMP_SkEncodedFormat;
+}
+
+SkImageGenerator::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
+ void* pixels,
+ size_t rowBytes,
+ const Options&,
+ SkPMColor ctable[],
+ int* ctableCount) {
+ SkCodec::RewindState rewindState = this->rewindIfNeeded();
+ if (rewindState == kCouldNotRewind_RewindState) {
+ return SkImageGenerator::kCouldNotRewind;
+ } else if (rewindState == kRewound_RewindState) {
+ (void)read_header(this->stream(), NULL);
+ }
+ if (info.dimensions() != this->getInfo().dimensions()) {
+ return SkImageGenerator::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 SkImageGenerator::kInvalidConversion;
+ }
+ SkISize size = info.dimensions();
+ uint8_t* dst = static_cast<uint8_t*>(pixels);
+ size_t srcRowBytes = SkAlign8(size.width()) >> 3;
+ SkAutoTMalloc<uint8_t> src(srcRowBytes);
+ for (int y = 0; y < size.height(); ++y) {
+ if (this->stream()->read(src.get(), srcRowBytes) != srcRowBytes) {
+ return SkImageGenerator::kIncompleteInput;
+ }
+ proc(dst, src.get(), size.width());
+ dst += rowBytes;
+ }
+ return SkImageGenerator::kSuccess;
+}
+
+bool SkWbmpCodec::IsWbmp(SkStream* stream) {
+ return read_header(stream, NULL);
+}
+
+SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
+ SkISize size;
+ if (!read_header(stream, &size)) {
+ return NULL;
+ }
+ SkImageInfo info =
+ SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType,
+ kOpaque_SkAlphaType);
+ return SkNEW_ARGS(SkWbmpCodec, (info, stream));
+}
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
new file mode 100644
index 0000000..fece249
--- /dev/null
+++ b/src/codec/SkCodec_wbmp.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCodec_wbmp_DEFINED
+#define SkCodec_wbmp_DEFINED
+
+#include "SkCodec.h"
+
+class SkWbmpCodec final : public SkCodec {
+public:
+ static bool IsWbmp(SkStream*);
+ static SkCodec* NewFromStream(SkStream*);
+protected:
+ SkEncodedFormat onGetEncodedFormat() const override;
+ Result onGetPixels(const SkImageInfo&, void*, size_t,
+ const Options&, SkPMColor[], int*) override;
+private:
+ SkWbmpCodec(const SkImageInfo&, SkStream*);
+ typedef SkCodec INHERITED;
+};
+
+#endif // SkCodec_wbmp_DEFINED
diff --git a/src/utils/SkMD5.h b/src/utils/SkMD5.h
index 1834a1b..ed55793 100644
--- a/src/utils/SkMD5.h
+++ b/src/utils/SkMD5.h
@@ -36,6 +36,12 @@
struct Digest {
uint8_t data[16];
+ bool operator ==(Digest const& other) const {
+ return 0 == memcmp(data, other.data, sizeof(data));
+ }
+ bool operator !=(Digest const& other) const {
+ return 0 != memcmp(data, other.data, sizeof(data));
+ }
};
/** Computes and returns the digest. */