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