image/decoders/nsBMPDecoder.cpp

changeset 0
6474c204b198
     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

mercurial