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 "SkImageDecoder.h"
12 #include "SkScaledBitmapSampler.h"
13 #include "SkStream.h"
14 #include "SkColorPriv.h"
15 #include "SkTDArray.h"
16 #include "SkTRegistry.h"
17
18 class SkBMPImageDecoder : public SkImageDecoder {
19 public:
SkBMPImageDecoder()20 SkBMPImageDecoder() {}
21
getFormat() const22 virtual Format getFormat() const {
23 return kBMP_Format;
24 }
25
26 protected:
27 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
28 };
29
Factory(SkStream * stream)30 static SkImageDecoder* Factory(SkStream* stream) {
31 static const char kBmpMagic[] = { 'B', 'M' };
32
33 size_t len = stream->getLength();
34 char buffer[sizeof(kBmpMagic)];
35
36 if (len > sizeof(kBmpMagic) &&
37 stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
38 !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
39 return SkNEW(SkBMPImageDecoder);
40 }
41 return NULL;
42 }
43
44 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
45
46 ///////////////////////////////////////////////////////////////////////////////
47
48 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
49 public:
50 // we don't copy the bitmap, just remember the pointer
SkBmpDecoderCallback(bool justBounds)51 SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
52
53 // override from BmpDecoderCallback
SetSize(int width,int height)54 virtual uint8* SetSize(int width, int height) {
55 fWidth = width;
56 fHeight = height;
57 if (fJustBounds) {
58 return NULL;
59 }
60
61 fRGB.setCount(width * height * 3); // 3 == r, g, b
62 return fRGB.begin();
63 }
64
width() const65 int width() const { return fWidth; }
height() const66 int height() const { return fHeight; }
rgb() const67 uint8_t* rgb() const { return fRGB.begin(); }
68
69 private:
70 SkTDArray<uint8_t> fRGB;
71 int fWidth;
72 int fHeight;
73 bool fJustBounds;
74 };
75
onDecode(SkStream * stream,SkBitmap * bm,Mode mode)76 bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
77
78 size_t length = stream->getLength();
79 SkAutoMalloc storage(length);
80
81 if (stream->read(storage.get(), length) != length) {
82 return false;
83 }
84
85 const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
86 SkBmpDecoderCallback callback(justBounds);
87
88 // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
89 {
90 image_codec::BmpDecoderHelper helper;
91 const int max_pixels = 16383*16383; // max width*height
92 if (!helper.DecodeImage((const char*)storage.get(), length,
93 max_pixels, &callback)) {
94 return false;
95 }
96 }
97
98 // we don't need this anymore, so free it now (before we try to allocate
99 // the bitmap's pixels) rather than waiting for its destructor
100 storage.free();
101
102 int width = callback.width();
103 int height = callback.height();
104 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
105
106 // only accept prefConfig if it makes sense for us
107 if (SkBitmap::kARGB_4444_Config != config &&
108 SkBitmap::kRGB_565_Config != config) {
109 config = SkBitmap::kARGB_8888_Config;
110 }
111
112 SkScaledBitmapSampler sampler(width, height, getSampleSize());
113
114 if (justBounds) {
115 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
116 bm->setIsOpaque(true);
117 return true;
118 }
119 #ifdef SK_BUILD_FOR_ANDROID
120 // No Bitmap reuse supported for this format
121 if (!bm->isNull()) {
122 return false;
123 }
124 #endif
125 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
126 bm->setIsOpaque(true);
127
128 if (!this->allocPixelRef(bm, NULL)) {
129 return false;
130 }
131
132 SkAutoLockPixels alp(*bm);
133
134 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
135 return false;
136 }
137
138 const int srcRowBytes = width * 3;
139 const int dstHeight = sampler.scaledHeight();
140 const uint8_t* srcRow = callback.rgb();
141
142 srcRow += sampler.srcY0() * srcRowBytes;
143 for (int y = 0; y < dstHeight; y++) {
144 sampler.next(srcRow);
145 srcRow += sampler.srcDY() * srcRowBytes;
146 }
147 return true;
148 }
149