image/decoders/nsBMPDecoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /* I got the format description from http://www.daubnet.com/formats/BMP.html */
     7 /* This is a Cross-Platform BMP Decoder, which should work everywhere, including
     8  * Big-Endian machines like the PowerPC. */
    10 #include <stdlib.h>
    12 #include "ImageLogging.h"
    13 #include "mozilla/Endian.h"
    14 #include "nsBMPDecoder.h"
    16 #include "nsIInputStream.h"
    17 #include "RasterImage.h"
    18 #include <algorithm>
    20 namespace mozilla {
    21 namespace image {
    23 #ifdef PR_LOGGING
    24 static PRLogModuleInfo *
    25 GetBMPLog()
    26 {
    27   static PRLogModuleInfo *sBMPLog;
    28   if (!sBMPLog)
    29     sBMPLog = PR_NewLogModule("BMPDecoder");
    30   return sBMPLog;
    31 }
    32 #endif
    34 // Convert from row (1..height) to absolute line (0..height-1)
    35 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
    36 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
    38 nsBMPDecoder::nsBMPDecoder(RasterImage &aImage)
    39  : Decoder(aImage)
    40 {
    41   mColors = nullptr;
    42   mRow = nullptr;
    43   mCurPos = mPos = mNumColors = mRowBytes = 0;
    44   mOldLine = mCurLine = 1; // Otherwise decoder will never start
    45   mState = eRLEStateInitial;
    46   mStateData = 0;
    47   mLOH = WIN_V3_HEADER_LENGTH;
    48   mUseAlphaData = mHaveAlphaData = false;
    49 }
    51 nsBMPDecoder::~nsBMPDecoder()
    52 {
    53   delete[] mColors;
    54   if (mRow) {
    55       moz_free(mRow);
    56   }
    57 }
    59 // Sets whether or not the BMP will use alpha data
    60 void 
    61 nsBMPDecoder::SetUseAlphaData(bool useAlphaData) 
    62 {
    63   mUseAlphaData = useAlphaData;
    64 }
    66 // Obtains the bits per pixel from the internal BIH header
    67 int32_t 
    68 nsBMPDecoder::GetBitsPerPixel() const
    69 {
    70   return mBIH.bpp;
    71 }
    73 // Obtains the width from the internal BIH header
    74 int32_t 
    75 nsBMPDecoder::GetWidth() const
    76 {
    77   return mBIH.width; 
    78 }
    80 // Obtains the abs-value of the height from the internal BIH header
    81 int32_t 
    82 nsBMPDecoder::GetHeight() const
    83 {
    84   return abs(mBIH.height);
    85 }
    87 // Obtains the internal output image buffer
    88 uint32_t* 
    89 nsBMPDecoder::GetImageData() 
    90 {
    91   return reinterpret_cast<uint32_t*>(mImageData);
    92 }
    94 // Obtains the size of the compressed image resource
    95 int32_t 
    96 nsBMPDecoder::GetCompressedImageSize() const
    97 {
    98   // For everything except BI_RGB the header field must be defined
    99   if (mBIH.compression != BI_RGB) {
   100     return mBIH.image_size;
   101   }
   103   // mBIH.image_size isn't always filled for BI_RGB so calculate it manually
   104   // The pixel array size is calculated based on extra 4 byte boundary padding
   105   uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
   106   // Pad to DWORD Boundary
   107   if (rowSize % 4) {
   108     rowSize += (4 - (rowSize % 4));
   109   }
   111   // The height should be the absolute value of what the height is in the BIH.
   112   // If positive the bitmap is stored bottom to top, otherwise top to bottom
   113   int32_t pixelArraySize = rowSize * GetHeight();
   114   return pixelArraySize;
   115 }
   117 // Obtains whether or not a BMP file had alpha data in its 4th byte
   118 // for 32BPP bitmaps.  Only use after the bitmap has been processed.
   119 bool 
   120 nsBMPDecoder::HasAlphaData() const 
   121 {
   122   return mHaveAlphaData;
   123 }
   126 void
   127 nsBMPDecoder::FinishInternal()
   128 {
   129     // We shouldn't be called in error cases
   130     NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!");
   132     // We should never make multiple frames
   133     NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?");
   135     // Send notifications if appropriate
   136     if (!IsSizeDecode() && HasSize()) {
   138         // Invalidate
   139         nsIntRect r(0, 0, mBIH.width, GetHeight());
   140         PostInvalidation(r);
   142         if (mUseAlphaData) {
   143           PostFrameStop(FrameBlender::kFrameHasAlpha);
   144         } else {
   145           PostFrameStop(FrameBlender::kFrameOpaque);
   146         }
   147         PostDecodeDone();
   148     }
   149 }
   151 // ----------------------------------------
   152 // Actual Data Processing
   153 // ----------------------------------------
   155 static void calcBitmask(uint32_t aMask, uint8_t& aBegin, uint8_t& aLength)
   156 {
   157     // find the rightmost 1
   158     uint8_t pos;
   159     bool started = false;
   160     aBegin = aLength = 0;
   161     for (pos = 0; pos <= 31; pos++) {
   162         if (!started && (aMask & (1 << pos))) {
   163             aBegin = pos;
   164             started = true;
   165         }
   166         else if (started && !(aMask & (1 << pos))) {
   167             aLength = pos - aBegin;
   168             break;
   169         }
   170     }
   171 }
   173 NS_METHOD nsBMPDecoder::CalcBitShift()
   174 {
   175     uint8_t begin, length;
   176     // red
   177     calcBitmask(mBitFields.red, begin, length);
   178     mBitFields.redRightShift = begin;
   179     mBitFields.redLeftShift = 8 - length;
   180     // green
   181     calcBitmask(mBitFields.green, begin, length);
   182     mBitFields.greenRightShift = begin;
   183     mBitFields.greenLeftShift = 8 - length;
   184     // blue
   185     calcBitmask(mBitFields.blue, begin, length);
   186     mBitFields.blueRightShift = begin;
   187     mBitFields.blueLeftShift = 8 - length;
   188     return NS_OK;
   189 }
   191 void
   192 nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy)
   193 {
   194     NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
   196     // aCount=0 means EOF, mCurLine=0 means we're past end of image
   197     if (!aCount || !mCurLine)
   198         return;
   200     if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
   201         uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos;
   202         if (toCopy > aCount)
   203             toCopy = aCount;
   204         memcpy(mRawBuf + mPos, aBuffer, toCopy);
   205         mPos += toCopy;
   206         aCount -= toCopy;
   207         aBuffer += toCopy;
   208     }
   209     if (mPos == BFH_INTERNAL_LENGTH) {
   210         ProcessFileHeader();
   211         if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') {
   212             PostDataError();
   213             return;
   214         }
   215         if (mBFH.bihsize == OS2_BIH_LENGTH)
   216             mLOH = OS2_HEADER_LENGTH;
   217     }
   218     if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
   219         uint32_t toCopy = mLOH - mPos;
   220         if (toCopy > aCount)
   221             toCopy = aCount;
   222         memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy);
   223         mPos += toCopy;
   224         aCount -= toCopy;
   225         aBuffer += toCopy;
   226     }
   228     // HasSize is called to ensure that if at this point mPos == mLOH but
   229     // we have no data left to process, the next time WriteInternal is called
   230     // we won't enter this condition again.
   231     if (mPos == mLOH && !HasSize()) {
   232         ProcessInfoHeader();
   233         PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP is %lix%lix%lu. compression=%lu\n",
   234                mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
   235         // Verify we support this bit depth
   236         if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
   237             mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) {
   238           PostDataError();
   239           return;
   240         }
   242         // BMPs with negative width are invalid
   243         // Reject extremely wide images to keep the math sane
   244         const int32_t k64KWidth = 0x0000FFFF;
   245         if (mBIH.width < 0 || mBIH.width > k64KWidth) {
   246             PostDataError();
   247             return;
   248         }
   250         if (mBIH.height == INT_MIN) {
   251             PostDataError();
   252             return;
   253         }
   255         uint32_t real_height = GetHeight();
   257         // Post our size to the superclass
   258         PostSize(mBIH.width, real_height);
   259         if (HasError()) {
   260           // Setting the size led to an error.
   261           return;
   262         }
   264         // We have the size. If we're doing a size decode, we got what
   265         // we came for.
   266         if (IsSizeDecode())
   267             return;
   269         // We're doing a real decode.
   270         mOldLine = mCurLine = real_height;
   272         if (mBIH.bpp <= 8) {
   273             mNumColors = 1 << mBIH.bpp;
   274             if (mBIH.colors && mBIH.colors < mNumColors)
   275                 mNumColors = mBIH.colors;
   277             // Always allocate 256 even though mNumColors might be smaller
   278             mColors = new colorTable[256];
   279             memset(mColors, 0, 256 * sizeof(colorTable));
   280         }
   281         else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
   282             // Use default 5-5-5 format
   283             mBitFields.red   = 0x7C00;
   284             mBitFields.green = 0x03E0;
   285             mBitFields.blue  = 0x001F;
   286             CalcBitShift();
   287         }
   289         // Make sure we have a valid value for our supported compression modes
   290         // before adding the frame
   291         if (mBIH.compression != BI_RGB && mBIH.compression != BI_RLE8 && 
   292             mBIH.compression != BI_RLE4 && mBIH.compression != BI_BITFIELDS) {
   293           PostDataError();
   294           return;
   295         }
   297         // If we have RLE4 or RLE8 or BI_ALPHABITFIELDS, then ensure we
   298         // have valid BPP values before adding the frame
   299         if (mBIH.compression == BI_RLE8 && mBIH.bpp != 8) {
   300           PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 
   301                  ("BMP RLE8 compression only supports 8 bits per pixel\n"));
   302           PostDataError();
   303           return;
   304         }
   305         if (mBIH.compression == BI_RLE4 && mBIH.bpp != 4 && mBIH.bpp != 1) {
   306           PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 
   307                  ("BMP RLE4 compression only supports 4 bits per pixel\n"));
   308           PostDataError();
   309           return;
   310         }
   311         if (mBIH.compression == BI_ALPHABITFIELDS && 
   312             mBIH.bpp != 16 && mBIH.bpp != 32) {
   313           PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 
   314                  ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n"));
   315           PostDataError();
   316           return;
   317         }
   319         if (mBIH.compression != BI_RLE8 && mBIH.compression != BI_RLE4 &&
   320             mBIH.compression != BI_ALPHABITFIELDS) {
   321             // mRow is not used for RLE encoded images
   322             mRow = (uint8_t*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4);
   323             // + 4 because the line is padded to a 4 bit boundary, but I don't want
   324             // to make exact calculations here, that's unnecessary.
   325             // Also, it compensates rounding error.
   326             if (!mRow) {
   327               PostDataError();
   328               return;
   329             }
   330         }
   331         if (!mImageData) {
   332             PostDecoderError(NS_ERROR_FAILURE);
   333             return;
   334         }
   336         // Prepare for transparency
   337         if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
   338             // Clear the image, as the RLE may jump over areas
   339             memset(mImageData, 0, mImageDataLength);
   340         }
   341     }
   343     if (mColors && mPos >= mLOH) {
   344       // OS/2 Bitmaps have no padding byte
   345       uint8_t bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4;
   346       if (mPos < (mLOH + mNumColors * bytesPerColor)) {
   347         // Number of bytes already received
   348         uint32_t colorBytes = mPos - mLOH; 
   349         // Color which is currently received
   350         uint8_t colorNum = colorBytes / bytesPerColor;
   351         uint8_t at = colorBytes % bytesPerColor;
   352         while (aCount && (mPos < (mLOH + mNumColors * bytesPerColor))) {
   353             switch (at) {
   354                 case 0:
   355                     mColors[colorNum].blue = *aBuffer;
   356                     break;
   357                 case 1:
   358                     mColors[colorNum].green = *aBuffer;
   359                     break;
   360                 case 2:
   361                     mColors[colorNum].red = *aBuffer;
   362                     // If there is no padding byte, increment the color index
   363                     // since we're done with the current color.
   364                     if (bytesPerColor == 3)
   365                       colorNum++;
   366                     break;
   367                 case 3:
   368                     // This is a padding byte only in Windows BMPs. Increment
   369                     // the color index since we're done with the current color.
   370                     colorNum++;
   371                     break;
   372             }
   373             mPos++; aBuffer++; aCount--;
   374             at = (at + 1) % bytesPerColor;
   375         }
   376       }
   377     }
   378     else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH)) {
   379         // If compression is used, this is a windows bitmap, hence we can
   380         // use WIN_HEADER_LENGTH instead of mLOH
   381         uint32_t toCopy = (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
   382         if (toCopy > aCount)
   383             toCopy = aCount;
   384         memcpy(mRawBuf + (mPos - WIN_V3_HEADER_LENGTH), aBuffer, toCopy);
   385         mPos += toCopy;
   386         aBuffer += toCopy;
   387         aCount -= toCopy;
   388     }
   389     if (mPos == WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH && 
   390         mBIH.compression == BI_BITFIELDS) {
   391         mBitFields.red = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf));
   392         mBitFields.green = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 4));
   393         mBitFields.blue = LittleEndian::readUint32(reinterpret_cast<uint32_t*>(mRawBuf + 8));
   394         CalcBitShift();
   395     }
   396     while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data
   397         mPos++; aBuffer++; aCount--;
   398     }
   399     if (aCount && ++mPos >= mBFH.dataoffset) {
   400         // Need to increment mPos, else we might get to mPos==mLOH again
   401         // From now on, mPos is irrelevant
   402         if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) {
   403             uint32_t rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // + 7 to round up
   404             if (rowSize % 4) {
   405                 rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
   406             }
   407             uint32_t toCopy;
   408             do {
   409                 toCopy = rowSize - mRowBytes;
   410                 if (toCopy) {
   411                     if (toCopy > aCount)
   412                         toCopy = aCount;
   413                     memcpy(mRow + mRowBytes, aBuffer, toCopy);
   414                     aCount -= toCopy;
   415                     aBuffer += toCopy;
   416                     mRowBytes += toCopy;
   417                 }
   418                 if (rowSize == mRowBytes) {
   419                     // Collected a whole row into mRow, process it
   420                     uint8_t* p = mRow;
   421                     uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, 0);
   422                     uint32_t lpos = mBIH.width;
   423                     switch (mBIH.bpp) {
   424                       case 1:
   425                         while (lpos > 0) {
   426                           int8_t bit;
   427                           uint8_t idx;
   428                           for (bit = 7; bit >= 0 && lpos > 0; bit--) {
   429                               idx = (*p >> bit) & 1;
   430                               SetPixel(d, idx, mColors);
   431                               --lpos;
   432                           }
   433                           ++p;
   434                         }
   435                         break;
   436                       case 4:
   437                         while (lpos > 0) {
   438                           Set4BitPixel(d, *p, lpos, mColors);
   439                           ++p;
   440                         }
   441                         break;
   442                       case 8:
   443                         while (lpos > 0) {
   444                           SetPixel(d, *p, mColors);
   445                           --lpos;
   446                           ++p;
   447                         }
   448                         break;
   449                       case 16:
   450                         while (lpos > 0) {
   451                           uint16_t val = LittleEndian::readUint16(reinterpret_cast<uint16_t*>(p));
   452                           SetPixel(d,
   453                                   (val & mBitFields.red) >> mBitFields.redRightShift << mBitFields.redLeftShift,
   454                                   (val & mBitFields.green) >> mBitFields.greenRightShift << mBitFields.greenLeftShift,
   455                                   (val & mBitFields.blue) >> mBitFields.blueRightShift << mBitFields.blueLeftShift);
   456                           --lpos;
   457                           p+=2;
   458                         }
   459                         break;
   460                       case 24:
   461                         while (lpos > 0) {
   462                           SetPixel(d, p[2], p[1], p[0]);
   463                           p += 2;
   464                           --lpos;
   465                           ++p;
   466                         }
   467                         break;
   468                       case 32:
   469                         while (lpos > 0) {
   470                           if (mUseAlphaData) {
   471                             if (!mHaveAlphaData && p[3]) {
   472                               // Non-zero alpha byte detected! Clear previous
   473                               // pixels that we have already processed.
   474                               // This works because we know that if we 
   475                               // are reaching here then the alpha data in byte 
   476                               // 4 has been right all along.  And we know it
   477                               // has been set to 0 the whole time, so that 
   478                               // means that everything is transparent so far.
   479                               uint32_t* start = reinterpret_cast<uint32_t*>(mImageData) + GetWidth() * (mCurLine - 1);
   480                               uint32_t heightDifference = GetHeight() - mCurLine + 1;
   481                               uint32_t pixelCount = GetWidth() * heightDifference;
   483                               memset(start, 0, pixelCount * sizeof(uint32_t));
   485                               mHaveAlphaData = true;
   486                             }
   487                             SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
   488                           } else {
   489                             SetPixel(d, p[2], p[1], p[0]);
   490                           }
   491                           p += 4;
   492                           --lpos;
   493                         }
   494                         break;
   495                       default:
   496                         NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
   497                     }
   498                     mCurLine --;
   499                     if (mCurLine == 0) { // Finished last line
   500                       break;
   501                     }
   502                     mRowBytes = 0;
   504                 }
   505             } while (aCount > 0);
   506         }
   507         else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
   508             if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) || 
   509                 ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
   510               PR_LOG(GetBMPLog(), PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
   511               PostDataError();
   512               return;
   513             }
   515             while (aCount > 0) {
   516                 uint8_t byte;
   518                 switch(mState) {
   519                     case eRLEStateInitial:
   520                         mStateData = (uint8_t)*aBuffer++;
   521                         aCount--;
   523                         mState = eRLEStateNeedSecondEscapeByte;
   524                         continue;
   526                     case eRLEStateNeedSecondEscapeByte:
   527                         byte = *aBuffer++;
   528                         aCount--;
   529                         if (mStateData != RLE_ESCAPE) { // encoded mode
   530                             // Encoded mode consists of two bytes: 
   531                             // the first byte (mStateData) specifies the
   532                             // number of consecutive pixels to be drawn 
   533                             // using the color index contained in
   534                             // the second byte
   535                             // Work around bitmaps that specify too many pixels
   536                             mState = eRLEStateInitial;
   537                             uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos, mStateData);
   538                             if (pixelsNeeded) {
   539                                 uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
   540                                 mCurPos += pixelsNeeded;
   541                                 if (mBIH.compression == BI_RLE8) {
   542                                     do {
   543                                         SetPixel(d, byte, mColors);
   544                                         pixelsNeeded --;
   545                                     } while (pixelsNeeded);
   546                                 } else {
   547                                     do {
   548                                         Set4BitPixel(d, byte, pixelsNeeded, mColors);
   549                                     } while (pixelsNeeded);
   550                                 }
   551                             }
   552                             continue;
   553                         }
   555                         switch(byte) {
   556                             case RLE_ESCAPE_EOL:
   557                                 // End of Line: Go to next row
   558                                 mCurLine --;
   559                                 mCurPos = 0;
   560                                 mState = eRLEStateInitial;
   561                                 break;
   563                             case RLE_ESCAPE_EOF: // EndOfFile
   564                                 mCurPos = mCurLine = 0;
   565                                 break;
   567                             case RLE_ESCAPE_DELTA:
   568                                 mState = eRLEStateNeedXDelta;
   569                                 continue;
   571                             default : // absolute mode
   572                                 // Save the number of pixels to read
   573                                 mStateData = byte;
   574                                 if (mCurPos + mStateData > (uint32_t)mBIH.width) {
   575                                     // We can work around bitmaps that specify one
   576                                     // pixel too many, but only if their width is odd.
   577                                     mStateData -= mBIH.width & 1;
   578                                     if (mCurPos + mStateData > (uint32_t)mBIH.width) {
   579                                         PostDataError();
   580                                         return;
   581                                     }
   582                                 }
   584                                 // See if we will need to skip a byte
   585                                 // to word align the pixel data
   586                                 // mStateData is a number of pixels
   587                                 // so allow for the RLE compression type
   588                                 // Pixels RLE8=1 RLE4=2
   589                                 //    1    Pad    Pad
   590                                 //    2    No     Pad
   591                                 //    3    Pad    No
   592                                 //    4    No     No
   593                                 if (((mStateData - 1) & mBIH.compression) != 0)
   594                                     mState = eRLEStateAbsoluteMode;
   595                                 else
   596                                     mState = eRLEStateAbsoluteModePadded;
   597                                 continue;
   598                         }
   599                         break;
   601                     case eRLEStateNeedXDelta:
   602                         // Handle the XDelta and proceed to get Y Delta
   603                         byte = *aBuffer++;
   604                         aCount--;
   605                         mCurPos += byte;
   606                         if (mCurPos > mBIH.width)
   607                             mCurPos = mBIH.width;
   609                         mState = eRLEStateNeedYDelta;
   610                         continue;
   612                     case eRLEStateNeedYDelta:
   613                         // Get the Y Delta and then "handle" the move
   614                         byte = *aBuffer++;
   615                         aCount--;
   616                         mState = eRLEStateInitial;
   617                         mCurLine -= std::min<int32_t>(byte, mCurLine);
   618                         break;
   620                     case eRLEStateAbsoluteMode: // Absolute Mode
   621                     case eRLEStateAbsoluteModePadded:
   622                         if (mStateData) {
   623                             // In absolute mode, the second byte (mStateData)
   624                             // represents the number of pixels 
   625                             // that follow, each of which contains 
   626                             // the color index of a single pixel.
   627                             uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
   628                             uint32_t* oldPos = d;
   629                             if (mBIH.compression == BI_RLE8) {
   630                                 while (aCount > 0 && mStateData > 0) {
   631                                     byte = *aBuffer++;
   632                                     aCount--;
   633                                     SetPixel(d, byte, mColors);
   634                                     mStateData--;
   635                                 }
   636                             } else {
   637                                 while (aCount > 0 && mStateData > 0) {
   638                                     byte = *aBuffer++;
   639                                     aCount--;
   640                                     Set4BitPixel(d, byte, mStateData, mColors);
   641                                 }
   642                             }
   643                             mCurPos += d - oldPos;
   644                         }
   646                         if (mStateData == 0) {
   647                             // In absolute mode, each run must 
   648                             // be aligned on a word boundary
   650                             if (mState == eRLEStateAbsoluteMode) { // Word Aligned
   651                                 mState = eRLEStateInitial;
   652                             } else if (aCount > 0) {               // Not word Aligned
   653                                 // "next" byte is just a padding byte
   654                                 // so "move" past it and we can continue
   655                                 aBuffer++;
   656                                 aCount--;
   657                                 mState = eRLEStateInitial;
   658                             }
   659                         }
   660                         // else state is still eRLEStateAbsoluteMode
   661                         continue;
   663                     default :
   664                         NS_ABORT_IF_FALSE(0, "BMP RLE decompression: unknown state!");
   665                         PostDecoderError(NS_ERROR_UNEXPECTED);
   666                         return;
   667                 }
   668                 // Because of the use of the continue statement
   669                 // we only get here for eol, eof or y delta
   670                 if (mCurLine == 0) { // Finished last line
   671                     break;
   672                 }
   673             }
   674         }
   675     }
   677     const uint32_t rows = mOldLine - mCurLine;
   678     if (rows) {
   680         // Invalidate
   681         nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
   682                     mBIH.width, rows);
   683         PostInvalidation(r);
   685         mOldLine = mCurLine;
   686     }
   688     return;
   689 }
   691 void nsBMPDecoder::ProcessFileHeader()
   692 {
   693     memset(&mBFH, 0, sizeof(mBFH));
   694     memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature));
   695     memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize));
   696     memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved));
   697     memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset));
   698     memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize));
   700     // Now correct the endianness of the header
   701     mBFH.filesize = LittleEndian::readUint32(&mBFH.filesize);
   702     mBFH.dataoffset = LittleEndian::readUint32(&mBFH.dataoffset);
   703     mBFH.bihsize = LittleEndian::readUint32(&mBFH.bihsize);
   704 }
   706 void nsBMPDecoder::ProcessInfoHeader()
   707 {
   708     memset(&mBIH, 0, sizeof(mBIH));
   709     if (mBFH.bihsize == 12) { // OS/2 Bitmap
   710         memcpy(&mBIH.width, mRawBuf, 2);
   711         memcpy(&mBIH.height, mRawBuf + 2, 2);
   712         memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes));
   713         memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp));
   714     }
   715     else {
   716         memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width));
   717         memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height));
   718         memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes));
   719         memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp));
   720         memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression));
   721         memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size));
   722         memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm));
   723         memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm));
   724         memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors));
   725         memcpy(&mBIH.important_colors, mRawBuf + 32, sizeof(mBIH.important_colors));
   726     }
   728     // Convert endianness
   729     mBIH.width = LittleEndian::readUint32(&mBIH.width);
   730     mBIH.height = LittleEndian::readUint32(&mBIH.height);
   731     mBIH.planes = LittleEndian::readUint16(&mBIH.planes);
   732     mBIH.bpp = LittleEndian::readUint16(&mBIH.bpp);
   734     mBIH.compression = LittleEndian::readUint32(&mBIH.compression);
   735     mBIH.image_size = LittleEndian::readUint32(&mBIH.image_size);
   736     mBIH.xppm = LittleEndian::readUint32(&mBIH.xppm);
   737     mBIH.yppm = LittleEndian::readUint32(&mBIH.yppm);
   738     mBIH.colors = LittleEndian::readUint32(&mBIH.colors);
   739     mBIH.important_colors = LittleEndian::readUint32(&mBIH.important_colors);
   740 }
   742 } // namespace image
   743 } // namespace mozilla

mercurial