• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DecodeImage(const char * p,size_t len,int max_pixels,BmpDecoderCallback * callback)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 
DoRLEDecode()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 
PutPixel(int x,int y,uint8 col)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 
DoStandardDecode()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 
GetInt()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 
GetShort()332 int BmpDecoderHelper::GetShort() {
333   uint8 b1 = GetByte();
334   uint8 b2 = GetByte();
335   return b1 | (b2 << 8);
336 }
337 
GetByte()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 
CalcShiftRight(uint32 mask)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 
CalcShiftLeft(uint32 mask)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
370