|
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 // Author: cevans@google.com (Chris Evans) |
|
10 |
|
11 #include "bmpdecoderhelper.h" |
|
12 |
|
13 namespace image_codec { |
|
14 |
|
15 static const int kBmpHeaderSize = 14; |
|
16 static const int kBmpInfoSize = 40; |
|
17 static const int kBmpOS2InfoSize = 12; |
|
18 static const int kMaxDim = SHRT_MAX / 2; |
|
19 |
|
20 bool BmpDecoderHelper::DecodeImage(const char* p, |
|
21 size_t 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 && (size_t)offset > pos_ && (size_t)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 |
|
178 void 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_ + 1 < len_) { |
|
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_ + 1 < len_) { |
|
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 |
|
257 void 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 |
|
271 void 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 |
|
324 int 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 |
|
332 int BmpDecoderHelper::GetShort() { |
|
333 uint8 b1 = GetByte(); |
|
334 uint8 b2 = GetByte(); |
|
335 return b1 | (b2 << 8); |
|
336 } |
|
337 |
|
338 uint8 BmpDecoderHelper::GetByte() { |
|
339 CHECK(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 |
|
348 int 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 |
|
357 int 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 |