michael@0: michael@0: /* michael@0: * Copyright 2007 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 "bmpdecoderhelper.h" michael@0: #include "SkColorPriv.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkScaledBitmapSampler.h" michael@0: #include "SkStream.h" michael@0: #include "SkStreamHelpers.h" michael@0: #include "SkTDArray.h" michael@0: michael@0: class SkBMPImageDecoder : public SkImageDecoder { michael@0: public: michael@0: SkBMPImageDecoder() {} michael@0: michael@0: virtual Format getFormat() const SK_OVERRIDE { michael@0: return kBMP_Format; michael@0: } michael@0: michael@0: protected: michael@0: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; michael@0: michael@0: private: michael@0: typedef SkImageDecoder INHERITED; michael@0: }; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: DEFINE_DECODER_CREATOR(BMPImageDecoder); michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static bool is_bmp(SkStreamRewindable* stream) { michael@0: static const char kBmpMagic[] = { 'B', 'M' }; michael@0: michael@0: michael@0: char buffer[sizeof(kBmpMagic)]; michael@0: michael@0: return stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) && michael@0: !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic)); michael@0: } michael@0: michael@0: static SkImageDecoder* sk_libbmp_dfactory(SkStreamRewindable* stream) { michael@0: if (is_bmp(stream)) { michael@0: return SkNEW(SkBMPImageDecoder); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: static SkImageDecoder_DecodeReg gReg(sk_libbmp_dfactory); michael@0: michael@0: static SkImageDecoder::Format get_format_bmp(SkStreamRewindable* stream) { michael@0: if (is_bmp(stream)) { michael@0: return SkImageDecoder::kBMP_Format; michael@0: } michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: michael@0: static SkImageDecoder_FormatReg gFormatReg(get_format_bmp); michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback { michael@0: public: michael@0: // we don't copy the bitmap, just remember the pointer michael@0: SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {} michael@0: michael@0: // override from BmpDecoderCallback michael@0: virtual uint8* SetSize(int width, int height) { michael@0: fWidth = width; michael@0: fHeight = height; michael@0: if (fJustBounds) { michael@0: return NULL; michael@0: } michael@0: michael@0: fRGB.setCount(width * height * 3); // 3 == r, g, b michael@0: return fRGB.begin(); michael@0: } michael@0: michael@0: int width() const { return fWidth; } michael@0: int height() const { return fHeight; } michael@0: const uint8_t* rgb() const { return fRGB.begin(); } michael@0: michael@0: private: michael@0: SkTDArray fRGB; michael@0: int fWidth; michael@0: int fHeight; michael@0: bool fJustBounds; michael@0: }; michael@0: michael@0: bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { michael@0: // First read the entire stream, so that all of the data can be passed to michael@0: // the BmpDecoderHelper. michael@0: michael@0: // Allocated space used to hold the data. michael@0: SkAutoMalloc storage; michael@0: // Byte length of all of the data. michael@0: const size_t length = CopyStreamToStorage(&storage, stream); michael@0: if (0 == length) { michael@0: return 0; michael@0: } michael@0: michael@0: const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode; michael@0: SkBmpDecoderCallback callback(justBounds); michael@0: michael@0: // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...] michael@0: { michael@0: image_codec::BmpDecoderHelper helper; michael@0: const int max_pixels = 16383*16383; // max width*height michael@0: if (!helper.DecodeImage((const char*)storage.get(), length, michael@0: max_pixels, &callback)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // we don't need this anymore, so free it now (before we try to allocate michael@0: // the bitmap's pixels) rather than waiting for its destructor michael@0: storage.free(); michael@0: michael@0: int width = callback.width(); michael@0: int height = callback.height(); michael@0: SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false); michael@0: michael@0: // only accept prefConfig if it makes sense for us michael@0: if (SkBitmap::kARGB_4444_Config != config && michael@0: SkBitmap::kRGB_565_Config != config) { michael@0: config = SkBitmap::kARGB_8888_Config; michael@0: } michael@0: michael@0: SkScaledBitmapSampler sampler(width, height, getSampleSize()); michael@0: michael@0: bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight(), 0, michael@0: kOpaque_SkAlphaType); michael@0: michael@0: if (justBounds) { michael@0: return true; michael@0: } michael@0: michael@0: if (!this->allocPixelRef(bm, NULL)) { michael@0: return false; michael@0: } michael@0: michael@0: SkAutoLockPixels alp(*bm); michael@0: michael@0: if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { michael@0: return false; michael@0: } michael@0: michael@0: const int srcRowBytes = width * 3; michael@0: const int dstHeight = sampler.scaledHeight(); michael@0: const uint8_t* srcRow = callback.rgb(); michael@0: michael@0: srcRow += sampler.srcY0() * srcRowBytes; michael@0: for (int y = 0; y < dstHeight; y++) { michael@0: sampler.next(srcRow); michael@0: srcRow += sampler.srcDY() * srcRowBytes; michael@0: } michael@0: return true; michael@0: }