diff options
Diffstat (limited to 'src/images/bmpdecoderhelper.cpp')
-rw-r--r-- | src/images/bmpdecoderhelper.cpp | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp new file mode 100644 index 0000000..acabf44 --- /dev/null +++ b/src/images/bmpdecoderhelper.cpp @@ -0,0 +1,376 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Author: cevans@google.com (Chris Evans) + +#include "bmpdecoderhelper.h" + +namespace image_codec { + +static const int kBmpHeaderSize = 14; +static const int kBmpInfoSize = 40; +static const int kBmpOS2InfoSize = 12; +static const int kMaxDim = SHRT_MAX / 2; + +bool BmpDecoderHelper::DecodeImage(const char* p, + int len, + int max_pixels, + BmpDecoderCallback* callback) { + data_ = reinterpret_cast<const uint8*>(p); + pos_ = 0; + len_ = len; + inverted_ = true; + // Parse the header structure. + if (len < kBmpHeaderSize + 4) { + return false; + } + GetShort(); // Signature. + GetInt(); // Size. + GetInt(); // Reserved. + int offset = GetInt(); + // Parse the info structure. + int infoSize = GetInt(); + if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) { + return false; + } + int cols = 0; + int comp = 0; + int colLen = 4; + if (infoSize >= kBmpInfoSize) { + if (len < kBmpHeaderSize + kBmpInfoSize) { + return false; + } + width_ = GetInt(); + height_ = GetInt(); + GetShort(); // Planes. + bpp_ = GetShort(); + comp = GetInt(); + GetInt(); // Size. + GetInt(); // XPPM. + GetInt(); // YPPM. + cols = GetInt(); + GetInt(); // Important colours. + } else { + if (len < kBmpHeaderSize + kBmpOS2InfoSize) { + return false; + } + colLen = 3; + width_ = GetShort(); + height_ = GetShort(); + GetShort(); // Planes. + bpp_ = GetShort(); + } + if (height_ < 0) { + height_ = -height_; + inverted_ = false; + } + if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) { + return false; + } + if (width_ * height_ > max_pixels) { + return false; + } + if (cols < 0 || cols > 256) { + return false; + } + // Allocate then read in the colour map. + if (cols == 0 && bpp_ <= 8) { + cols = 1 << bpp_; + } + if (bpp_ <= 8 || cols > 0) { + uint8* colBuf = new uint8[256 * 3]; + memset(colBuf, '\0', 256 * 3); + colTab_.reset(colBuf); + } + if (cols > 0) { + if (pos_ + (cols * colLen) > len_) { + return false; + } + for (int i = 0; i < cols; ++i) { + int base = i * 3; + colTab_[base + 2] = GetByte(); + colTab_[base + 1] = GetByte(); + colTab_[base] = GetByte(); + if (colLen == 4) { + GetByte(); + } + } + } + // Read in the compression data if necessary. + redBits_ = 0x7c00; + greenBits_ = 0x03e0; + blueBits_ = 0x001f; + bool rle = false; + if (comp == 1 || comp == 2) { + rle = true; + } else if (comp == 3) { + if (pos_ + 12 > len_) { + return false; + } + redBits_ = GetInt() & 0xffff; + greenBits_ = GetInt() & 0xffff; + blueBits_ = GetInt() & 0xffff; + } + redShiftRight_ = CalcShiftRight(redBits_); + greenShiftRight_ = CalcShiftRight(greenBits_); + blueShiftRight_ = CalcShiftRight(blueBits_); + redShiftLeft_ = CalcShiftLeft(redBits_); + greenShiftLeft_ = CalcShiftLeft(greenBits_); + blueShiftLeft_ = CalcShiftLeft(blueBits_); + rowPad_ = 0; + pixelPad_ = 0; + int rowLen; + if (bpp_ == 32) { + rowLen = width_ * 4; + pixelPad_ = 1; + } else if (bpp_ == 24) { + rowLen = width_ * 3; + } else if (bpp_ == 16) { + rowLen = width_ * 2; + } else if (bpp_ == 8) { + rowLen = width_; + } else if (bpp_ == 4) { + rowLen = width_ / 2; + if (width_ & 1) { + rowLen++; + } + } else if (bpp_ == 1) { + rowLen = width_ / 8; + if (width_ & 7) { + rowLen++; + } + } else { + return false; + } + // Round the rowLen up to a multiple of 4. + if (rowLen % 4 != 0) { + rowPad_ = 4 - (rowLen % 4); + rowLen += rowPad_; + } + + if (offset > 0 && offset > pos_ && offset < len_) { + pos_ = offset; + } + // Deliberately off-by-one; a load of BMPs seem to have their last byte + // missing. + if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) { + return false; + } + + output_ = callback->SetSize(width_, height_); + if (NULL == output_) { + return true; // meaning we succeeded, but they want us to stop now + } + + if (rle && (bpp_ == 4 || bpp_ == 8)) { + DoRLEDecode(); + } else { + DoStandardDecode(); + } + return true; +} + +void BmpDecoderHelper::DoRLEDecode() { + static const uint8 RLE_ESCAPE = 0; + static const uint8 RLE_EOL = 0; + static const uint8 RLE_EOF = 1; + static const uint8 RLE_DELTA = 2; + int x = 0; + int y = height_ - 1; + while (pos_ < len_ - 1) { + uint8 cmd = GetByte(); + if (cmd != RLE_ESCAPE) { + uint8 pixels = GetByte(); + int num = 0; + uint8 col = pixels; + while (cmd-- && x < width_) { + if (bpp_ == 4) { + if (num & 1) { + col = pixels & 0xf; + } else { + col = pixels >> 4; + } + } + PutPixel(x++, y, col); + num++; + } + } else { + cmd = GetByte(); + if (cmd == RLE_EOF) { + return; + } else if (cmd == RLE_EOL) { + x = 0; + y--; + if (y < 0) { + return; + } + } else if (cmd == RLE_DELTA) { + if (pos_ < len_ - 1) { + uint8 dx = GetByte(); + uint8 dy = GetByte(); + x += dx; + if (x > width_) { + x = width_; + } + y -= dy; + if (y < 0) { + return; + } + } + } else { + int num = 0; + int bytesRead = 0; + uint8 val = 0; + while (cmd-- && pos_ < len_) { + if (bpp_ == 8 || !(num & 1)) { + val = GetByte(); + bytesRead++; + } + uint8 col = val; + if (bpp_ == 4) { + if (num & 1) { + col = col & 0xf; + } else { + col >>= 4; + } + } + if (x < width_) { + PutPixel(x++, y, col); + } + num++; + } + // All pixel runs must be an even number of bytes - skip a byte if we + // read an odd number. + if ((bytesRead & 1) && pos_ < len_) { + GetByte(); + } + } + } + } +} + +void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) { + CHECK(x >= 0 && x < width_); + CHECK(y >= 0 && y < height_); + if (!inverted_) { + y = height_ - (y + 1); + } + + int base = ((y * width_) + x) * 3; + int colBase = col * 3; + output_[base] = colTab_[colBase]; + output_[base + 1] = colTab_[colBase + 1]; + output_[base + 2] = colTab_[colBase + 2]; +} + +void BmpDecoderHelper::DoStandardDecode() { + int row = 0; + uint8 currVal = 0; + for (int h = height_ - 1; h >= 0; h--, row++) { + int realH = h; + if (!inverted_) { + realH = height_ - (h + 1); + } + uint8* line = output_ + (3 * width_ * realH); + for (int w = 0; w < width_; w++) { + if (bpp_ >= 24) { + line[2] = GetByte(); + line[1] = GetByte(); + line[0] = GetByte(); + } else if (bpp_ == 16) { + uint32 val = GetShort(); + line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_; + line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_; + line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_; + } else if (bpp_ <= 8) { + uint8 col; + if (bpp_ == 8) { + col = GetByte(); + } else if (bpp_ == 4) { + if ((w % 2) == 0) { + currVal = GetByte(); + col = currVal >> 4; + } else { + col = currVal & 0xf; + } + } else { + if ((w % 8) == 0) { + currVal = GetByte(); + } + int bit = w & 7; + col = ((currVal >> (7 - bit)) & 1); + } + int base = col * 3; + line[0] = colTab_[base]; + line[1] = colTab_[base + 1]; + line[2] = colTab_[base + 2]; + } + line += 3; + for (int i = 0; i < pixelPad_; ++i) { + GetByte(); + } + } + for (int i = 0; i < rowPad_; ++i) { + GetByte(); + } + } +} + +int BmpDecoderHelper::GetInt() { + uint8 b1 = GetByte(); + uint8 b2 = GetByte(); + uint8 b3 = GetByte(); + uint8 b4 = GetByte(); + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +int BmpDecoderHelper::GetShort() { + uint8 b1 = GetByte(); + uint8 b2 = GetByte(); + return b1 | (b2 << 8); +} + +uint8 BmpDecoderHelper::GetByte() { + CHECK(pos_ >= 0 && pos_ <= len_); + // We deliberately allow this off-by-one access to cater for BMPs with their + // last byte missing. + if (pos_ == len_) { + return 0; + } + return data_[pos_++]; +} + +int BmpDecoderHelper::CalcShiftRight(uint32 mask) { + int ret = 0; + while (mask != 0 && !(mask & 1)) { + mask >>= 1; + ret++; + } + return ret; +} + +int BmpDecoderHelper::CalcShiftLeft(uint32 mask) { + int ret = 0; + while (mask != 0 && !(mask & 1)) { + mask >>= 1; + } + while (mask != 0 && !(mask & 0x80)) { + mask <<= 1; + ret++; + } + return ret; +} + +} // namespace image_codec |