| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- /*
- * 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 <memory.h>
- #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
|