diff options
author | 2015-03-27 12:16:53 -0700 | |
---|---|---|
committer | 2015-03-27 12:16:53 -0700 | |
commit | a096d7a6d03662073f4cd46f7db5fe2cf5495c36 (patch) | |
tree | dcaf6ab6002e37a9525a82101ac640f2ead1bf46 /src/codec/SkCodec_wbmp.cpp | |
parent | 135b7ecaa81018aa497da6ef8a8493263df456ef (diff) |
SkCodec: add wbmp class
Review URL: https://codereview.chromium.org/1006583005
Diffstat (limited to 'src/codec/SkCodec_wbmp.cpp')
-rw-r--r-- | src/codec/SkCodec_wbmp.cpp | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp new file mode 100644 index 0000000000..465c76d4dc --- /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)); +} |