• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DecodeImage(const char * p,int len,int max_pixels,BmpDecoderCallback * callback)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 
DoRLEDecode()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 
PutPixel(int x,int y,uint8 col)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 
DoStandardDecode()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 
GetInt()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 
GetShort()339 int BmpDecoderHelper::GetShort() {
340   uint8 b1 = GetByte();
341   uint8 b2 = GetByte();
342   return b1 | (b2 << 8);
343 }
344 
GetByte()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 
CalcShiftRight(uint32 mask)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 
CalcShiftLeft(uint32 mask)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
377