Back to index

lightning-sunbird  0.9+nobinonly
nsBMPDecoder.cpp
Go to the documentation of this file.
00001 /* vim:set tw=80 expandtab softtabstop=4 ts=4 sw=4: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla BMP Decoder.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Christian Biesinger <cbiesinger@web.de>.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Neil Rashbrook <neil@parkwaycc.co.uk>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 /* I got the format description from http://www.daubnet.com/formats/BMP.html */
00039 
00040 /* This is a Cross-Platform BMP Decoder, which should work everywhere, including
00041  * Big-Endian machines like the PowerPC. */
00042 
00043 #include <stdlib.h>
00044 
00045 #include "nsBMPDecoder.h"
00046 
00047 #include "nsIInputStream.h"
00048 #include "nsIComponentManager.h"
00049 #include "imgIContainerObserver.h"
00050 
00051 #include "imgILoad.h"
00052 
00053 #include "prlog.h"
00054 
00055 #ifdef PR_LOGGING
00056 PRLogModuleInfo *gBMPLog = PR_NewLogModule("BMPDecoder");
00057 #endif
00058 
00059 NS_IMPL_ISUPPORTS1(nsBMPDecoder, imgIDecoder)
00060 
00061 nsBMPDecoder::nsBMPDecoder()
00062 {
00063     mColors = nsnull;
00064     mRow = nsnull;
00065     mPos = mNumColors = mRowBytes = 0;
00066     mCurLine = 1; // Otherwise decoder will never start
00067     mState = eRLEStateInitial;
00068     mStateData = 0;
00069     mAlpha = mAlphaPtr = mDecoded = mDecoding = nsnull;
00070     mLOH = WIN_HEADER_LENGTH;
00071 }
00072 
00073 nsBMPDecoder::~nsBMPDecoder()
00074 {
00075     delete[] mColors;
00076     free(mRow);
00077     if (mAlpha)
00078         free(mAlpha);
00079     if (mDecoded)
00080         free(mDecoded);
00081 }
00082 
00083 NS_IMETHODIMP nsBMPDecoder::Init(imgILoad *aLoad)
00084 {
00085     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aLoad));
00086     mObserver = do_QueryInterface(aLoad);
00087 
00088     nsresult rv;
00089     mImage = do_CreateInstance("@mozilla.org/image/container;1", &rv);
00090     if (NS_FAILED(rv))
00091         return rv;
00092 
00093     mFrame = do_CreateInstance("@mozilla.org/gfx/image/frame;2", &rv);
00094     if (NS_FAILED(rv))
00095         return rv;
00096 
00097     return aLoad->SetImage(mImage);
00098 }
00099 
00100 NS_IMETHODIMP nsBMPDecoder::Close()
00101 {
00102     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Close()\n"));
00103     if (mObserver) {
00104         mObserver->OnStopContainer(nsnull, mImage);
00105         mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
00106         mObserver = nsnull;
00107     }
00108     mImage = nsnull;
00109     mFrame = nsnull;
00110     return NS_OK;
00111 }
00112 
00113 NS_IMETHODIMP nsBMPDecoder::Flush()
00114 {
00115     mFrame->SetMutable(PR_FALSE);
00116     return NS_OK;
00117 }
00118 
00119 NS_METHOD nsBMPDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
00120                              const char* aFromRawSegment, PRUint32 aToOffset,
00121                              PRUint32 aCount, PRUint32 *aWriteCount) {
00122     nsBMPDecoder *decoder = NS_REINTERPRET_CAST(nsBMPDecoder*, aClosure);
00123     *aWriteCount = aCount;
00124     return decoder->ProcessData(aFromRawSegment, aCount);
00125 }
00126 
00127 NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
00128 {
00129     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount, aRetval));
00130 
00131     return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
00132 }
00133 
00134 // ----------------------------------------
00135 // Actual Data Processing
00136 // ----------------------------------------
00137 
00138 nsresult nsBMPDecoder::SetData()
00139 {
00140     PRInt32 line = (mBIH.height < 0) ? (-mBIH.height - mCurLine--) : --mCurLine;
00141     nsresult rv = mFrame->SetImageData(mDecoded, mBpr, line * mBpr);
00142     NS_ENSURE_SUCCESS(rv, rv);
00143 
00144     nsIntRect r(0, line, mBIH.width, 1);
00145     return mObserver->OnDataAvailable(nsnull, mFrame, &r);
00146 }
00147 
00148 nsresult nsBMPDecoder::WriteRLERows(PRUint32 rows)
00149 {
00150     PRUint32 alpha, cnt, line;
00151     PRUint8 bit;
00152     PRUint8* pos = mAlpha;
00153 
00154     // First pack the alpha data
00155     nsresult rv = mFrame->GetAlphaBytesPerRow(&alpha);
00156     NS_ENSURE_SUCCESS(rv, rv);
00157     for (cnt = 0; cnt < alpha; cnt++) {
00158         PRUint8 byte = 0;
00159         for (bit = 128; bit; bit >>= 1)
00160             byte |= *pos++ & bit;
00161         mAlpha[cnt] = byte;
00162     }
00163 
00164     for (cnt = 0; cnt < rows; cnt++) {
00165         line = (mBIH.height < 0) ? (-mBIH.height - mCurLine--) : --mCurLine;
00166         rv = mFrame->SetAlphaData(mAlpha, alpha, line * alpha);
00167         NS_ENSURE_SUCCESS(rv, rv);
00168         rv = mFrame->SetImageData(mDecoded, mBpr, line * mBpr);
00169         NS_ENSURE_SUCCESS(rv, rv);
00170         if (cnt == 0) {
00171             memset(mAlpha, 0, mBIH.width);
00172             memset(mDecoded, 0, mBpr);
00173         }
00174     }
00175 
00176     line = (mBIH.height < 0) ? (-mBIH.height - mCurLine - rows) : mCurLine;
00177     nsIntRect r(0, line, mBIH.width, rows);
00178     return mObserver->OnDataAvailable(nsnull, mFrame, &r);
00179 }
00180 
00181 static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
00182 {
00183     // find the rightmost 1
00184     PRUint8 pos;
00185     PRBool started = PR_FALSE;
00186     aBegin = aLength = 0;
00187     for (pos = 0; pos <= 31; pos++) {
00188         if (!started && (aMask & (1 << pos))) {
00189             aBegin = pos;
00190             started = PR_TRUE;
00191         }
00192         else if (started && !(aMask & (1 << pos))) {
00193             aLength = pos - aBegin;
00194             break;
00195         }
00196     }
00197 }
00198 
00199 NS_METHOD nsBMPDecoder::CalcBitShift()
00200 {
00201     PRUint8 begin, length;
00202     // red
00203     calcBitmask(mBitFields.red, begin, length);
00204     mBitFields.redRightShift = begin;
00205     mBitFields.redLeftShift = 8 - length;
00206     // green
00207     calcBitmask(mBitFields.green, begin, length);
00208     mBitFields.greenRightShift = begin;
00209     mBitFields.greenLeftShift = 8 - length;
00210     // blue
00211     calcBitmask(mBitFields.blue, begin, length);
00212     mBitFields.blueRightShift = begin;
00213     mBitFields.blueLeftShift = 8 - length;
00214     return NS_OK;
00215 }
00216 
00217 NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
00218 {
00219     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::ProcessData(%p, %lu)", aBuffer, aCount));
00220     if (!aCount || !mCurLine) // aCount=0 means EOF, mCurLine=0 means we're past end of image
00221         return NS_OK;
00222 
00223     nsresult rv;
00224     if (mPos < BFH_LENGTH) { /* In BITMAPFILEHEADER */
00225         PRUint32 toCopy = BFH_LENGTH - mPos;
00226         if (toCopy > aCount)
00227             toCopy = aCount;
00228         memcpy(mRawBuf + mPos, aBuffer, toCopy);
00229         mPos += toCopy;
00230         aCount -= toCopy;
00231         aBuffer += toCopy;
00232     }
00233     if (mPos == BFH_LENGTH) {
00234         rv = mObserver->OnStartDecode(nsnull);
00235         NS_ENSURE_SUCCESS(rv, rv);
00236         ProcessFileHeader();
00237         if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M')
00238             return NS_ERROR_FAILURE;
00239         if (mBFH.bihsize == OS2_BIH_LENGTH)
00240             mLOH = OS2_HEADER_LENGTH;
00241     }
00242     if (mPos >= BFH_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
00243         PRUint32 toCopy = mLOH - mPos;
00244         if (toCopy > aCount)
00245             toCopy = aCount;
00246         memcpy(mRawBuf + (mPos - BFH_LENGTH), aBuffer, toCopy);
00247         mPos += toCopy;
00248         aCount -= toCopy;
00249         aBuffer += toCopy;
00250     }
00251     if (mPos == mLOH) {
00252         ProcessInfoHeader();
00253         PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP image is %lix%lix%lu. compression=%lu\n",
00254             mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
00255         // Verify we support this bit depth
00256         if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
00257             mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32)
00258           return NS_ERROR_UNEXPECTED;
00259 
00260         if (mBIH.bpp <= 8) {
00261             mNumColors = 1 << mBIH.bpp;
00262             if (mBIH.colors && mBIH.colors < mNumColors)
00263                 mNumColors = mBIH.colors;
00264 
00265             // Always allocate 256 even though mNumColors might be smaller
00266             mColors = new colorTable[256];
00267             if (!mColors)
00268                 return NS_ERROR_OUT_OF_MEMORY;
00269 
00270             memset(mColors, 0, 256 * sizeof(colorTable));
00271         }
00272         else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
00273             // Use default 5-5-5 format
00274             mBitFields.red   = 0x7C00;
00275             mBitFields.green = 0x03E0;
00276             mBitFields.blue  = 0x001F;
00277             CalcBitShift();
00278         }
00279         // BMPs with negative width are invalid
00280         // Reject extremely wide images to keep the math sane
00281         const PRInt32 k64KWidth = 0x0000FFFF;
00282         if (mBIH.width < 0 || mBIH.width > k64KWidth)
00283             return NS_ERROR_FAILURE;
00284 
00285         PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
00286         rv = mImage->Init(mBIH.width, real_height, mObserver);
00287         NS_ENSURE_SUCCESS(rv, rv);
00288         rv = mObserver->OnStartContainer(nsnull, mImage);
00289         NS_ENSURE_SUCCESS(rv, rv);
00290         mCurLine = real_height;
00291 
00292         mRow = (PRUint8*)malloc((mBIH.width * mBIH.bpp)/8 + 4);
00293         // +4 because the line is padded to a 4 bit boundary, but I don't want
00294         // to make exact calculations here, that's unnecessary.
00295         // Also, it compensates rounding error.
00296         if (!mRow) {
00297             return NS_ERROR_OUT_OF_MEMORY;
00298         }
00299         if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
00300             rv = mFrame->Init(0, 0, mBIH.width, real_height, RLE_GFXFORMAT_ALPHA, 24);
00301         } else {
00302             rv = mFrame->Init(0, 0, mBIH.width, real_height, BMP_GFXFORMAT, 24);
00303         }
00304         NS_ENSURE_SUCCESS(rv, rv);
00305         rv = mImage->AppendFrame(mFrame);
00306         NS_ENSURE_SUCCESS(rv, rv);
00307         mObserver->OnStartFrame(nsnull, mFrame);
00308         NS_ENSURE_SUCCESS(rv, rv);
00309         rv = mFrame->GetImageBytesPerRow(&mBpr);
00310         NS_ENSURE_SUCCESS(rv, rv);
00311     }
00312     PRUint8 bpc; // bytes per color
00313     bpc = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; // OS/2 Bitmaps have no padding byte
00314     if (mColors && (mPos >= mLOH && (mPos < (mLOH + mNumColors * bpc)))) {
00315         // We will receive (mNumColors * bpc) bytes of color data
00316         PRUint32 colorBytes = mPos - mLOH; // Number of bytes already received
00317         PRUint8 colorNum = colorBytes / bpc; // Color which is currently received
00318         PRUint8 at = colorBytes % bpc;
00319         while (aCount && (mPos < (mLOH + mNumColors * bpc))) {
00320             switch (at) {
00321                 case 0:
00322                     mColors[colorNum].blue = *aBuffer;
00323                     break;
00324                 case 1:
00325                     mColors[colorNum].green = *aBuffer;
00326                     break;
00327                 case 2:
00328                     mColors[colorNum].red = *aBuffer;
00329                     colorNum++;
00330                     break;
00331                 case 3:
00332                     // This is a padding byte
00333                     break;
00334             }
00335             mPos++; aBuffer++; aCount--;
00336             at = (at + 1) % bpc;
00337         }
00338     }
00339     else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_HEADER_LENGTH + BITFIELD_LENGTH)) {
00340         // If compression is used, this is a windows bitmap, hence we can
00341         // use WIN_HEADER_LENGTH instead of mLOH
00342         PRUint32 toCopy = (WIN_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
00343         if (toCopy > aCount)
00344             toCopy = aCount;
00345         memcpy(mRawBuf + (mPos - WIN_HEADER_LENGTH), aBuffer, toCopy);
00346         mPos += toCopy;
00347         aBuffer += toCopy;
00348         aCount -= toCopy;
00349     }
00350     if (mBIH.compression == BI_BITFIELDS && mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH) {
00351         mBitFields.red = LITTLE_TO_NATIVE32(*(PRUint32*)mRawBuf);
00352         mBitFields.green = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 4));
00353         mBitFields.blue = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 8));
00354         CalcBitShift();
00355     }
00356     while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data
00357         mPos++; aBuffer++; aCount--;
00358     }
00359     if (aCount && ++mPos >= mBFH.dataoffset) {
00360         // Need to increment mPos, else we might get to mPos==mLOH again
00361         // From now on, mPos is irrelevant
00362         if (!mBIH.compression || mBIH.compression == BI_BITFIELDS) {
00363             PRUint32 rowSize = (mBIH.bpp * mBIH.width + 7) / 8; // +7 to round up
00364             if (rowSize % 4)
00365                 rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
00366             PRUint32 toCopy;
00367             do {
00368                 toCopy = rowSize - mRowBytes;
00369                 if (toCopy) {
00370                     if (toCopy > aCount)
00371                         toCopy = aCount;
00372                     memcpy(mRow + mRowBytes, aBuffer, toCopy);
00373                     aCount -= toCopy;
00374                     aBuffer += toCopy;
00375                     mRowBytes += toCopy;
00376                 }
00377                 if ((rowSize - mRowBytes) == 0) {
00378                     if (!mDecoded) {
00379                         mDecoded = (PRUint8*)malloc(mBpr);
00380                         if (!mDecoded)
00381                             return NS_ERROR_OUT_OF_MEMORY;
00382                     }
00383 
00384                     PRUint8* p = mRow;
00385                     PRUint8* d = mDecoded;
00386                     PRUint32 lpos = mBIH.width;
00387                     switch (mBIH.bpp) {
00388                       case 1:
00389                         while (lpos > 0) {
00390                           PRInt8 bit;
00391                           PRUint8 idx;
00392                           for (bit = 7; bit >= 0 && lpos > 0; bit--) {
00393                               idx = (*p >> bit) & 1;
00394                               SetPixel(d, idx, mColors);
00395                               --lpos;
00396                           }
00397                           ++p;
00398                         }
00399                         break;
00400                       case 4:
00401                         while (lpos > 0) {
00402                           Set4BitPixel(d, *p, lpos, mColors);
00403                           ++p;
00404                         }
00405                         break;
00406                       case 8:
00407                         while (lpos > 0) {
00408                           SetPixel(d, *p, mColors);
00409                           --lpos;
00410                           ++p;
00411                         }
00412                         break;
00413                       case 16:
00414                         while (lpos > 0) {
00415                           PRUint16 val = LITTLE_TO_NATIVE16(*(PRUint16*)p);
00416                           SetPixel(d,
00417                                   (val & mBitFields.red) >> mBitFields.redRightShift << mBitFields.redLeftShift,
00418                                   (val & mBitFields.green) >> mBitFields.greenRightShift << mBitFields.greenLeftShift,
00419                                   (val & mBitFields.blue) >> mBitFields.blueRightShift << mBitFields.blueLeftShift);
00420                           --lpos;
00421                           p+=2;
00422                         }
00423                         break;
00424                       case 32:
00425                       case 24:
00426                         while (lpos > 0) {
00427                           SetPixel(d, p[2], p[1], p[0]);
00428                           p += 2;
00429                           --lpos;
00430                           if (mBIH.bpp == 32)
00431                             p++; // Padding byte
00432                           ++p;
00433                         }
00434                         break;
00435                       default:
00436                         NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
00437                     }
00438                       
00439                     nsresult rv = SetData();
00440                     NS_ENSURE_SUCCESS(rv, rv);
00441 
00442                     if (mCurLine == 0) { // Finished last line
00443                         return mObserver->OnStopFrame(nsnull, mFrame);
00444                     }
00445                     mRowBytes = 0;
00446 
00447                 }
00448             } while (aCount > 0);
00449         } 
00450         else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
00451             if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) 
00452              || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
00453                 PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
00454                 return NS_ERROR_FAILURE;
00455             }
00456 
00457             if (!mAlpha) {
00458                 PRUint32 alpha;
00459                 rv = mFrame->GetAlphaBytesPerRow(&alpha);
00460                 NS_ENSURE_SUCCESS(rv, rv);
00461                 // Allocate an unpacked buffer
00462                 mAlpha = (PRUint8*)calloc(alpha, 8);
00463                 if (!mAlpha)
00464                   return NS_ERROR_OUT_OF_MEMORY;
00465                 mAlphaPtr = mAlpha;
00466             }
00467 
00468             if (!mDecoded) {
00469                 mDecoded = (PRUint8*)calloc(mBpr, 1);
00470                 if (!mDecoded)
00471                   return NS_ERROR_OUT_OF_MEMORY;
00472                 mDecoding = mDecoded;
00473             }
00474 
00475             while (aCount > 0) {
00476                 PRUint8 byte;
00477 
00478                 switch(mState) {
00479                     case eRLEStateInitial:
00480                         mStateData = (PRUint8)*aBuffer++;
00481                         aCount--;
00482 
00483                         mState = eRLEStateNeedSecondEscapeByte;
00484                         continue;
00485 
00486                     case eRLEStateNeedSecondEscapeByte:
00487                         byte = *aBuffer++;
00488                         aCount--;
00489                         if (mStateData != RLE_ESCAPE) { // encoded mode
00490                             // Encoded mode consists of two bytes: 
00491                             // the first byte (mStateData) specifies the
00492                             // number of consecutive pixels to be drawn 
00493                             // using the color index contained in
00494                             // the second byte
00495                             // Work around bitmaps that specify too many pixels
00496                             if (mAlphaPtr + mStateData > mAlpha + mBIH.width)
00497                                 mStateData = (PRUint32)(mAlpha + mBIH.width - mAlphaPtr);
00498                             memset(mAlphaPtr, 0xFF, mStateData);
00499                             mAlphaPtr += mStateData;
00500                             if (mBIH.compression == BI_RLE8) {
00501                                 while (mStateData > 0) {
00502                                     SetPixel(mDecoding, byte, mColors);
00503                                     mStateData--;
00504                                 }
00505                             } else {
00506                                 while (mStateData > 0) {
00507                                     Set4BitPixel(mDecoding, byte, mStateData, mColors);
00508                                 }
00509                             }
00510                             
00511                             mState = eRLEStateInitial;
00512                             continue;
00513                         }
00514 
00515                         switch(byte) {
00516                             case RLE_ESCAPE_EOL:
00517                                 // End of Line: Write out current row
00518                                 // and reset our row buffer
00519                                 rv = WriteRLERows(1);
00520                                 NS_ENSURE_SUCCESS(rv, rv);
00521                                 mAlphaPtr = mAlpha;
00522                                 mDecoding = mDecoded;
00523 
00524                                 mState = eRLEStateInitial;
00525                                 break;
00526 
00527                             case RLE_ESCAPE_EOF: // EndOfFile
00528                                 rv = WriteRLERows(mCurLine);
00529                                 NS_ENSURE_SUCCESS(rv, rv);
00530                                 break;
00531 
00532                             case RLE_ESCAPE_DELTA:
00533                                 mState = eRLEStateNeedXDelta;
00534                                 continue;
00535 
00536                             default : // absolute mode
00537                                 // Save the number of pixels to read
00538                                 mStateData = byte;
00539                                 if (mAlphaPtr + mStateData > mAlpha + mBIH.width) {
00540                                     // We can work around bitmaps that specify one
00541                                     // pixel too many, but only if their width is odd.
00542                                     mStateData -= mBIH.width & 1;
00543                                     if (mAlphaPtr + mStateData > mAlpha + mBIH.width)
00544                                         return NS_ERROR_FAILURE;
00545                                 }
00546                                 memset(mAlphaPtr, 0xFF, mStateData);
00547                                 mAlphaPtr += mStateData;
00548 
00549                                 // See if we will need to skip a byte
00550                                 // to word align the pixel data
00551                                 // mStateData is a number of pixels
00552                                 // so allow for the RLE compression type
00553                                 // Pixels RLE8=1 RLE4=2
00554                                 //    1    Pad    Pad
00555                                 //    2    No     Pad
00556                                 //    3    Pad    No
00557                                 //    4    No     No
00558                                 if (((mStateData - 1) & mBIH.compression) != 0)
00559                                     mState = eRLEStateAbsoluteMode;
00560                                 else
00561                                     mState = eRLEStateAbsoluteModePadded;
00562                                 continue;
00563                         }
00564                         break;
00565 
00566                     case eRLEStateNeedXDelta:
00567                         // Handle the XDelta and proceed to get Y Delta
00568                         byte = *aBuffer++;
00569                         aCount--;
00570                         mAlphaPtr += byte;
00571                         if (mAlphaPtr > mAlpha + mBIH.width)
00572                             mAlphaPtr = mAlpha + mBIH.width;
00573                         mDecoding += byte * GFXBYTESPERPIXEL;
00574 
00575                         mState = eRLEStateNeedYDelta;
00576                         continue;
00577 
00578                     case eRLEStateNeedYDelta:
00579                         // Get the Y Delta and then "handle" the move
00580                         byte = *aBuffer++;
00581                         aCount--;
00582                         mState = eRLEStateInitial;
00583                         if (byte == 0)
00584                             continue; // Nothing more to do
00585 
00586                         rv = WriteRLERows(PR_MIN(byte, mCurLine));
00587                         NS_ENSURE_SUCCESS(rv, rv);
00588                         break;
00589 
00590                     case eRLEStateAbsoluteMode: // Absolute Mode
00591                     case eRLEStateAbsoluteModePadded:
00592                         // In absolute mode, the second byte (mStateData)
00593                         // represents the number of pixels 
00594                         // that follow, each of which contains 
00595                         // the color index of a single pixel.
00596                         if (mBIH.compression == BI_RLE8) {
00597                             while (aCount > 0 && mStateData > 0) {
00598                                 byte = *aBuffer++;
00599                                 aCount--;
00600                                 SetPixel(mDecoding, byte, mColors);
00601                                 mStateData--;
00602                             }
00603                         } else {
00604                             while (aCount > 0 && mStateData > 0) {
00605                                 byte = *aBuffer++;
00606                                 aCount--;
00607                                 Set4BitPixel(mDecoding, byte, mStateData, mColors);
00608                             }
00609                         }
00610 
00611                         if (mStateData == 0) {
00612                             // In absolute mode, each run must 
00613                             // be aligned on a word boundary
00614 
00615                             if (mState == eRLEStateAbsoluteMode) { // Word Aligned
00616                                 mState = eRLEStateInitial;
00617                             } else if (aCount > 0) {               // Not word Aligned
00618                                 // "next" byte is just a padding byte
00619                                 // so "move" past it and we can continue
00620                                 aBuffer++;
00621                                 aCount--;
00622                                 mState = eRLEStateInitial;
00623                             }
00624                         }
00625                         // else state is still eRLEStateAbsoluteMode
00626                         continue;
00627 
00628                     default :
00629                         NS_NOTREACHED("BMP RLE decompression: unknown state!");
00630                         return NS_ERROR_FAILURE;
00631                 }
00632                 // Because of the use of the continue statement
00633                 // we only get here for eol, eof or y delta
00634                 if (mCurLine == 0) { // Finished last line
00635                     return mObserver->OnStopFrame(nsnull, mFrame);
00636                 }
00637             }
00638         }
00639     }
00640     
00641     return NS_OK;
00642 }
00643 
00644 void nsBMPDecoder::ProcessFileHeader()
00645 {
00646     memset(&mBFH, 0, sizeof(mBFH));
00647     memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature));
00648     memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize));
00649     memcpy(&mBFH.reserved, mRawBuf + 6, sizeof(mBFH.reserved));
00650     memcpy(&mBFH.dataoffset, mRawBuf + 10, sizeof(mBFH.dataoffset));
00651     memcpy(&mBFH.bihsize, mRawBuf + 14, sizeof(mBFH.bihsize));
00652 
00653     // Now correct the endianness of the header
00654     mBFH.filesize = LITTLE_TO_NATIVE32(mBFH.filesize);
00655     mBFH.dataoffset = LITTLE_TO_NATIVE32(mBFH.dataoffset);
00656     mBFH.bihsize = LITTLE_TO_NATIVE32(mBFH.bihsize);
00657 }
00658 
00659 void nsBMPDecoder::ProcessInfoHeader()
00660 {
00661     memset(&mBIH, 0, sizeof(mBIH));
00662     if (mBFH.bihsize == 12) { // OS/2 Bitmap
00663         memcpy(&mBIH.width, mRawBuf, 2);
00664         memcpy(&mBIH.height, mRawBuf + 2, 2);
00665         memcpy(&mBIH.planes, mRawBuf + 4, sizeof(mBIH.planes));
00666         memcpy(&mBIH.bpp, mRawBuf + 6, sizeof(mBIH.bpp));
00667     }
00668     else {
00669         memcpy(&mBIH.width, mRawBuf, sizeof(mBIH.width));
00670         memcpy(&mBIH.height, mRawBuf + 4, sizeof(mBIH.height));
00671         memcpy(&mBIH.planes, mRawBuf + 8, sizeof(mBIH.planes));
00672         memcpy(&mBIH.bpp, mRawBuf + 10, sizeof(mBIH.bpp));
00673         memcpy(&mBIH.compression, mRawBuf + 12, sizeof(mBIH.compression));
00674         memcpy(&mBIH.image_size, mRawBuf + 16, sizeof(mBIH.image_size));
00675         memcpy(&mBIH.xppm, mRawBuf + 20, sizeof(mBIH.xppm));
00676         memcpy(&mBIH.yppm, mRawBuf + 24, sizeof(mBIH.yppm));
00677         memcpy(&mBIH.colors, mRawBuf + 28, sizeof(mBIH.colors));
00678         memcpy(&mBIH.important_colors, mRawBuf + 32, sizeof(mBIH.important_colors));
00679     }
00680 
00681     // Convert endianness
00682     mBIH.width = LITTLE_TO_NATIVE32(mBIH.width);
00683     mBIH.height = LITTLE_TO_NATIVE32(mBIH.height);
00684     mBIH.planes = LITTLE_TO_NATIVE16(mBIH.planes);
00685     mBIH.bpp = LITTLE_TO_NATIVE16(mBIH.bpp);
00686 
00687     mBIH.compression = LITTLE_TO_NATIVE32(mBIH.compression);
00688     mBIH.image_size = LITTLE_TO_NATIVE32(mBIH.image_size);
00689     mBIH.xppm = LITTLE_TO_NATIVE32(mBIH.xppm);
00690     mBIH.yppm = LITTLE_TO_NATIVE32(mBIH.yppm);
00691     mBIH.colors = LITTLE_TO_NATIVE32(mBIH.colors);
00692     mBIH.important_colors = LITTLE_TO_NATIVE32(mBIH.important_colors);
00693 }