OLD | NEW |
| (Empty) |
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 (nullptr == 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 | |
OLD | NEW |