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
13namespace image_codec {
14
15static const int kBmpHeaderSize = 14;
16static const int kBmpInfoSize = 40;
17static const int kBmpOS2InfoSize = 12;
18static const int kMaxDim = SHRT_MAX / 2;
19
20bool 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
178void 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
257void 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
271void 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
324int 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
332int BmpDecoderHelper::GetShort() {
333  uint8 b1 = GetByte();
334  uint8 b2 = GetByte();
335  return b1 | (b2 << 8);
336}
337
338uint8 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
348int 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
357int 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