1
2/*
3 * Copyright 2007 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "bmpdecoderhelper.h"
11#include "SkColorPriv.h"
12#include "SkImageDecoder.h"
13#include "SkScaledBitmapSampler.h"
14#include "SkStream.h"
15#include "SkStreamHelpers.h"
16#include "SkTDArray.h"
17#include "SkTRegistry.h"
18
19class SkBMPImageDecoder : public SkImageDecoder {
20public:
21    SkBMPImageDecoder() {}
22
23    virtual Format getFormat() const SK_OVERRIDE {
24        return kBMP_Format;
25    }
26
27protected:
28    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
29
30private:
31    typedef SkImageDecoder INHERITED;
32};
33
34///////////////////////////////////////////////////////////////////////////////
35DEFINE_DECODER_CREATOR(BMPImageDecoder);
36///////////////////////////////////////////////////////////////////////////////
37
38static bool is_bmp(SkStream* stream) {
39    static const char kBmpMagic[] = { 'B', 'M' };
40
41
42    char buffer[sizeof(kBmpMagic)];
43
44    return stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
45        !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic));
46}
47
48static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
49    if (is_bmp(stream)) {
50        return SkNEW(SkBMPImageDecoder);
51    }
52    return NULL;
53}
54
55static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libbmp_dfactory);
56
57static SkImageDecoder::Format get_format_bmp(SkStream* stream) {
58    if (is_bmp(stream)) {
59        return SkImageDecoder::kBMP_Format;
60    }
61    return SkImageDecoder::kUnknown_Format;
62}
63
64static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_bmp);
65
66///////////////////////////////////////////////////////////////////////////////
67
68class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
69public:
70    // we don't copy the bitmap, just remember the pointer
71    SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
72
73    // override from BmpDecoderCallback
74    virtual uint8* SetSize(int width, int height) {
75        fWidth = width;
76        fHeight = height;
77        if (fJustBounds) {
78            return NULL;
79        }
80
81        fRGB.setCount(width * height * 3);  // 3 == r, g, b
82        return fRGB.begin();
83    }
84
85    int width() const { return fWidth; }
86    int height() const { return fHeight; }
87    const uint8_t* rgb() const { return fRGB.begin(); }
88
89private:
90    SkTDArray<uint8_t> fRGB;
91    int fWidth;
92    int fHeight;
93    bool fJustBounds;
94};
95
96bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
97    // First read the entire stream, so that all of the data can be passed to
98    // the BmpDecoderHelper.
99
100    // Allocated space used to hold the data.
101    SkAutoMalloc storage;
102    // Byte length of all of the data.
103    const size_t length = CopyStreamToStorage(&storage, stream);
104    if (0 == length) {
105        return 0;
106    }
107
108    const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
109    SkBmpDecoderCallback callback(justBounds);
110
111    // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
112    {
113        image_codec::BmpDecoderHelper helper;
114        const int max_pixels = 16383*16383; // max width*height
115        if (!helper.DecodeImage((const char*)storage.get(), length,
116                                max_pixels, &callback)) {
117            return false;
118        }
119    }
120
121    // we don't need this anymore, so free it now (before we try to allocate
122    // the bitmap's pixels) rather than waiting for its destructor
123    storage.free();
124
125    int width = callback.width();
126    int height = callback.height();
127    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
128
129    // only accept prefConfig if it makes sense for us
130    if (SkBitmap::kARGB_4444_Config != config &&
131            SkBitmap::kRGB_565_Config != config) {
132        config = SkBitmap::kARGB_8888_Config;
133    }
134
135    SkScaledBitmapSampler sampler(width, height, getSampleSize());
136
137    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
138    bm->setIsOpaque(true);
139
140    if (justBounds) {
141        return true;
142    }
143
144    if (!this->allocPixelRef(bm, NULL)) {
145        return false;
146    }
147
148    SkAutoLockPixels alp(*bm);
149
150    if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {
151        return false;
152    }
153
154    const int srcRowBytes = width * 3;
155    const int dstHeight = sampler.scaledHeight();
156    const uint8_t* srcRow = callback.rgb();
157
158    srcRow += sampler.srcY0() * srcRowBytes;
159    for (int y = 0; y < dstHeight; y++) {
160        sampler.next(srcRow);
161        srcRow += sampler.srcDY() * srcRowBytes;
162    }
163    return true;
164}
165