1 /*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "bmpdecoderhelper.h"
18 #include "SkImageDecoder.h"
19 #include "SkScaledBitmapSampler.h"
20 #include "SkStream.h"
21 #include "SkColorPriv.h"
22 #include "SkTDArray.h"
23 #include "SkTRegistry.h"
24
25 class SkBMPImageDecoder : public SkImageDecoder {
26 public:
SkBMPImageDecoder()27 SkBMPImageDecoder() {}
28
getFormat() const29 virtual Format getFormat() const {
30 return kBMP_Format;
31 }
32
33 protected:
34 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
35 SkBitmap::Config pref, Mode mode);
36 };
37
Factory(SkStream * stream)38 static SkImageDecoder* Factory(SkStream* stream) {
39 static const char kBmpMagic[] = { 'B', 'M' };
40
41 size_t len = stream->getLength();
42 char buffer[sizeof(kBmpMagic)];
43
44 if (len > sizeof(kBmpMagic) &&
45 stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
46 !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
47 return SkNEW(SkBMPImageDecoder);
48 }
49 return NULL;
50 }
51
52 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
53
54 ///////////////////////////////////////////////////////////////////////////////
55
56 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
57 public:
58 // we don't copy the bitmap, just remember the pointer
SkBmpDecoderCallback(bool justBounds)59 SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
60
61 // override from BmpDecoderCallback
SetSize(int width,int height)62 virtual uint8* SetSize(int width, int height) {
63 fWidth = width;
64 fHeight = height;
65 if (fJustBounds) {
66 return NULL;
67 }
68
69 fRGB.setCount(width * height * 3); // 3 == r, g, b
70 return fRGB.begin();
71 }
72
width() const73 int width() const { return fWidth; }
height() const74 int height() const { return fHeight; }
rgb() const75 uint8_t* rgb() const { return fRGB.begin(); }
76
77 private:
78 SkTDArray<uint8_t> fRGB;
79 int fWidth;
80 int fHeight;
81 bool fJustBounds;
82 };
83
onDecode(SkStream * stream,SkBitmap * bm,SkBitmap::Config prefConfig,Mode mode)84 bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
85 SkBitmap::Config prefConfig, Mode mode) {
86
87 size_t length = stream->getLength();
88 SkAutoMalloc storage(length);
89
90 if (stream->read(storage.get(), length) != length) {
91 return false;
92 }
93
94 const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
95 SkBmpDecoderCallback callback(justBounds);
96
97 // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
98 {
99 image_codec::BmpDecoderHelper helper;
100 const int max_pixels = 16383*16383; // max width*height
101 if (!helper.DecodeImage((const char*)storage.get(), length,
102 max_pixels, &callback)) {
103 return false;
104 }
105 }
106
107 // we don't need this anymore, so free it now (before we try to allocate
108 // the bitmap's pixels) rather than waiting for its destructor
109 storage.free();
110
111 int width = callback.width();
112 int height = callback.height();
113 SkBitmap::Config config = SkBitmap::kARGB_8888_Config;
114
115 // only accept prefConfig if it makes sense for us
116 if (SkBitmap::kARGB_4444_Config == prefConfig ||
117 SkBitmap::kRGB_565_Config == config) {
118 config = prefConfig;
119 }
120
121 SkScaledBitmapSampler sampler(width, height, getSampleSize());
122
123 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
124 bm->setIsOpaque(true);
125 if (justBounds) {
126 return true;
127 }
128
129 if (!this->allocPixelRef(bm, NULL)) {
130 return false;
131 }
132
133 SkAutoLockPixels alp(*bm);
134
135 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
136 return false;
137 }
138
139 const int srcRowBytes = width * 3;
140 const int dstHeight = sampler.scaledHeight();
141 const uint8_t* srcRow = callback.rgb();
142
143 srcRow += sampler.srcY0() * srcRowBytes;
144 for (int y = 0; y < dstHeight; y++) {
145 sampler.next(srcRow);
146 srcRow += sampler.srcDY() * srcRowBytes;
147 }
148 return true;
149 }
150