| OLD | NEW |
| (Empty) |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 339 int BmpDecoderHelper::GetShort() { | |
| 340 uint8 b1 = GetByte(); | |
| 341 uint8 b2 = GetByte(); | |
| 342 return b1 | (b2 << 8); | |
| 343 } | |
| 344 | |
| 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 | |
| 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 | |
| 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 | |
| OLD | NEW |