blob: a4dcbf61742c5e6fb705d101aa07d3ae638e904f [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
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"
reed@android.com00bf85a2009-01-22 13:04:56 +000023#include "SkTRegistry.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
25class SkBMPImageDecoder : public SkImageDecoder {
26public:
27 SkBMPImageDecoder() {}
28
29 virtual Format getFormat() const {
30 return kBMP_Format;
31 }
32
33protected:
34 virtual bool onDecode(SkStream* stream, SkBitmap* bm,
35 SkBitmap::Config pref, Mode mode);
36};
37
reed@android.com00bf85a2009-01-22 13:04:56 +000038static SkImageDecoder* Factory(SkStream* stream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039 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
reed@android.com00bf85a2009-01-22 13:04:56 +000052static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
53
reed@android.com8a1c16f2008-12-17 15:59:43 +000054///////////////////////////////////////////////////////////////////////////////
55
56class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
57public:
58 // we don't copy the bitmap, just remember the pointer
59 SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
60
61 // override from BmpDecoderCallback
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
73 int width() const { return fWidth; }
74 int height() const { return fHeight; }
75 uint8_t* rgb() const { return fRGB.begin(); }
76
77private:
78 SkTDArray<uint8_t> fRGB;
79 int fWidth;
80 int fHeight;
81 bool fJustBounds;
82};
83
84bool 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}