michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkImageDecoder.h" michael@0: #include "SkColor.h" michael@0: #include "SkColorPriv.h" michael@0: #include "SkMath.h" michael@0: #include "SkStream.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkUtils.h" michael@0: michael@0: class SkWBMPImageDecoder : public SkImageDecoder { michael@0: public: michael@0: virtual Format getFormat() const SK_OVERRIDE { michael@0: return kWBMP_Format; michael@0: } michael@0: michael@0: protected: michael@0: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; michael@0: michael@0: private: michael@0: typedef SkImageDecoder INHERITED; michael@0: }; michael@0: michael@0: static bool read_byte(SkStream* stream, uint8_t* data) michael@0: { michael@0: return stream->read(data, 1) == 1; michael@0: } michael@0: michael@0: static bool read_mbf(SkStream* stream, int* value) michael@0: { michael@0: int n = 0; michael@0: uint8_t data; michael@0: do { michael@0: if (!read_byte(stream, &data)) { michael@0: return false; michael@0: } michael@0: n = (n << 7) | (data & 0x7F); michael@0: } while (data & 0x80); michael@0: michael@0: *value = n; michael@0: return true; michael@0: } michael@0: michael@0: struct wbmp_head { michael@0: int fWidth; michael@0: int fHeight; michael@0: michael@0: bool init(SkStream* stream) michael@0: { michael@0: uint8_t data; michael@0: michael@0: if (!read_byte(stream, &data) || data != 0) { // unknown type michael@0: return false; michael@0: } michael@0: if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header michael@0: return false; michael@0: } michael@0: if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) { michael@0: return false; michael@0: } michael@0: if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) { michael@0: return false; michael@0: } michael@0: return fWidth != 0 && fHeight != 0; michael@0: } michael@0: }; michael@0: michael@0: static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits) michael@0: { michael@0: int bytes = bits >> 3; michael@0: michael@0: for (int i = 0; i < bytes; i++) { michael@0: unsigned mask = *src++; michael@0: dst[0] = (mask >> 7) & 1; michael@0: dst[1] = (mask >> 6) & 1; michael@0: dst[2] = (mask >> 5) & 1; michael@0: dst[3] = (mask >> 4) & 1; michael@0: dst[4] = (mask >> 3) & 1; michael@0: dst[5] = (mask >> 2) & 1; michael@0: dst[6] = (mask >> 1) & 1; michael@0: dst[7] = (mask >> 0) & 1; michael@0: dst += 8; michael@0: } michael@0: michael@0: bits &= 7; michael@0: if (bits > 0) { michael@0: unsigned mask = *src; michael@0: do { michael@0: *dst++ = (mask >> 7) & 1;; michael@0: mask <<= 1; michael@0: } while (--bits != 0); michael@0: } michael@0: } michael@0: michael@0: bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, michael@0: Mode mode) michael@0: { michael@0: wbmp_head head; michael@0: michael@0: if (!head.init(stream)) { michael@0: return false; michael@0: } michael@0: michael@0: int width = head.fWidth; michael@0: int height = head.fHeight; michael@0: michael@0: decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height, 0, michael@0: kOpaque_SkAlphaType); michael@0: michael@0: if (SkImageDecoder::kDecodeBounds_Mode == mode) { michael@0: return true; michael@0: } michael@0: michael@0: const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; michael@0: SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2)); michael@0: SkAutoUnref aur(ct); michael@0: michael@0: if (!this->allocPixelRef(decodedBitmap, ct)) { michael@0: return false; michael@0: } michael@0: michael@0: SkAutoLockPixels alp(*decodedBitmap); michael@0: michael@0: uint8_t* dst = decodedBitmap->getAddr8(0, 0); michael@0: // store the 1-bit valuess at the end of our pixels, so we won't stomp michael@0: // on them before we're read them. Just trying to avoid a temp allocation michael@0: size_t srcRB = SkAlign8(width) >> 3; michael@0: size_t srcSize = height * srcRB; michael@0: uint8_t* src = dst + decodedBitmap->getSize() - srcSize; michael@0: if (stream->read(src, srcSize) != srcSize) { michael@0: return false; michael@0: } michael@0: michael@0: for (int y = 0; y < height; y++) michael@0: { michael@0: expand_bits_to_bytes(dst, src, width); michael@0: dst += decodedBitmap->rowBytes(); michael@0: src += srcRB; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: DEFINE_DECODER_CREATOR(WBMPImageDecoder); michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static SkImageDecoder* sk_wbmp_dfactory(SkStreamRewindable* stream) { michael@0: wbmp_head head; michael@0: michael@0: if (head.init(stream)) { michael@0: return SkNEW(SkWBMPImageDecoder); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: static SkImageDecoder::Format get_format_wbmp(SkStreamRewindable* stream) { michael@0: wbmp_head head; michael@0: if (head.init(stream)) { michael@0: return SkImageDecoder::kWBMP_Format; michael@0: } michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: michael@0: static SkImageDecoder_DecodeReg gDReg(sk_wbmp_dfactory); michael@0: static SkImageDecoder_FormatReg gFormatReg(get_format_wbmp);