1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/decoders/nsBMPDecoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,743 @@ 1.4 +/* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +/* I got the format description from http://www.daubnet.com/formats/BMP.html */ 1.9 + 1.10 +/* This is a Cross-Platform BMP Decoder, which should work everywhere, including 1.11 + * Big-Endian machines like the PowerPC. */ 1.12 + 1.13 +#include <stdlib.h> 1.14 + 1.15 +#include "ImageLogging.h" 1.16 +#include "mozilla/Endian.h" 1.17 +#include "nsBMPDecoder.h" 1.18 + 1.19 +#include "nsIInputStream.h" 1.20 +#include "RasterImage.h" 1.21 +#include <algorithm> 1.22 + 1.23 +namespace mozilla { 1.24 +namespace image { 1.25 + 1.26 +#ifdef PR_LOGGING 1.27 +static PRLogModuleInfo * 1.28 +GetBMPLog() 1.29 +{ 1.30 + static PRLogModuleInfo *sBMPLog; 1.31 + if (!sBMPLog) 1.32 + sBMPLog = PR_NewLogModule("BMPDecoder"); 1.33 + return sBMPLog; 1.34 +} 1.35 +#endif 1.36 + 1.37 +// Convert from row (1..height) to absolute line (0..height-1) 1.38 +#define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1)) 1.39 +#define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col) 1.40 + 1.41 +nsBMPDecoder::nsBMPDecoder(RasterImage &aImage) 1.42 + : Decoder(aImage) 1.43 +{ 1.44 + mColors = nullptr; 1.45 + mRow = nullptr; 1.46 + mCurPos = mPos = mNumColors = mRowBytes = 0; 1.47 + mOldLine = mCurLine = 1; // Otherwise decoder will never start 1.48 + mState = eRLEStateInitial; 1.49 + mStateData = 0; 1.50 + mLOH = WIN_V3_HEADER_LENGTH; 1.51 + mUseAlphaData = mHaveAlphaData = false; 1.52 +} 1.53 + 1.54 +nsBMPDecoder::~nsBMPDecoder() 1.55 +{ 1.56 + delete[] mColors; 1.57 + if (mRow) { 1.58 + moz_free(mRow); 1.59 + } 1.60 +} 1.61 + 1.62 +// Sets whether or not the BMP will use alpha data 1.63 +void 1.64 +nsBMPDecoder::SetUseAlphaData(bool useAlphaData) 1.65 +{ 1.66 + mUseAlphaData = useAlphaData; 1.67 +} 1.68 + 1.69 +// Obtains the bits per pixel from the internal BIH header 1.70 +int32_t 1.71 +nsBMPDecoder::GetBitsPerPixel() const 1.72 +{ 1.73 + return mBIH.bpp; 1.74 +} 1.75 + 1.76 +// Obtains the width from the internal BIH header 1.77 +int32_t 1.78 +nsBMPDecoder::GetWidth() const 1.79 +{ 1.80 + return mBIH.width; 1.81 +} 1.82 + 1.83 +// Obtains the abs-value of the height from the internal BIH header 1.84 +int32_t 1.85 +nsBMPDecoder::GetHeight() const 1.86 +{ 1.87 + return abs(mBIH.height); 1.88 +} 1.89 + 1.90 +// Obtains the internal output image buffer 1.91 +uint32_t* 1.92 +nsBMPDecoder::GetImageData() 1.93 +{ 1.94 + return reinterpret_cast<uint32_t*>(mImageData); 1.95 +} 1.96 + 1.97 +// Obtains the size of the compressed image resource 1.98 +int32_t 1.99 +nsBMPDecoder::GetCompressedImageSize() const 1.100 +{ 1.101 + // For everything except BI_RGB the header field must be defined 1.102 + if (mBIH.compression != BI_RGB) { 1.103 + return mBIH.image_size; 1.104 + } 1.105 + 1.106 + // mBIH.image_size isn't always filled for BI_RGB so calculate it manually 1.107 + // The pixel array size is calculated based on extra 4 byte boundary padding 1.108 + uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up 1.109 + // Pad to DWORD Boundary 1.110 + if (rowSize % 4) { 1.111 + rowSize += (4 - (rowSize % 4)); 1.112 + } 1.113 + 1.114 + // The height should be the absolute value of what the height is in the BIH. 1.115 + // If positive the bitmap is stored bottom to top, otherwise top to bottom 1.116 + int32_t pixelArraySize = rowSize * GetHeight(); 1.117 + return pixelArraySize; 1.118 +} 1.119 + 1.120 +// Obtains whether or not a BMP file had alpha data in its 4th byte 1.121 +// for 32BPP bitmaps. Only use after the bitmap has been processed. 1.122 +bool 1.123 +nsBMPDecoder::HasAlphaData() const 1.124 +{ 1.125 + return mHaveAlphaData; 1.126 +} 1.127 + 1.128 + 1.129 +void 1.130 +nsBMPDecoder::FinishInternal() 1.131 +{ 1.132 + // We shouldn't be called in error cases 1.133 + NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!"); 1.134 + 1.135 + // We should never make multiple frames 1.136 + NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?"); 1.137 + 1.138 + // Send notifications if appropriate 1.139 + if (!IsSizeDecode() && HasSize()) { 1.140 + 1.141 + // Invalidate 1.142 + nsIntRect r(0, 0, mBIH.width, GetHeight()); 1.143 + PostInvalidation(r); 1.144 + 1.145 + if (mUseAlphaData) { 1.146 + PostFrameStop(FrameBlender::kFrameHasAlpha); 1.147 + } else { 1.148 + PostFrameStop(FrameBlender::kFrameOpaque); 1.149 + } 1.150 + PostDecodeDone(); 1.151 + } 1.152 +} 1.153 + 1.154 +// ---------------------------------------- 1.155 +// Actual Data Processing 1.156 +// ---------------------------------------- 1.157 + 1.158 +static void calcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength) 1.159 +{ 1.160 + // find the rightmost 1 1.161 + uint8_t pos; 1.162 + bool started = false; 1.163 + aBegin = aLength = 0; 1.164 + for (pos = 0; pos <= 31; pos++) { 1.165 + if (!started && (aMask & (1 << pos))) { 1.166 + aBegin = pos; 1.167 + started = true; 1.168 + } 1.169 + else if (started && !(aMask & (1 << pos))) { 1.170 + aLength = pos - aBegin; 1.171 + break; 1.172 + } 1.173 + } 1.174 +} 1.175 + 1.176 +NS_METHOD nsBMPDecoder::CalcBitShift() 1.177 +{ 1.178 + uint8_t begin, length; 1.179 + // red 1.180 + calcBitmask(mBitFields.red, begin, length); 1.181 + mBitFields.redRightShift = begin; 1.182 + mBitFields.redLeftShift = 8 - length; 1.183 + // green 1.184 + calcBitmask(mBitFields.green, begin, length); 1.185 + mBitFields.greenRightShift = begin; 1.186 + mBitFields.greenLeftShift = 8 - length; 1.187 + // blue 1.188 + calcBitmask(mBitFields.blue, begin, length); 1.189 + mBitFields.blueRightShift = begin; 1.190 + mBitFields.blueLeftShift = 8 - length; 1.191 + return NS_OK; 1.192 +} 1.193 + 1.194 +void 1.195 +nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy) 1.196 +{ 1.197 + NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); 1.198 + 1.199 + // aCount=0 means EOF, mCurLine=0 means we're past end of image 1.200 + if (!aCount || !mCurLine) 1.201 + return; 1.202 + 1.203 + if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */ 1.204 + uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos; 1.205 + if (toCopy > aCount) 1.206 + toCopy = aCount; 1.207 + memcpy(mRawBuf + mPos, aBuffer, toCopy); 1.208 + mPos += toCopy; 1.209 + aCount -= toCopy; 1.210 + aBuffer += toCopy; 1.211 + } 1.212 + if (mPos == BFH_INTERNAL_LENGTH) { 1.213 + ProcessFileHeader(); 1.214 + if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') { 1.215 + PostDataError(); 1.216 + return; 1.217 + } 1.218 + if (mBFH.bihsize == OS2_BIH_LENGTH) 1.219 + mLOH = OS2_HEADER_LENGTH; 1.220 + } 1.221 + if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */ 1.222 + uint32_t toCopy = mLOH - mPos; 1.223 + if (toCopy > aCount) 1.224 + toCopy = aCount; 1.225 + memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy); 1.226 + mPos += toCopy; 1.227 + aCount -= toCopy; 1.228 + aBuffer += toCopy; 1.229 + } 1.230 + 1.231 + // HasSize is called to ensure that if at this point mPos == mLOH but 1.232 + // we have no data left to process, the next time WriteInternal is called 1.233 + // we won't enter this condition again. 1.234 + if (mPos == mLOH && !HasSize()) { 1.235 + ProcessInfoHeader(); 1.236 + PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n", 1.237 + mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression)); 1.238 + // Verify we support this bit depth 1.239 + if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 && 1.240 + mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) { 1.241 + PostDataError(); 1.242 + return; 1.243 + } 1.244 + 1.245 + // BMPs with negative width are invalid 1.246 + // Reject extremely wide images to keep the math sane 1.247 + const int32_t k64KWidth = 0x0000FFFF; 1.248 + if (mBIH.width < 0 || mBIH.width > k64KWidth) { 1.249 + PostDataError(); 1.250 + return; 1.251 + } 1.252 + 1.253 + if (mBIH.height == INT_MIN) { 1.254 + PostDataError(); 1.255 + return; 1.256 + } 1.257 + 1.258 + uint32_t real_height = GetHeight(); 1.259 + 1.260 + // Post our size to the superclass 1.261 + PostSize(mBIH.width, real_height); 1.262 + if (HasError()) { 1.263 + // Setting the size led to an error. 1.264 + return; 1.265 + } 1.266 + 1.267 + // We have the size. If we're doing a size decode, we got what 1.268 + // we came for. 1.269 + if (IsSizeDecode()) 1.270 + return; 1.271 + 1.272 + // We're doing a real decode. 1.273 + mOldLine = mCurLine = real_height; 1.274 + 1.275 + if (mBIH.bpp <= 8) { 1.276 + mNumColors = 1 << mBIH.bpp; 1.277 + if (mBIH.colors && mBIH.colors < mNumColors) 1.278 + mNumColors = mBIH.colors; 1.279 + 1.280 + // Always allocate 256 even though mNumColors might be smaller 1.281 + mColors = new colorTable[256]; 1.282 + memset(mColors, 0, 256 * sizeof(colorTable)); 1.283 + } 1.284 + else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) { 1.285 + // Use default 5-5-5 format 1.286 + mBitFields.red = 0x7C00; 1.287 + mBitFields.green = 0x03E0; 1.288 + mBitFields.blue = 0x001F; 1.289 + CalcBitShift(); 1.290 + } 1.291 + 1.292 + // Make sure we have a valid value for our supported compression modes 1.293 + // before adding the frame 1.294 + if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 && 1.295 + mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) { 1.296 + PostDataError(); 1.297 + return; 1.298 + } 1.299 + 1.300 + // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we 1.301 + // have valid BPP values before adding the frame 1.302 + if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) { 1.303 + PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 1.304 + ("BMP RLE8 compression only supports 8 bits per pixel\n")); 1.305 + PostDataError(); 1.306 + return; 1.307 + } 1.308 + if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4 && mBIH.bpp != 1) { 1.309 + PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 1.310 + ("BMP RLE4 compression only supports 4 bits per pixel\n")); 1.311 + PostDataError(); 1.312 + return; 1.313 + } 1.314 + if (mBIH.compression == BI_ALPHABITFIELDS && 1.315 + mBIH.bpp != 16 && mBIH.bpp != 32) { 1.316 + PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 1.317 + ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n")); 1.318 + PostDataError(); 1.319 + return; 1.320 + } 1.321 + 1.322 + if (mBIH.compression != BI_RLE8 && mBIH.compression != BI_RLE4 && 1.323 + mBIH.compression != BI_ALPHABITFIELDS) { 1.324 + // mRow is not used for RLE encoded images 1.325 + mRow = (uint8_t*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4); 1.326 + // + 4 because the line is padded to a 4 bit boundary, but I don't want 1.327 + // to make exact calculations here, that's unnecessary. 1.328 + // Also, it compensates rounding error. 1.329 + if (!mRow) { 1.330 + PostDataError(); 1.331 + return; 1.332 + } 1.333 + } 1.334 + if (!mImageData) { 1.335 + PostDecoderError(NS_ERROR_FAILURE); 1.336 + return; 1.337 + } 1.338 + 1.339 + // Prepare for transparency 1.340 + if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { 1.341 + // Clear the image, as the RLE may jump over areas 1.342 + memset(mImageData, 0, mImageDataLength); 1.343 + } 1.344 + } 1.345 + 1.346 + if (mColors && mPos >= mLOH) { 1.347 + // OS/2 Bitmaps have no padding byte 1.348 + uint8_t bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; 1.349 + if (mPos < (mLOH + mNumColors * bytesPerColor)) { 1.350 + // Number of bytes already received 1.351 + uint32_t colorBytes = mPos - mLOH; 1.352 + // Color which is currently received 1.353 + uint8_t colorNum = colorBytes / bytesPerColor; 1.354 + uint8_t at = colorBytes % bytesPerColor; 1.355 + while (aCount && (mPos < (mLOH + mNumColors * bytesPerColor))) { 1.356 + switch (at) { 1.357 + case 0: 1.358 + mColors[colorNum].blue = *aBuffer; 1.359 + break; 1.360 + case 1: 1.361 + mColors[colorNum].green = *aBuffer; 1.362 + break; 1.363 + case 2: 1.364 + mColors[colorNum].red = *aBuffer; 1.365 + // If there is no padding byte, increment the color index 1.366 + // since we're done with the current color. 1.367 + if (bytesPerColor == 3) 1.368 + colorNum++; 1.369 + break; 1.370 + case 3: 1.371 + // This is a padding byte only in Windows BMPs. Increment 1.372 + // the color index since we're done with the current color. 1.373 + colorNum++; 1.374 + break; 1.375 + } 1.376 + mPos++; aBuffer++; aCount--; 1.377 + at = (at + 1) % bytesPerColor; 1.378 + } 1.379 + } 1.380 + } 1.381 + else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH)) { 1.382 + // If compression is used, this is a windows bitmap, hence we can 1.383 + // use WIN_HEADER_LENGTH instead of mLOH 1.384 + uint32_t toCopy = (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH) - mPos; 1.385 + if (toCopy > aCount) 1.386 + toCopy = aCount; 1.387 + memcpy(mRawBuf + (mPos - WIN_V3_HEADER_LENGTH), aBuffer, toCopy); 1.388 + mPos += toCopy; 1.389 + aBuffer += toCopy; 1.390 + aCount -= toCopy; 1.391 + } 1.392 + if (mPos == WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH && 1.393 + mBIH.compression == BI_BITFIELDS) { 1.394 + mBitFields.red = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf)); 1.395 + mBitFields.green = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 4)); 1.396 + mBitFields.blue = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 8)); 1.397 + CalcBitShift(); 1.398 + } 1.399 + while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data 1.400 + mPos++; aBuffer++; aCount--; 1.401 + } 1.402 + if (aCount && ++mPos >= mBFH.dataoffset) { 1.403 + // Need to increment mPos, else we might get to mPos==mLOH again 1.404 + // From now on, mPos is irrelevant 1.405 + if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) { 1.406 + uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up 1.407 + if (rowSize % 4) { 1.408 + rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary 1.409 + } 1.410 + uint32_t toCopy; 1.411 + do { 1.412 + toCopy = rowSize - mRowBytes; 1.413 + if (toCopy) { 1.414 + if (toCopy > aCount) 1.415 + toCopy = aCount; 1.416 + memcpy(mRow + mRowBytes, aBuffer, toCopy); 1.417 + aCount -= toCopy; 1.418 + aBuffer += toCopy; 1.419 + mRowBytes += toCopy; 1.420 + } 1.421 + if (rowSize == mRowBytes) { 1.422 + // Collected a whole row into mRow, process it 1.423 + uint8_t* p = mRow; 1.424 + uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, 0); 1.425 + uint32_t lpos = mBIH.width; 1.426 + switch (mBIH.bpp) { 1.427 + case 1: 1.428 + while (lpos > 0) { 1.429 + int8_t bit; 1.430 + uint8_t idx; 1.431 + for (bit = 7; bit >= 0 && lpos > 0; bit--) { 1.432 + idx = (*p >> bit) & 1; 1.433 + SetPixel(d, idx, mColors); 1.434 + --lpos; 1.435 + } 1.436 + ++p; 1.437 + } 1.438 + break; 1.439 + case 4: 1.440 + while (lpos > 0) { 1.441 + Set4BitPixel(d, *p, lpos, mColors); 1.442 + ++p; 1.443 + } 1.444 + break; 1.445 + case 8: 1.446 + while (lpos > 0) { 1.447 + SetPixel(d, *p, mColors); 1.448 + --lpos; 1.449 + ++p; 1.450 + } 1.451 + break; 1.452 + case 16: 1.453 + while (lpos > 0) { 1.454 + uint16_t val = LittleEndian::readUint16(reinterpret_cast<uint16_t*>(p)); 1.455 + SetPixel(d, 1.456 + (val & mBitFields.red) >> mBitFields.redRightShift << mBitFields.redLeftShift, 1.457 + (val & mBitFields.green) >> mBitFields.greenRightShift << mBitFields.greenLeftShift, 1.458 + (val & mBitFields.blue) >> mBitFields.blueRightShift << mBitFields.blueLeftShift); 1.459 + --lpos; 1.460 + p+=2; 1.461 + } 1.462 + break; 1.463 + case 24: 1.464 + while (lpos > 0) { 1.465 + SetPixel(d, p[2], p[1], p[0]); 1.466 + p += 2; 1.467 + --lpos; 1.468 + ++p; 1.469 + } 1.470 + break; 1.471 + case 32: 1.472 + while (lpos > 0) { 1.473 + if (mUseAlphaData) { 1.474 + if (!mHaveAlphaData && p[3]) { 1.475 + // Non-zero alpha byte detected! Clear previous 1.476 + // pixels that we have already processed. 1.477 + // This works because we know that if we 1.478 + // are reaching here then the alpha data in byte 1.479 + // 4 has been right all along. And we know it 1.480 + // has been set to 0 the whole time, so that 1.481 + // means that everything is transparent so far. 1.482 + uint32_t* start = reinterpret_cast<uint32_t*>(mImageData) + GetWidth() * (mCurLine - 1); 1.483 + uint32_t heightDifference = GetHeight() - mCurLine + 1; 1.484 + uint32_t pixelCount = GetWidth() * heightDifference; 1.485 + 1.486 + memset(start, 0, pixelCount * sizeof(uint32_t)); 1.487 + 1.488 + mHaveAlphaData = true; 1.489 + } 1.490 + SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF); 1.491 + } else { 1.492 + SetPixel(d, p[2], p[1], p[0]); 1.493 + } 1.494 + p += 4; 1.495 + --lpos; 1.496 + } 1.497 + break; 1.498 + default: 1.499 + NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it"); 1.500 + } 1.501 + mCurLine --; 1.502 + if (mCurLine == 0) { // Finished last line 1.503 + break; 1.504 + } 1.505 + mRowBytes = 0; 1.506 + 1.507 + } 1.508 + } while (aCount > 0); 1.509 + } 1.510 + else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) { 1.511 + if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) || 1.512 + ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) { 1.513 + PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n")); 1.514 + PostDataError(); 1.515 + return; 1.516 + } 1.517 + 1.518 + while (aCount > 0) { 1.519 + uint8_t byte; 1.520 + 1.521 + switch(mState) { 1.522 + case eRLEStateInitial: 1.523 + mStateData = (uint8_t)*aBuffer++; 1.524 + aCount--; 1.525 + 1.526 + mState = eRLEStateNeedSecondEscapeByte; 1.527 + continue; 1.528 + 1.529 + case eRLEStateNeedSecondEscapeByte: 1.530 + byte = *aBuffer++; 1.531 + aCount--; 1.532 + if (mStateData != RLE_ESCAPE) { // encoded mode 1.533 + // Encoded mode consists of two bytes: 1.534 + // the first byte (mStateData) specifies the 1.535 + // number of consecutive pixels to be drawn 1.536 + // using the color index contained in 1.537 + // the second byte 1.538 + // Work around bitmaps that specify too many pixels 1.539 + mState = eRLEStateInitial; 1.540 + uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos, mStateData); 1.541 + if (pixelsNeeded) { 1.542 + uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos); 1.543 + mCurPos += pixelsNeeded; 1.544 + if (mBIH.compression == BI_RLE8) { 1.545 + do { 1.546 + SetPixel(d, byte, mColors); 1.547 + pixelsNeeded --; 1.548 + } while (pixelsNeeded); 1.549 + } else { 1.550 + do { 1.551 + Set4BitPixel(d, byte, pixelsNeeded, mColors); 1.552 + } while (pixelsNeeded); 1.553 + } 1.554 + } 1.555 + continue; 1.556 + } 1.557 + 1.558 + switch(byte) { 1.559 + case RLE_ESCAPE_EOL: 1.560 + // End of Line: Go to next row 1.561 + mCurLine --; 1.562 + mCurPos = 0; 1.563 + mState = eRLEStateInitial; 1.564 + break; 1.565 + 1.566 + case RLE_ESCAPE_EOF: // EndOfFile 1.567 + mCurPos = mCurLine = 0; 1.568 + break; 1.569 + 1.570 + case RLE_ESCAPE_DELTA: 1.571 + mState = eRLEStateNeedXDelta; 1.572 + continue; 1.573 + 1.574 + default : // absolute mode 1.575 + // Save the number of pixels to read 1.576 + mStateData = byte; 1.577 + if (mCurPos + mStateData > (uint32_t)mBIH.width) { 1.578 + // We can work around bitmaps that specify one 1.579 + // pixel too many, but only if their width is odd. 1.580 + mStateData -= mBIH.width & 1; 1.581 + if (mCurPos + mStateData > (uint32_t)mBIH.width) { 1.582 + PostDataError(); 1.583 + return; 1.584 + } 1.585 + } 1.586 + 1.587 + // See if we will need to skip a byte 1.588 + // to word align the pixel data 1.589 + // mStateData is a number of pixels 1.590 + // so allow for the RLE compression type 1.591 + // Pixels RLE8=1 RLE4=2 1.592 + // 1 Pad Pad 1.593 + // 2 No Pad 1.594 + // 3 Pad No 1.595 + // 4 No No 1.596 + if (((mStateData - 1) & mBIH.compression) != 0) 1.597 + mState = eRLEStateAbsoluteMode; 1.598 + else 1.599 + mState = eRLEStateAbsoluteModePadded; 1.600 + continue; 1.601 + } 1.602 + break; 1.603 + 1.604 + case eRLEStateNeedXDelta: 1.605 + // Handle the XDelta and proceed to get Y Delta 1.606 + byte = *aBuffer++; 1.607 + aCount--; 1.608 + mCurPos += byte; 1.609 + if (mCurPos > mBIH.width) 1.610 + mCurPos = mBIH.width; 1.611 + 1.612 + mState = eRLEStateNeedYDelta; 1.613 + continue; 1.614 + 1.615 + case eRLEStateNeedYDelta: 1.616 + // Get the Y Delta and then "handle" the move 1.617 + byte = *aBuffer++; 1.618 + aCount--; 1.619 + mState = eRLEStateInitial; 1.620 + mCurLine -= std::min<int32_t>(byte, mCurLine); 1.621 + break; 1.622 + 1.623 + case eRLEStateAbsoluteMode: // Absolute Mode 1.624 + case eRLEStateAbsoluteModePadded: 1.625 + if (mStateData) { 1.626 + // In absolute mode, the second byte (mStateData) 1.627 + // represents the number of pixels 1.628 + // that follow, each of which contains 1.629 + // the color index of a single pixel. 1.630 + uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos); 1.631 + uint32_t* oldPos = d; 1.632 + if (mBIH.compression == BI_RLE8) { 1.633 + while (aCount > 0 && mStateData > 0) { 1.634 + byte = *aBuffer++; 1.635 + aCount--; 1.636 + SetPixel(d, byte, mColors); 1.637 + mStateData--; 1.638 + } 1.639 + } else { 1.640 + while (aCount > 0 && mStateData > 0) { 1.641 + byte = *aBuffer++; 1.642 + aCount--; 1.643 + Set4BitPixel(d, byte, mStateData, mColors); 1.644 + } 1.645 + } 1.646 + mCurPos += d - oldPos; 1.647 + } 1.648 + 1.649 + if (mStateData == 0) { 1.650 + // In absolute mode, each run must 1.651 + // be aligned on a word boundary 1.652 + 1.653 + if (mState == eRLEStateAbsoluteMode) { // Word Aligned 1.654 + mState = eRLEStateInitial; 1.655 + } else if (aCount > 0) { // Not word Aligned 1.656 + // "next" byte is just a padding byte 1.657 + // so "move" past it and we can continue 1.658 + aBuffer++; 1.659 + aCount--; 1.660 + mState = eRLEStateInitial; 1.661 + } 1.662 + } 1.663 + // else state is still eRLEStateAbsoluteMode 1.664 + continue; 1.665 + 1.666 + default : 1.667 + NS_ABORT_IF_FALSE(0, "BMP RLE decompression: unknown state!"); 1.668 + PostDecoderError(NS_ERROR_UNEXPECTED); 1.669 + return; 1.670 + } 1.671 + // Because of the use of the continue statement 1.672 + // we only get here for eol, eof or y delta 1.673 + if (mCurLine == 0) { // Finished last line 1.674 + break; 1.675 + } 1.676 + } 1.677 + } 1.678 + } 1.679 + 1.680 + const uint32_t rows = mOldLine - mCurLine; 1.681 + if (rows) { 1.682 + 1.683 + // Invalidate 1.684 + nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine, 1.685 + mBIH.width, rows); 1.686 + PostInvalidation(r); 1.687 + 1.688 + mOldLine = mCurLine; 1.689 + } 1.690 + 1.691 + return; 1.692 +} 1.693 + 1.694 +void nsBMPDecoder::ProcessFileHeader() 1.695 +{ 1.696 + memset(&mBFH, 0, sizeof(mBFH)); 1.697 + memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature)); 1.698 + memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize)); 1.699 + memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved)); 1.700 + memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset)); 1.701 + memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize)); 1.702 + 1.703 + // Now correct the endianness of the header 1.704 + mBFH.filesize = LittleEndian::readUint32(&mBFH.filesize); 1.705 + mBFH.dataoffset = LittleEndian::readUint32(&mBFH.dataoffset); 1.706 + mBFH.bihsize = LittleEndian::readUint32(&mBFH.bihsize); 1.707 +} 1.708 + 1.709 +void nsBMPDecoder::ProcessInfoHeader() 1.710 +{ 1.711 + memset(&mBIH, 0, sizeof(mBIH)); 1.712 + if (mBFH.bihsize == 12) { // OS/2 Bitmap 1.713 + memcpy(&mBIH.width, mRawBuf, 2); 1.714 + memcpy(&mBIH.height, mRawBuf + 2, 2); 1.715 + memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes)); 1.716 + memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp)); 1.717 + } 1.718 + else { 1.719 + memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width)); 1.720 + memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height)); 1.721 + memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes)); 1.722 + memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp)); 1.723 + memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression)); 1.724 + memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size)); 1.725 + memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm)); 1.726 + memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm)); 1.727 + memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors)); 1.728 + memcpy(&mBIH.important_colors, mRawBuf + 32, sizeof(mBIH.important_colors)); 1.729 + } 1.730 + 1.731 + // Convert endianness 1.732 + mBIH.width = LittleEndian::readUint32(&mBIH.width); 1.733 + mBIH.height = LittleEndian::readUint32(&mBIH.height); 1.734 + mBIH.planes = LittleEndian::readUint16(&mBIH.planes); 1.735 + mBIH.bpp = LittleEndian::readUint16(&mBIH.bpp); 1.736 + 1.737 + mBIH.compression = LittleEndian::readUint32(&mBIH.compression); 1.738 + mBIH.image_size = LittleEndian::readUint32(&mBIH.image_size); 1.739 + mBIH.xppm = LittleEndian::readUint32(&mBIH.xppm); 1.740 + mBIH.yppm = LittleEndian::readUint32(&mBIH.yppm); 1.741 + mBIH.colors = LittleEndian::readUint32(&mBIH.colors); 1.742 + mBIH.important_colors = LittleEndian::readUint32(&mBIH.important_colors); 1.743 +} 1.744 + 1.745 +} // namespace image 1.746 +} // namespace mozilla