blob: 77496649c2dafa24abcf17d1bc84a0f1bd921b07 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2007 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009// Author: cevans@google.com (Chris Evans)
10
11#include "bmpdecoderhelper.h"
12
13namespace image_codec {
14
15static const int kBmpHeaderSize = 14;
16static const int kBmpInfoSize = 40;
17static const int kBmpOS2InfoSize = 12;
18static const int kMaxDim = SHRT_MAX / 2;
19
20bool BmpDecoderHelper::DecodeImage(const char* p,
21 int len,
22 int max_pixels,
23 BmpDecoderCallback* callback) {
24 data_ = reinterpret_cast<const uint8*>(p);
25 pos_ = 0;
26 len_ = len;
27 inverted_ = true;
28 // Parse the header structure.
29 if (len < kBmpHeaderSize + 4) {
30 return false;
31 }
32 GetShort(); // Signature.
33 GetInt(); // Size.
34 GetInt(); // Reserved.
35 int offset = GetInt();
36 // Parse the info structure.
37 int infoSize = GetInt();
38 if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) {
39 return false;
40 }
41 int cols = 0;
42 int comp = 0;
43 int colLen = 4;
44 if (infoSize >= kBmpInfoSize) {
45 if (len < kBmpHeaderSize + kBmpInfoSize) {
46 return false;
47 }
48 width_ = GetInt();
49 height_ = GetInt();
50 GetShort(); // Planes.
51 bpp_ = GetShort();
52 comp = GetInt();
53 GetInt(); // Size.
54 GetInt(); // XPPM.
55 GetInt(); // YPPM.
56 cols = GetInt();
57 GetInt(); // Important colours.
58 } else {
59 if (len < kBmpHeaderSize + kBmpOS2InfoSize) {
60 return false;
61 }
62 colLen = 3;
63 width_ = GetShort();
64 height_ = GetShort();
65 GetShort(); // Planes.
66 bpp_ = GetShort();
67 }
68 if (height_ < 0) {
69 height_ = -height_;
70 inverted_ = false;
71 }
72 if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) {
73 return false;
74 }
75 if (width_ * height_ > max_pixels) {
76 return false;
77 }
78 if (cols < 0 || cols > 256) {
79 return false;
80 }
81 // Allocate then read in the colour map.
82 if (cols == 0 && bpp_ <= 8) {
83 cols = 1 << bpp_;
84 }
85 if (bpp_ <= 8 || cols > 0) {
86 uint8* colBuf = new uint8[256 * 3];
87 memset(colBuf, '\0', 256 * 3);
88 colTab_.reset(colBuf);
89 }
90 if (cols > 0) {
91 if (pos_ + (cols * colLen) > len_) {
92 return false;
93 }
94 for (int i = 0; i < cols; ++i) {
95 int base = i * 3;
96 colTab_[base + 2] = GetByte();
97 colTab_[base + 1] = GetByte();
98 colTab_[base] = GetByte();
99 if (colLen == 4) {
100 GetByte();
101 }
102 }
103 }
104 // Read in the compression data if necessary.
105 redBits_ = 0x7c00;
106 greenBits_ = 0x03e0;
107 blueBits_ = 0x001f;
108 bool rle = false;
109 if (comp == 1 || comp == 2) {
110 rle = true;
111 } else if (comp == 3) {
112 if (pos_ + 12 > len_) {
113 return false;
114 }
115 redBits_ = GetInt() & 0xffff;
116 greenBits_ = GetInt() & 0xffff;
117 blueBits_ = GetInt() & 0xffff;
118 }
119 redShiftRight_ = CalcShiftRight(redBits_);
120 greenShiftRight_ = CalcShiftRight(greenBits_);
121 blueShiftRight_ = CalcShiftRight(blueBits_);
122 redShiftLeft_ = CalcShiftLeft(redBits_);
123 greenShiftLeft_ = CalcShiftLeft(greenBits_);
124 blueShiftLeft_ = CalcShiftLeft(blueBits_);
125 rowPad_ = 0;
126 pixelPad_ = 0;
127 int rowLen;
128 if (bpp_ == 32) {
129 rowLen = width_ * 4;
130 pixelPad_ = 1;
131 } else if (bpp_ == 24) {
132 rowLen = width_ * 3;
133 } else if (bpp_ == 16) {
134 rowLen = width_ * 2;
135 } else if (bpp_ == 8) {
136 rowLen = width_;
137 } else if (bpp_ == 4) {
138 rowLen = width_ / 2;
139 if (width_ & 1) {
140 rowLen++;
141 }
142 } else if (bpp_ == 1) {
143 rowLen = width_ / 8;
144 if (width_ & 7) {
145 rowLen++;
146 }
147 } else {
148 return false;
149 }
150 // Round the rowLen up to a multiple of 4.
151 if (rowLen % 4 != 0) {
152 rowPad_ = 4 - (rowLen % 4);
153 rowLen += rowPad_;
154 }
155
156 if (offset > 0 && offset > pos_ && offset < len_) {
157 pos_ = offset;
158 }
159 // Deliberately off-by-one; a load of BMPs seem to have their last byte
160 // missing.
161 if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) {
162 return false;
163 }
164
165 output_ = callback->SetSize(width_, height_);
166 if (NULL == output_) {
167 return true; // meaning we succeeded, but they want us to stop now
168 }
169
170 if (rle && (bpp_ == 4 || bpp_ == 8)) {
171 DoRLEDecode();
172 } else {
173 DoStandardDecode();
174 }
175 return true;
176}
177
178void BmpDecoderHelper::DoRLEDecode() {
179 static const uint8 RLE_ESCAPE = 0;
180 static const uint8 RLE_EOL = 0;
181 static const uint8 RLE_EOF = 1;
182 static const uint8 RLE_DELTA = 2;
183 int x = 0;
184 int y = height_ - 1;
185 while (pos_ < len_ - 1) {
186 uint8 cmd = GetByte();
187 if (cmd != RLE_ESCAPE) {
188 uint8 pixels = GetByte();
189 int num = 0;
190 uint8 col = pixels;
191 while (cmd-- && x < width_) {
192 if (bpp_ == 4) {
193 if (num & 1) {
194 col = pixels & 0xf;
195 } else {
196 col = pixels >> 4;
197 }
198 }
199 PutPixel(x++, y, col);
200 num++;
201 }
202 } else {
203 cmd = GetByte();
204 if (cmd == RLE_EOF) {
205 return;
206 } else if (cmd == RLE_EOL) {
207 x = 0;
208 y--;
209 if (y < 0) {
210 return;
211 }
212 } else if (cmd == RLE_DELTA) {
213 if (pos_ < len_ - 1) {
214 uint8 dx = GetByte();
215 uint8 dy = GetByte();
216 x += dx;
217 if (x > width_) {
218 x = width_;
219 }
220 y -= dy;
221 if (y < 0) {
222 return;
223 }
224 }
225 } else {
226 int num = 0;
227 int bytesRead = 0;
228 uint8 val = 0;
229 while (cmd-- && pos_ < len_) {
230 if (bpp_ == 8 || !(num & 1)) {
231 val = GetByte();
232 bytesRead++;
233 }
234 uint8 col = val;
235 if (bpp_ == 4) {
236 if (num & 1) {
237 col = col & 0xf;
238 } else {
239 col >>= 4;
240 }
241 }
242 if (x < width_) {
243 PutPixel(x++, y, col);
244 }
245 num++;
246 }
247 // All pixel runs must be an even number of bytes - skip a byte if we
248 // read an odd number.
249 if ((bytesRead & 1) && pos_ < len_) {
250 GetByte();
251 }
252 }
253 }
254 }
255}
256
257void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) {
258 CHECK(x >= 0 && x < width_);
259 CHECK(y >= 0 && y < height_);
260 if (!inverted_) {
261 y = height_ - (y + 1);
262 }
263
264 int base = ((y * width_) + x) * 3;
265 int colBase = col * 3;
266 output_[base] = colTab_[colBase];
267 output_[base + 1] = colTab_[colBase + 1];
268 output_[base + 2] = colTab_[colBase + 2];
269}
270
271void BmpDecoderHelper::DoStandardDecode() {
272 int row = 0;
273 uint8 currVal = 0;
274 for (int h = height_ - 1; h >= 0; h--, row++) {
275 int realH = h;
276 if (!inverted_) {
277 realH = height_ - (h + 1);
278 }
279 uint8* line = output_ + (3 * width_ * realH);
280 for (int w = 0; w < width_; w++) {
281 if (bpp_ >= 24) {
282 line[2] = GetByte();
283 line[1] = GetByte();
284 line[0] = GetByte();
285 } else if (bpp_ == 16) {
286 uint32 val = GetShort();
287 line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_;
288 line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_;
289 line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_;
290 } else if (bpp_ <= 8) {
291 uint8 col;
292 if (bpp_ == 8) {
293 col = GetByte();
294 } else if (bpp_ == 4) {
295 if ((w % 2) == 0) {
296 currVal = GetByte();
297 col = currVal >> 4;
298 } else {
299 col = currVal & 0xf;
300 }
301 } else {
302 if ((w % 8) == 0) {
303 currVal = GetByte();
304 }
305 int bit = w & 7;
306 col = ((currVal >> (7 - bit)) & 1);
307 }
308 int base = col * 3;
309 line[0] = colTab_[base];
310 line[1] = colTab_[base + 1];
311 line[2] = colTab_[base + 2];
312 }
313 line += 3;
314 for (int i = 0; i < pixelPad_; ++i) {
315 GetByte();
316 }
317 }
318 for (int i = 0; i < rowPad_; ++i) {
319 GetByte();
320 }
321 }
322}
323
324int BmpDecoderHelper::GetInt() {
325 uint8 b1 = GetByte();
326 uint8 b2 = GetByte();
327 uint8 b3 = GetByte();
328 uint8 b4 = GetByte();
329 return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
330}
331
332int BmpDecoderHelper::GetShort() {
333 uint8 b1 = GetByte();
334 uint8 b2 = GetByte();
335 return b1 | (b2 << 8);
336}
337
338uint8 BmpDecoderHelper::GetByte() {
339 CHECK(pos_ >= 0 && pos_ <= len_);
340 // We deliberately allow this off-by-one access to cater for BMPs with their
341 // last byte missing.
342 if (pos_ == len_) {
343 return 0;
344 }
345 return data_[pos_++];
346}
347
348int BmpDecoderHelper::CalcShiftRight(uint32 mask) {
349 int ret = 0;
350 while (mask != 0 && !(mask & 1)) {
351 mask >>= 1;
352 ret++;
353 }
354 return ret;
355}
356
357int BmpDecoderHelper::CalcShiftLeft(uint32 mask) {
358 int ret = 0;
359 while (mask != 0 && !(mask & 1)) {
360 mask >>= 1;
361 }
362 while (mask != 0 && !(mask & 0x80)) {
363 mask <<= 1;
364 ret++;
365 }
366 return ret;
367}
368
369} // namespace image_codec