OLD | NEW |
(Empty) | |
| 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 |
| 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 (nullptr == 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 332 int BmpDecoderHelper::GetShort() { |
| 333 uint8 b1 = GetByte(); |
| 334 uint8 b2 = GetByte(); |
| 335 return b1 | (b2 << 8); |
| 336 } |
| 337 |
| 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 |
| 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 |
| 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 |
OLD | NEW |