1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/images/bmpdecoderhelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,369 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2007 The Android Open Source Project 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 +// Author: cevans@google.com (Chris Evans) 1.13 + 1.14 +#include "bmpdecoderhelper.h" 1.15 + 1.16 +namespace image_codec { 1.17 + 1.18 +static const int kBmpHeaderSize = 14; 1.19 +static const int kBmpInfoSize = 40; 1.20 +static const int kBmpOS2InfoSize = 12; 1.21 +static const int kMaxDim = SHRT_MAX / 2; 1.22 + 1.23 +bool BmpDecoderHelper::DecodeImage(const char* p, 1.24 + size_t len, 1.25 + int max_pixels, 1.26 + BmpDecoderCallback* callback) { 1.27 + data_ = reinterpret_cast<const uint8*>(p); 1.28 + pos_ = 0; 1.29 + len_ = len; 1.30 + inverted_ = true; 1.31 + // Parse the header structure. 1.32 + if (len < kBmpHeaderSize + 4) { 1.33 + return false; 1.34 + } 1.35 + GetShort(); // Signature. 1.36 + GetInt(); // Size. 1.37 + GetInt(); // Reserved. 1.38 + int offset = GetInt(); 1.39 + // Parse the info structure. 1.40 + int infoSize = GetInt(); 1.41 + if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) { 1.42 + return false; 1.43 + } 1.44 + int cols = 0; 1.45 + int comp = 0; 1.46 + int colLen = 4; 1.47 + if (infoSize >= kBmpInfoSize) { 1.48 + if (len < kBmpHeaderSize + kBmpInfoSize) { 1.49 + return false; 1.50 + } 1.51 + width_ = GetInt(); 1.52 + height_ = GetInt(); 1.53 + GetShort(); // Planes. 1.54 + bpp_ = GetShort(); 1.55 + comp = GetInt(); 1.56 + GetInt(); // Size. 1.57 + GetInt(); // XPPM. 1.58 + GetInt(); // YPPM. 1.59 + cols = GetInt(); 1.60 + GetInt(); // Important colours. 1.61 + } else { 1.62 + if (len < kBmpHeaderSize + kBmpOS2InfoSize) { 1.63 + return false; 1.64 + } 1.65 + colLen = 3; 1.66 + width_ = GetShort(); 1.67 + height_ = GetShort(); 1.68 + GetShort(); // Planes. 1.69 + bpp_ = GetShort(); 1.70 + } 1.71 + if (height_ < 0) { 1.72 + height_ = -height_; 1.73 + inverted_ = false; 1.74 + } 1.75 + if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) { 1.76 + return false; 1.77 + } 1.78 + if (width_ * height_ > max_pixels) { 1.79 + return false; 1.80 + } 1.81 + if (cols < 0 || cols > 256) { 1.82 + return false; 1.83 + } 1.84 + // Allocate then read in the colour map. 1.85 + if (cols == 0 && bpp_ <= 8) { 1.86 + cols = 1 << bpp_; 1.87 + } 1.88 + if (bpp_ <= 8 || cols > 0) { 1.89 + uint8* colBuf = new uint8[256 * 3]; 1.90 + memset(colBuf, '\0', 256 * 3); 1.91 + colTab_.reset(colBuf); 1.92 + } 1.93 + if (cols > 0) { 1.94 + if (pos_ + (cols * colLen) > len_) { 1.95 + return false; 1.96 + } 1.97 + for (int i = 0; i < cols; ++i) { 1.98 + int base = i * 3; 1.99 + colTab_[base + 2] = GetByte(); 1.100 + colTab_[base + 1] = GetByte(); 1.101 + colTab_[base] = GetByte(); 1.102 + if (colLen == 4) { 1.103 + GetByte(); 1.104 + } 1.105 + } 1.106 + } 1.107 + // Read in the compression data if necessary. 1.108 + redBits_ = 0x7c00; 1.109 + greenBits_ = 0x03e0; 1.110 + blueBits_ = 0x001f; 1.111 + bool rle = false; 1.112 + if (comp == 1 || comp == 2) { 1.113 + rle = true; 1.114 + } else if (comp == 3) { 1.115 + if (pos_ + 12 > len_) { 1.116 + return false; 1.117 + } 1.118 + redBits_ = GetInt() & 0xffff; 1.119 + greenBits_ = GetInt() & 0xffff; 1.120 + blueBits_ = GetInt() & 0xffff; 1.121 + } 1.122 + redShiftRight_ = CalcShiftRight(redBits_); 1.123 + greenShiftRight_ = CalcShiftRight(greenBits_); 1.124 + blueShiftRight_ = CalcShiftRight(blueBits_); 1.125 + redShiftLeft_ = CalcShiftLeft(redBits_); 1.126 + greenShiftLeft_ = CalcShiftLeft(greenBits_); 1.127 + blueShiftLeft_ = CalcShiftLeft(blueBits_); 1.128 + rowPad_ = 0; 1.129 + pixelPad_ = 0; 1.130 + int rowLen; 1.131 + if (bpp_ == 32) { 1.132 + rowLen = width_ * 4; 1.133 + pixelPad_ = 1; 1.134 + } else if (bpp_ == 24) { 1.135 + rowLen = width_ * 3; 1.136 + } else if (bpp_ == 16) { 1.137 + rowLen = width_ * 2; 1.138 + } else if (bpp_ == 8) { 1.139 + rowLen = width_; 1.140 + } else if (bpp_ == 4) { 1.141 + rowLen = width_ / 2; 1.142 + if (width_ & 1) { 1.143 + rowLen++; 1.144 + } 1.145 + } else if (bpp_ == 1) { 1.146 + rowLen = width_ / 8; 1.147 + if (width_ & 7) { 1.148 + rowLen++; 1.149 + } 1.150 + } else { 1.151 + return false; 1.152 + } 1.153 + // Round the rowLen up to a multiple of 4. 1.154 + if (rowLen % 4 != 0) { 1.155 + rowPad_ = 4 - (rowLen % 4); 1.156 + rowLen += rowPad_; 1.157 + } 1.158 + 1.159 + if (offset > 0 && (size_t)offset > pos_ && (size_t)offset < len_) { 1.160 + pos_ = offset; 1.161 + } 1.162 + // Deliberately off-by-one; a load of BMPs seem to have their last byte 1.163 + // missing. 1.164 + if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) { 1.165 + return false; 1.166 + } 1.167 + 1.168 + output_ = callback->SetSize(width_, height_); 1.169 + if (NULL == output_) { 1.170 + return true; // meaning we succeeded, but they want us to stop now 1.171 + } 1.172 + 1.173 + if (rle && (bpp_ == 4 || bpp_ == 8)) { 1.174 + DoRLEDecode(); 1.175 + } else { 1.176 + DoStandardDecode(); 1.177 + } 1.178 + return true; 1.179 +} 1.180 + 1.181 +void BmpDecoderHelper::DoRLEDecode() { 1.182 + static const uint8 RLE_ESCAPE = 0; 1.183 + static const uint8 RLE_EOL = 0; 1.184 + static const uint8 RLE_EOF = 1; 1.185 + static const uint8 RLE_DELTA = 2; 1.186 + int x = 0; 1.187 + int y = height_ - 1; 1.188 + while (pos_ + 1 < len_) { 1.189 + uint8 cmd = GetByte(); 1.190 + if (cmd != RLE_ESCAPE) { 1.191 + uint8 pixels = GetByte(); 1.192 + int num = 0; 1.193 + uint8 col = pixels; 1.194 + while (cmd-- && x < width_) { 1.195 + if (bpp_ == 4) { 1.196 + if (num & 1) { 1.197 + col = pixels & 0xf; 1.198 + } else { 1.199 + col = pixels >> 4; 1.200 + } 1.201 + } 1.202 + PutPixel(x++, y, col); 1.203 + num++; 1.204 + } 1.205 + } else { 1.206 + cmd = GetByte(); 1.207 + if (cmd == RLE_EOF) { 1.208 + return; 1.209 + } else if (cmd == RLE_EOL) { 1.210 + x = 0; 1.211 + y--; 1.212 + if (y < 0) { 1.213 + return; 1.214 + } 1.215 + } else if (cmd == RLE_DELTA) { 1.216 + if (pos_ + 1 < len_) { 1.217 + uint8 dx = GetByte(); 1.218 + uint8 dy = GetByte(); 1.219 + x += dx; 1.220 + if (x > width_) { 1.221 + x = width_; 1.222 + } 1.223 + y -= dy; 1.224 + if (y < 0) { 1.225 + return; 1.226 + } 1.227 + } 1.228 + } else { 1.229 + int num = 0; 1.230 + int bytesRead = 0; 1.231 + uint8 val = 0; 1.232 + while (cmd-- && pos_ < len_) { 1.233 + if (bpp_ == 8 || !(num & 1)) { 1.234 + val = GetByte(); 1.235 + bytesRead++; 1.236 + } 1.237 + uint8 col = val; 1.238 + if (bpp_ == 4) { 1.239 + if (num & 1) { 1.240 + col = col & 0xf; 1.241 + } else { 1.242 + col >>= 4; 1.243 + } 1.244 + } 1.245 + if (x < width_) { 1.246 + PutPixel(x++, y, col); 1.247 + } 1.248 + num++; 1.249 + } 1.250 + // All pixel runs must be an even number of bytes - skip a byte if we 1.251 + // read an odd number. 1.252 + if ((bytesRead & 1) && pos_ < len_) { 1.253 + GetByte(); 1.254 + } 1.255 + } 1.256 + } 1.257 + } 1.258 +} 1.259 + 1.260 +void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) { 1.261 + CHECK(x >= 0 && x < width_); 1.262 + CHECK(y >= 0 && y < height_); 1.263 + if (!inverted_) { 1.264 + y = height_ - (y + 1); 1.265 + } 1.266 + 1.267 + int base = ((y * width_) + x) * 3; 1.268 + int colBase = col * 3; 1.269 + output_[base] = colTab_[colBase]; 1.270 + output_[base + 1] = colTab_[colBase + 1]; 1.271 + output_[base + 2] = colTab_[colBase + 2]; 1.272 +} 1.273 + 1.274 +void BmpDecoderHelper::DoStandardDecode() { 1.275 + int row = 0; 1.276 + uint8 currVal = 0; 1.277 + for (int h = height_ - 1; h >= 0; h--, row++) { 1.278 + int realH = h; 1.279 + if (!inverted_) { 1.280 + realH = height_ - (h + 1); 1.281 + } 1.282 + uint8* line = output_ + (3 * width_ * realH); 1.283 + for (int w = 0; w < width_; w++) { 1.284 + if (bpp_ >= 24) { 1.285 + line[2] = GetByte(); 1.286 + line[1] = GetByte(); 1.287 + line[0] = GetByte(); 1.288 + } else if (bpp_ == 16) { 1.289 + uint32 val = GetShort(); 1.290 + line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_; 1.291 + line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_; 1.292 + line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_; 1.293 + } else if (bpp_ <= 8) { 1.294 + uint8 col; 1.295 + if (bpp_ == 8) { 1.296 + col = GetByte(); 1.297 + } else if (bpp_ == 4) { 1.298 + if ((w % 2) == 0) { 1.299 + currVal = GetByte(); 1.300 + col = currVal >> 4; 1.301 + } else { 1.302 + col = currVal & 0xf; 1.303 + } 1.304 + } else { 1.305 + if ((w % 8) == 0) { 1.306 + currVal = GetByte(); 1.307 + } 1.308 + int bit = w & 7; 1.309 + col = ((currVal >> (7 - bit)) & 1); 1.310 + } 1.311 + int base = col * 3; 1.312 + line[0] = colTab_[base]; 1.313 + line[1] = colTab_[base + 1]; 1.314 + line[2] = colTab_[base + 2]; 1.315 + } 1.316 + line += 3; 1.317 + for (int i = 0; i < pixelPad_; ++i) { 1.318 + GetByte(); 1.319 + } 1.320 + } 1.321 + for (int i = 0; i < rowPad_; ++i) { 1.322 + GetByte(); 1.323 + } 1.324 + } 1.325 +} 1.326 + 1.327 +int BmpDecoderHelper::GetInt() { 1.328 + uint8 b1 = GetByte(); 1.329 + uint8 b2 = GetByte(); 1.330 + uint8 b3 = GetByte(); 1.331 + uint8 b4 = GetByte(); 1.332 + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); 1.333 +} 1.334 + 1.335 +int BmpDecoderHelper::GetShort() { 1.336 + uint8 b1 = GetByte(); 1.337 + uint8 b2 = GetByte(); 1.338 + return b1 | (b2 << 8); 1.339 +} 1.340 + 1.341 +uint8 BmpDecoderHelper::GetByte() { 1.342 + CHECK(pos_ <= len_); 1.343 + // We deliberately allow this off-by-one access to cater for BMPs with their 1.344 + // last byte missing. 1.345 + if (pos_ == len_) { 1.346 + return 0; 1.347 + } 1.348 + return data_[pos_++]; 1.349 +} 1.350 + 1.351 +int BmpDecoderHelper::CalcShiftRight(uint32 mask) { 1.352 + int ret = 0; 1.353 + while (mask != 0 && !(mask & 1)) { 1.354 + mask >>= 1; 1.355 + ret++; 1.356 + } 1.357 + return ret; 1.358 +} 1.359 + 1.360 +int BmpDecoderHelper::CalcShiftLeft(uint32 mask) { 1.361 + int ret = 0; 1.362 + while (mask != 0 && !(mask & 1)) { 1.363 + mask >>= 1; 1.364 + } 1.365 + while (mask != 0 && !(mask & 0x80)) { 1.366 + mask <<= 1; 1.367 + ret++; 1.368 + } 1.369 + return ret; 1.370 +} 1.371 + 1.372 +} // namespace image_codec