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 int 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 && offset > pos_ && 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_ < len_ - 1) { 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_ < len_ - 1) { 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_ >= 0 && 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