Bug 334144 - Handling of alpha bits in nsBMPDecoder don't make sense in Cairo terms
authorasqueella@gmail.com
Tue, 17 Jul 2007 14:08:46 -0700
changeset 3580 eafa26c0cba4c6330b11e5bc8507dd6eadb1ae38
parent 3579 f80998fb2b3c6ffe6805e9553e323e26d351411d
child 3581 41151460e06b6ac8cdc1466515e9b128b82bf81d
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs334144
milestone1.9a7pre
Bug 334144 - Handling of alpha bits in nsBMPDecoder don't make sense in Cairo terms p=Alfred Kayser <alfredkayser@nl.ibm.com> r=pavlov/sr=tor
modules/libpr0n/build/Makefile.in
modules/libpr0n/decoders/bmp/Makefile.in
modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
modules/libpr0n/decoders/bmp/nsBMPDecoder.h
modules/libpr0n/decoders/bmp/nsICODecoder.cpp
modules/libpr0n/decoders/bmp/nsICODecoder.h
--- a/modules/libpr0n/build/Makefile.in
+++ b/modules/libpr0n/build/Makefile.in
@@ -49,16 +49,17 @@ IS_COMPONENT	= 1
 MODULE_NAME	= nsImageLib2Module
 GRE_MODULE	= 1
 LIBXUL_LIBRARY = 1
 
 PACKAGE_FILE = imglib2.pkg
 
 REQUIRES	= xpcom \
 		  string \
+		  thebes \
 		  necko \
 		  nkcache \
 		  gfx \
 		  $(JPEG_REQUIRES) \
 		  $(PNG_REQUIRES) \
 		  $(ZLIB_REQUIRES) \
 		  $(NULL)
 
--- a/modules/libpr0n/decoders/bmp/Makefile.in
+++ b/modules/libpr0n/decoders/bmp/Makefile.in
@@ -45,15 +45,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgbmp
 LIBRARY_NAME	= imgbmp_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= nsBMPModule
 LIBXUL_LIBRARY  = 1
 
 REQUIRES	= xpcom \
+		  string \
 		  gfx \
+		  thebes \
 		  imglib2 \
 		  $(NULL)
 
 CPPSRCS        = nsBMPDecoder.cpp nsICODecoder.cpp
 
 include $(topsrcdir)/config/rules.mk
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
@@ -42,47 +42,50 @@
 
 #include <stdlib.h>
 
 #include "nsBMPDecoder.h"
 
 #include "nsIInputStream.h"
 #include "nsIComponentManager.h"
 #include "imgIContainerObserver.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
 
 #include "imgILoad.h"
+#include "nsIImage.h"
 
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo *gBMPLog = PR_NewLogModule("BMPDecoder");
 #endif
 
+// Convert from row (1..height) to absolute line (0..height-1)
+#define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
+#define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
+
 NS_IMPL_ISUPPORTS1(nsBMPDecoder, imgIDecoder)
 
 nsBMPDecoder::nsBMPDecoder()
 {
     mColors = nsnull;
     mRow = nsnull;
-    mPos = mNumColors = mRowBytes = 0;
-    mCurLine = 1; // Otherwise decoder will never start
+    mCurPos = mPos = mNumColors = mRowBytes = 0;
+    mOldLine = mCurLine = 1; // Otherwise decoder will never start
     mState = eRLEStateInitial;
     mStateData = 0;
-    mAlpha = mAlphaPtr = mDecoded = mDecoding = nsnull;
     mLOH = WIN_HEADER_LENGTH;
 }
 
 nsBMPDecoder::~nsBMPDecoder()
 {
     delete[] mColors;
-    free(mRow);
-    if (mAlpha)
-        free(mAlpha);
-    if (mDecoded)
-        free(mDecoded);
+    if (mRow)
+        free(mRow);
 }
 
 NS_IMETHODIMP nsBMPDecoder::Init(imgILoad *aLoad)
 {
     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aLoad));
     mObserver = do_QueryInterface(aLoad);
 
     nsresult rv;
@@ -96,16 +99,17 @@ NS_IMETHODIMP nsBMPDecoder::Init(imgILoa
 
     return aLoad->SetImage(mImage);
 }
 
 NS_IMETHODIMP nsBMPDecoder::Close()
 {
     PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Close()\n"));
     if (mObserver) {
+        mObserver->OnStopFrame(nsnull, mFrame);
         mObserver->OnStopContainer(nsnull, mImage);
         mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
         mObserver = nsnull;
     }
     mImage = nsnull;
     mFrame = nsnull;
     return NS_OK;
 }
@@ -130,69 +134,16 @@ NS_IMETHODIMP nsBMPDecoder::WriteFrom(ns
 
     return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
 }
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
 
-nsresult nsBMPDecoder::SetData()
-{
-    PRInt32 line = (mBIH.height < 0) ? (-mBIH.height - mCurLine--) : --mCurLine;
-    nsresult rv = mFrame->SetImageData(mDecoded, mBpr, line * mBpr);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsIntRect r(0, line, mBIH.width, 1);
-    return mObserver->OnDataAvailable(nsnull, mFrame, &r);
-}
-
-nsresult nsBMPDecoder::WriteRLERows(PRUint32 rows)
-{
-    PRUint32 cnt, line;
-
-    PRUint32 abpr;
-    PRUint8 bit;
-    PRUint8* pos = mAlpha;
-    // First pack the alpha data
-    nsresult rv = mFrame->GetAlphaBytesPerRow(&abpr);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    gfx_format format;
-    mFrame->GetFormat(&format);
-    if (format == RLE_GFXFORMAT_ALPHA)
-        abpr >>= 2;
-
-    for (cnt = 0; cnt < abpr; cnt++) {
-        PRUint8 byte = 0;
-        for (bit = 128; bit; bit >>= 1)
-            byte |= *pos++ & bit;
-        mAlpha[cnt] = byte;
-#ifdef IS_LITTLE_ENDIAN
-        mDecoded[(cnt << 2) + 3] = byte ? 0 : 255;
-#else
-        mDecoded[(cnt << 2)] = byte ? 0 : 255;
-#endif
-    }
-
-    for (cnt = 0; cnt < rows; cnt++) {
-        line = (mBIH.height < 0) ? (-mBIH.height - mCurLine--) : --mCurLine;
-        rv = mFrame->SetImageData(mDecoded, mBpr, line * mBpr);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (cnt == 0) {
-            memset(mAlpha, 0, mBIH.width);
-            memset(mDecoded, 0, mBpr);
-        }
-    }
-
-    line = (mBIH.height < 0) ? (-mBIH.height - mCurLine - rows) : mCurLine;
-    nsIntRect r(0, line, mBIH.width, rows);
-    return mObserver->OnDataAvailable(nsnull, mFrame, &r);
-}
-
 static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
 {
     // find the rightmost 1
     PRUint8 pos;
     PRBool started = PR_FALSE;
     aBegin = aLength = 0;
     for (pos = 0; pos <= 31; pos++) {
         if (!started && (aMask & (1 << pos))) {
@@ -289,37 +240,53 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
         if (mBIH.width < 0 || mBIH.width > k64KWidth)
             return NS_ERROR_FAILURE;
 
         PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
         rv = mImage->Init(mBIH.width, real_height, mObserver);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = mObserver->OnStartContainer(nsnull, mImage);
         NS_ENSURE_SUCCESS(rv, rv);
-        mCurLine = real_height;
+        mOldLine = mCurLine = real_height;
 
-        mRow = (PRUint8*)malloc((mBIH.width * mBIH.bpp)/8 + 4);
-        // +4 because the line is padded to a 4 bit boundary, but I don't want
-        // to make exact calculations here, that's unnecessary.
-        // Also, it compensates rounding error.
-        if (!mRow) {
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
         if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
             rv = mFrame->Init(0, 0, mBIH.width, real_height, RLE_GFXFORMAT_ALPHA, 24);
         } else {
+            // mRow is not used for RLE encoded images
+            mRow = (PRUint8*)malloc((mBIH.width * mBIH.bpp)/8 + 4);
+            // +4 because the line is padded to a 4 bit boundary, but I don't want
+            // to make exact calculations here, that's unnecessary.
+            // Also, it compensates rounding error.
+            if (!mRow) {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
             rv = mFrame->Init(0, 0, mBIH.width, real_height, BMP_GFXFORMAT, 24);
         }
         NS_ENSURE_SUCCESS(rv, rv);
+
+        PRUint32 imageLength;
+        mFrame->GetImageData((PRUint8**)&mImageData, &imageLength);
+        if (!mImageData)
+            return NS_ERROR_FAILURE;
+
+        // Prepare for transparancy
+        if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
+            if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) 
+             || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
+                PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
+                return NS_ERROR_FAILURE;
+            }
+            // Clear the image, as the RLE may jump over areas
+            memset(mImageData, 0, imageLength);
+        }
+
         rv = mImage->AppendFrame(mFrame);
         NS_ENSURE_SUCCESS(rv, rv);
         mObserver->OnStartFrame(nsnull, mFrame);
         NS_ENSURE_SUCCESS(rv, rv);
-        rv = mFrame->GetImageBytesPerRow(&mBpr);
-        NS_ENSURE_SUCCESS(rv, rv);
     }
     PRUint8 bpc; // bytes per color
     bpc = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; // OS/2 Bitmaps have no padding byte
     if (mColors && (mPos >= mLOH && (mPos < (mLOH + mNumColors * bpc)))) {
         // We will receive (mNumColors * bpc) bytes of color data
         PRUint32 colorBytes = mPos - mLOH; // Number of bytes already received
         PRUint8 colorNum = colorBytes / bpc; // Color which is currently received
         PRUint8 at = colorBytes % bpc;
@@ -376,25 +343,20 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                 if (toCopy) {
                     if (toCopy > aCount)
                         toCopy = aCount;
                     memcpy(mRow + mRowBytes, aBuffer, toCopy);
                     aCount -= toCopy;
                     aBuffer += toCopy;
                     mRowBytes += toCopy;
                 }
-                if ((rowSize - mRowBytes) == 0) {
-                    if (!mDecoded) {
-                        mDecoded = (PRUint8*)malloc(mBpr);
-                        if (!mDecoded)
-                            return NS_ERROR_OUT_OF_MEMORY;
-                    }
-
+                if (rowSize == mRowBytes) {
+                    // Collected a whole row into mRow, process it
                     PRUint8* p = mRow;
-                    PRUint8* d = mDecoded;
+                    PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, 0);
                     PRUint32 lpos = mBIH.width;
                     switch (mBIH.bpp) {
                       case 1:
                         while (lpos > 0) {
                           PRInt8 bit;
                           PRUint8 idx;
                           for (bit = 7; bit >= 0 && lpos > 0; bit--) {
                               idx = (*p >> bit) & 1;
@@ -437,53 +399,32 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                           if (mBIH.bpp == 32)
                             p++; // Padding byte
                           ++p;
                         }
                         break;
                       default:
                         NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
                     }
-                      
-                    nsresult rv = SetData();
-                    NS_ENSURE_SUCCESS(rv, rv);
-
+                    mCurLine --;
                     if (mCurLine == 0) { // Finished last line
-                        return mObserver->OnStopFrame(nsnull, mFrame);
+                        break;
                     }
                     mRowBytes = 0;
 
                 }
             } while (aCount > 0);
         } 
         else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
             if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) 
              || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
                 PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
                 return NS_ERROR_FAILURE;
             }
 
-            if (!mAlpha) {
-                PRUint32 alpha;
-                rv = mFrame->GetAlphaBytesPerRow(&alpha);
-                NS_ENSURE_SUCCESS(rv, rv);
-                // Allocate an unpacked buffer
-                mAlpha = (PRUint8*)calloc(alpha, 8);
-                if (!mAlpha)
-                  return NS_ERROR_OUT_OF_MEMORY;
-                mAlphaPtr = mAlpha;
-            }
-
-            if (!mDecoded) {
-                mDecoded = (PRUint8*)calloc(mBpr, 1);
-                if (!mDecoded)
-                  return NS_ERROR_OUT_OF_MEMORY;
-                mDecoding = mDecoded;
-            }
-
             while (aCount > 0) {
                 PRUint8 byte;
 
                 switch(mState) {
                     case eRLEStateInitial:
                         mStateData = (PRUint8)*aBuffer++;
                         aCount--;
 
@@ -495,68 +436,61 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                         aCount--;
                         if (mStateData != RLE_ESCAPE) { // encoded mode
                             // Encoded mode consists of two bytes: 
                             // the first byte (mStateData) specifies the
                             // number of consecutive pixels to be drawn 
                             // using the color index contained in
                             // the second byte
                             // Work around bitmaps that specify too many pixels
-                            if (mAlphaPtr + mStateData > mAlpha + mBIH.width)
-                                mStateData = (PRUint32)(mAlpha + mBIH.width - mAlphaPtr);
-                            memset(mAlphaPtr, 0xFF, mStateData);
-                            mAlphaPtr += mStateData;
-                            if (mBIH.compression == BI_RLE8) {
-                                while (mStateData > 0) {
-                                    SetPixel(mDecoding, byte, mColors);
-                                    mStateData--;
-                                }
-                            } else {
-                                while (mStateData > 0) {
-                                    Set4BitPixel(mDecoding, byte, mStateData, mColors);
+                            mState = eRLEStateInitial;
+                            PRUint32 pixelsNeeded = PR_MIN((PRUint32)(mBIH.width - mCurPos), mStateData);
+                            if (pixelsNeeded) {
+                                PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                                mCurPos += pixelsNeeded;
+                                if (mBIH.compression == BI_RLE8) {
+                                    do {
+                                        SetPixel(d, byte, mColors);
+                                        pixelsNeeded --;
+                                    } while (pixelsNeeded);
+                                } else {
+                                    do {
+                                        Set4BitPixel(d, byte, pixelsNeeded, mColors);
+                                    } while (pixelsNeeded);
                                 }
                             }
-                            
-                            mState = eRLEStateInitial;
                             continue;
                         }
 
                         switch(byte) {
                             case RLE_ESCAPE_EOL:
-                                // End of Line: Write out current row
-                                // and reset our row buffer
-                                rv = WriteRLERows(1);
-                                NS_ENSURE_SUCCESS(rv, rv);
-                                mAlphaPtr = mAlpha;
-                                mDecoding = mDecoded;
-
+                                // End of Line: Go to next row
+                                mCurLine --;
+                                mCurPos = 0;
                                 mState = eRLEStateInitial;
                                 break;
 
                             case RLE_ESCAPE_EOF: // EndOfFile
-                                rv = WriteRLERows(mCurLine);
-                                NS_ENSURE_SUCCESS(rv, rv);
+                                mCurPos = mCurLine = 0;
                                 break;
 
                             case RLE_ESCAPE_DELTA:
                                 mState = eRLEStateNeedXDelta;
                                 continue;
 
                             default : // absolute mode
                                 // Save the number of pixels to read
                                 mStateData = byte;
-                                if (mAlphaPtr + mStateData > mAlpha + mBIH.width) {
+                                if (mCurPos + mStateData > (PRUint32)mBIH.width) {
                                     // We can work around bitmaps that specify one
                                     // pixel too many, but only if their width is odd.
                                     mStateData -= mBIH.width & 1;
-                                    if (mAlphaPtr + mStateData > mAlpha + mBIH.width)
+                                    if (mCurPos + mStateData > (PRUint32)mBIH.width)
                                         return NS_ERROR_FAILURE;
                                 }
-                                memset(mAlphaPtr, 0xFF, mStateData);
-                                mAlphaPtr += mStateData;
 
                                 // See if we will need to skip a byte
                                 // to word align the pixel data
                                 // mStateData is a number of pixels
                                 // so allow for the RLE compression type
                                 // Pixels RLE8=1 RLE4=2
                                 //    1    Pad    Pad
                                 //    2    No     Pad
@@ -569,55 +503,55 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                                 continue;
                         }
                         break;
 
                     case eRLEStateNeedXDelta:
                         // Handle the XDelta and proceed to get Y Delta
                         byte = *aBuffer++;
                         aCount--;
-                        mAlphaPtr += byte;
-                        if (mAlphaPtr > mAlpha + mBIH.width)
-                            mAlphaPtr = mAlpha + mBIH.width;
-                        mDecoding += byte * GFXBYTESPERPIXEL;
+                        mCurPos += byte;
+                        if (mCurPos > mBIH.width)
+                            mCurPos = mBIH.width;
 
                         mState = eRLEStateNeedYDelta;
                         continue;
 
                     case eRLEStateNeedYDelta:
                         // Get the Y Delta and then "handle" the move
                         byte = *aBuffer++;
                         aCount--;
                         mState = eRLEStateInitial;
-                        if (byte == 0)
-                            continue; // Nothing more to do
-
-                        rv = WriteRLERows(PR_MIN(byte, mCurLine));
-                        NS_ENSURE_SUCCESS(rv, rv);
+                        mCurLine -= PR_MIN(byte, mCurLine);
                         break;
 
                     case eRLEStateAbsoluteMode: // Absolute Mode
                     case eRLEStateAbsoluteModePadded:
-                        // In absolute mode, the second byte (mStateData)
-                        // represents the number of pixels 
-                        // that follow, each of which contains 
-                        // the color index of a single pixel.
-                        if (mBIH.compression == BI_RLE8) {
-                            while (aCount > 0 && mStateData > 0) {
-                                byte = *aBuffer++;
-                                aCount--;
-                                SetPixel(mDecoding, byte, mColors);
-                                mStateData--;
+                        if (mStateData) {
+                            // In absolute mode, the second byte (mStateData)
+                            // represents the number of pixels 
+                            // that follow, each of which contains 
+                            // the color index of a single pixel.
+                            PRUint32* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                            PRUint32* oldPos = d;
+                            if (mBIH.compression == BI_RLE8) {
+                                while (aCount > 0 && mStateData > 0) {
+                                    byte = *aBuffer++;
+                                    aCount--;
+                                    SetPixel(d, byte, mColors);
+                                    mStateData--;
+                                }
+                            } else {
+                                while (aCount > 0 && mStateData > 0) {
+                                    byte = *aBuffer++;
+                                    aCount--;
+                                    Set4BitPixel(d, byte, mStateData, mColors);
+                                }
                             }
-                        } else {
-                            while (aCount > 0 && mStateData > 0) {
-                                byte = *aBuffer++;
-                                aCount--;
-                                Set4BitPixel(mDecoding, byte, mStateData, mColors);
-                            }
+                            mCurPos += d - oldPos;
                         }
 
                         if (mStateData == 0) {
                             // In absolute mode, each run must 
                             // be aligned on a word boundary
 
                             if (mState == eRLEStateAbsoluteMode) { // Word Aligned
                                 mState = eRLEStateInitial;
@@ -634,22 +568,36 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
 
                     default :
                         NS_NOTREACHED("BMP RLE decompression: unknown state!");
                         return NS_ERROR_FAILURE;
                 }
                 // Because of the use of the continue statement
                 // we only get here for eol, eof or y delta
                 if (mCurLine == 0) { // Finished last line
-                    return mObserver->OnStopFrame(nsnull, mFrame);
+                    break;
                 }
             }
         }
     }
     
+    const PRUint32 rows = mOldLine - mCurLine;
+    if (rows) {
+        nsIntRect r(0, LINE(mCurLine), mBIH.width, rows);
+
+        // Tell the image that it's data has been updated
+        nsCOMPtr<nsIImage> img(do_GetInterface(mFrame));
+        if (!img)
+            return PR_FALSE;
+        img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
+
+        mObserver->OnDataAvailable(nsnull, mFrame, &r);
+        mOldLine = mCurLine;
+    }
+
     return NS_OK;
 }
 
 void nsBMPDecoder::ProcessFileHeader()
 {
     memset(&mBFH, 0, sizeof(mBFH));
     memcpy(&mBFH.signature, mRawBuf, sizeof(mBFH.signature));
     memcpy(&mBFH.filesize, mRawBuf + 2, sizeof(mBFH.filesize));
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
@@ -39,16 +39,17 @@
 #ifndef _nsBMPDecoder_h
 #define _nsBMPDecoder_h
 
 #include "nsCOMPtr.h"
 #include "imgIDecoder.h"
 #include "imgIContainer.h"
 #include "imgIDecoderObserver.h"
 #include "gfxIImageFrame.h"
+#include "gfxColor.h"
 
 #define NS_BMPDECODER_CID \
 { /* {78c61626-4d1f-4843-9364-4652d98ff6e1} */ \
   0x78c61626, \
   0x4d1f, \
   0x4843, \
   { 0x93, 0x64, 0x46, 0x52, 0xd9, 0x8f, 0xf6, 0xe1 } \
 }
@@ -164,26 +165,16 @@ private:
      * @param aBuffer Data to process.
      * @oaram count Number of bytes in mBuffer */
     NS_METHOD ProcessData(const char* aBuffer, PRUint32 aCount);
 
     /** Calculates the red-, green- and blueshift in mBitFields using
      * the bitmasks from mBitFields */
     NS_METHOD CalcBitShift();
 
-    /** Sets the image data. mCurLine is used to get the row,
-     * mDecoded is used to get the data */
-    nsresult SetData();
-
-    /** Sets the rle data. mCurLine is used to get the row,
-     * mDecoded is used to get the data for the first row,
-     * all subsequent rows are blank.
-     * @param rows Number of rows of data to set */
-    nsresult WriteRLERows(PRUint32 rows);
-
     nsCOMPtr<imgIDecoderObserver> mObserver;
 
     nsCOMPtr<imgIContainer> mImage;
     nsCOMPtr<gfxIImageFrame> mFrame;
 
     PRUint32 mPos;
 
     BMPFILEHEADER mBFH;
@@ -192,56 +183,54 @@ private:
 
     PRUint32 mLOH; ///< Length of the header
 
     PRUint32 mNumColors; ///< The number of used colors, i.e. the number of entries in mColors
     colorTable *mColors;
 
     bitFields mBitFields;
 
+    PRUint32 *mImageData; ///< Pointer to the image data for the frame
     PRUint8 *mRow;      ///< Holds one raw line of the image
     PRUint32 mRowBytes; ///< How many bytes of the row were already received
     PRInt32 mCurLine;   ///< Index of the line of the image that's currently being decoded
-    PRUint8 *mAlpha;    ///< Holds one line of unpacked alpha data
-    PRUint8 *mAlphaPtr; ///< Pointer within unpacked alpha data
-    PRUint8 *mDecoded;  ///< Holds one line of color image data
-    PRUint8 *mDecoding; ///< Pointer within image data
+    PRInt32 mOldLine;   ///< Previous index of the line 
+    PRInt32 mCurPos;    ///< Index in the current line of the image
 
     ERLEState mState;   ///< Maintains the current state of the RLE decoding
     PRUint32 mStateData;///< Decoding information that is needed depending on mState
 
-    PRUint32 mBpr;      ///< Cached image bytes per row
-
     /** Set mBFH from the raw data in mRawBuf, converting from little-endian
      * data to native data as necessary */
     void ProcessFileHeader();
     /** Set mBIH from the raw data in mRawBuf, converting from little-endian
      * data to native data as necessary */
     void ProcessInfoHeader();
 };
 
 /** Sets the pixel data in aDecoded to the given values.
- * The variable passed in as aDecoded will be moved on 3 or 4 bytes! */
-inline void SetPixel(PRUint8*& aDecoded, PRUint8 aRed, PRUint8 aGreen, PRUint8 aBlue, PRUint8 aAlpha = 0xFF)
+ * @param aDecoded pointer to pixel to be set, will be incremented to point to the next pixel.
+ */
+static inline void SetPixel(PRUint32*& aDecoded, PRUint8 aRed, PRUint8 aGreen, PRUint8 aBlue, PRUint8 aAlpha = 0xFF)
 {
-    *(PRUint32*)aDecoded = (aAlpha << 24) | (aRed << 16) | (aGreen << 8) | aBlue;
-    aDecoded += 4;
+    *aDecoded++ = GFX_PACKED_PIXEL(aAlpha, aRed, aGreen, aBlue);
 }
 
-inline void SetPixel(PRUint8*& aDecoded, PRUint8 idx, colorTable* aColors)
+static inline void SetPixel(PRUint32*& aDecoded, PRUint8 idx, colorTable* aColors)
 {
     SetPixel(aDecoded, aColors[idx].red, aColors[idx].green, aColors[idx].blue);
 }
 
 /** Sets two (or one if aCount = 1) pixels
- * @param aDecoded where the data is stored. Will be moved 3 or 6 bytes,
+ * @param aDecoded where the data is stored. Will be moved 4 resp 8 bytes
  * depending on whether one or two pixels are written.
  * @param aData The values for the two pixels
- * @param aCount Current count. Is decremented by one or two. */
-inline void Set4BitPixel(PRUint8*& aDecoded, PRUint8 aData,
+ * @param aCount Current count. Is decremented by one or two.
+ */
+inline void Set4BitPixel(PRUint32*& aDecoded, PRUint8 aData,
                          PRUint32& aCount, colorTable* aColors)
 {
     PRUint8 idx = aData >> 4;
     SetPixel(aDecoded, idx, aColors);
     if (--aCount > 0) {
         idx = aData & 0xF;
         SetPixel(aDecoded, idx, aColors);
         --aCount;
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
@@ -44,16 +44,19 @@
 
 #include "nsICODecoder.h"
 
 #include "nsIInputStream.h"
 #include "nsIComponentManager.h"
 #include "imgIContainerObserver.h"
 
 #include "imgILoad.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIImage.h"
 
 #include "nsIProperties.h"
 #include "nsISupportsPrimitives.h"
 
 #include "nsAutoPtr.h"
 
 NS_IMPL_ISUPPORTS1(nsICODecoder, imgIDecoder)
 
@@ -61,70 +64,30 @@ NS_IMPL_ISUPPORTS1(nsICODecoder, imgIDec
 #define DIRENTRYOFFSET 6
 #define BITMAPINFOSIZE 40
 #define PREFICONSIZE 16
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
 
-static PRUint32 premultiply(PRUint32 x)
-{
-    PRUint32 a = x >> 24;
-    PRUint32 t = (x & 0xff00ff) * a + 0x800080;
-    t = (t + ((t >> 8) & 0xff00ff)) >> 8;
-    t &= 0xff00ff;
-
-    x = ((x >> 8) & 0xff) * a + 0x80;
-    x = (x + ((x >> 8) & 0xff));
-    x &= 0xff00;
-    x |= t | (a << 24);
-    return x;
-}
-
-nsresult nsICODecoder::SetImageData()
-{
-  if (mHaveAlphaData) {
-    // We have premultiply the pixels when we have alpha transparency
-    PRUint32* p = (PRUint32*)mDecodedBuffer;
-    for (PRUint32 c = mDirEntry.mWidth * mDirEntry.mHeight; c > 0; --c) {
-      *p = premultiply(*p);
-      p++;
-    }
-  }
-  // In Cairo we can set the whole image in one go
-  PRUint32 dataLen = mDirEntry.mHeight * mDirEntry.mWidth * 4;
-  mFrame->SetImageData(mDecodedBuffer, dataLen, 0);
-
-  nsIntRect r(0, 0, 0, 0);
-  mFrame->GetWidth(&r.width);
-  mFrame->GetHeight(&r.height);
-  mObserver->OnDataAvailable(nsnull, mFrame, &r);
-
-  return NS_OK;
-}
-
 PRUint32 nsICODecoder::CalcAlphaRowSize()
 {
-  PRUint32 rowSize = (mDirEntry.mWidth + 7) / 8; // +7 to round up
-  if (rowSize % 4)
-    rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
-  return rowSize;
+  // Calculate rowsize in DWORD's and then return in # of bytes
+  PRUint32 rowSize = (mDirEntry.mWidth + 31) / 32; // +31 to round up
+  return rowSize * 4;        // Return rowSize in bytes
 }
 
 nsICODecoder::nsICODecoder()
 {
   mPos = mNumColors = mRowBytes = mImageOffset = mCurrIcon = mNumIcons = 0;
   mCurLine = 1; // Otherwise decoder will never start
-  mStatus = NS_OK;
   mColors = nsnull;
   mRow = nsnull;
-  mHaveAlphaData = 0;
-  mDecodingAndMask = PR_FALSE;
-  mDecodedBuffer = nsnull;
+  mHaveAlphaData = mDecodingAndMask = PR_FALSE;
 }
 
 nsICODecoder::~nsICODecoder()
 {
 }
 
 NS_IMETHODIMP nsICODecoder::Init(imgILoad *aLoad)
 { 
@@ -138,17 +101,26 @@ NS_IMETHODIMP nsICODecoder::Init(imgILoa
   if (!mFrame)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return aLoad->SetImage(mImage);
 }
 
 NS_IMETHODIMP nsICODecoder::Close()
 {
+  // Tell the image that it's data has been updated 
+  // This should be a mFrame function, so that we don't have to query for interface...
+  nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight);
+  nsCOMPtr<nsIImage> img(do_GetInterface(mFrame));
+  if (img)
+    img->ImageUpdated(nsnull, nsImageUpdateFlags_kBitsChanged, &r);
+
   if (mObserver) {
+    mObserver->OnDataAvailable(nsnull, mFrame, &r);
+    mObserver->OnStopFrame(nsnull, mFrame);
     mObserver->OnStopContainer(nsnull, mImage);
     mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
     mObserver = nsnull;
   }
 
   mImage = nsnull;
   mFrame = nsnull;
  
@@ -158,51 +130,42 @@ NS_IMETHODIMP nsICODecoder::Close()
   mColors = nsnull;
 
   mCurLine = 0;
   mRowBytes = 0;
   mImageOffset = 0;
   mCurrIcon = 0;
   mNumIcons = 0;
 
-  free(mRow);
-  mRow = nsnull;
-
+  if (mRow) {
+    free(mRow);
+    mRow = nsnull;
+  }
   mDecodingAndMask = PR_FALSE;
-  free(mDecodedBuffer);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsICODecoder::Flush()
 {
-  // Set Data here because some ICOs don't have a complete AND Mask
-  // see bug 115357
-  if (mDecodingAndMask) {
-    SetImageData();
-    mObserver->OnStopFrame(nsnull, mFrame);
-  }
   return NS_OK;
 }
 
 
 NS_METHOD nsICODecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
                              const char* aFromRawSegment, PRUint32 aToOffset,
                              PRUint32 aCount, PRUint32 *aWriteCount) {
   nsICODecoder *decoder = reinterpret_cast<nsICODecoder*>(aClosure);
   *aWriteCount = aCount;
-  decoder->mStatus = decoder->ProcessData(aFromRawSegment, aCount);
-  return decoder->mStatus;
+  return decoder->ProcessData(aFromRawSegment, aCount);
 }
 
 NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
 {
-  nsresult rv = aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return mStatus;
+  return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
 }
 
 nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
   if (!aCount) // aCount=0 means EOF
     return NS_OK;
 
   while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons.
     if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor
@@ -332,16 +295,19 @@ nsresult nsICODecoder::ProcessData(const
       return NS_ERROR_OUT_OF_MEMORY;
     
     rv = mFrame->Init(0, 0, mDirEntry.mWidth, mDirEntry.mHeight, GFXFORMATALPHA8, 24);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mImage->AppendFrame(mFrame);
     NS_ENSURE_SUCCESS(rv, rv);
     mObserver->OnStartFrame(nsnull, mFrame);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 imageLength;
+    mFrame->GetImageData((PRUint8**)&mImageData, &imageLength);
   }
 
   if (mColors && (mPos >= mImageOffset + BITMAPINFOSIZE) && 
                  (mPos < (mImageOffset + BITMAPINFOSIZE + mNumColors * 4))) {
     // We will receive (mNumColors * 4) bytes of color data
     PRUint32 colorBytes = mPos - (mImageOffset + 40); // Number of bytes already received
     PRUint8 colorNum = colorBytes / 4; // Color which is currently received
     PRUint8 at = colorBytes % 4;
@@ -364,26 +330,23 @@ nsresult nsICODecoder::ProcessData(const
       at = (at + 1) % 4;
     }
   }
 
   if (!mDecodingAndMask && (mPos >= (mImageOffset + BITMAPINFOSIZE + mNumColors*4))) {
     if (mPos == (mImageOffset + BITMAPINFOSIZE + mNumColors*4)) {
       // Increment mPos to avoid reprocessing the info header.
       mPos++;
-      mDecodedBuffer = (PRUint8*)malloc(mDirEntry.mHeight*mDirEntry.mWidth*4);
-      if (!mDecodedBuffer)
-        return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Ensure memory has been allocated before decoding. If we get this far 
     // without allocated memory, the file is most likely invalid.
     NS_ASSERTION(mRow, "mRow is null");
-    NS_ASSERTION(mDecodedBuffer, "mDecodedBuffer is null");
-    if (!mRow || !mDecodedBuffer)
+    NS_ASSERTION(mImageData, "mImageData is null");
+    if (!mRow || !mImageData)
       return NS_ERROR_FAILURE;
 
     PRUint32 rowSize = (mBIH.bpp * mDirEntry.mWidth + 7) / 8; // +7 to round up
     if (rowSize % 4)
       rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
     PRUint32 toCopy;
     do {
         toCopy = rowSize - mRowBytes;
@@ -392,17 +355,17 @@ nsresult nsICODecoder::ProcessData(const
                 toCopy = aCount;
             memcpy(mRow + mRowBytes, aBuffer, toCopy);
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
         }
         if (rowSize == mRowBytes) {
             mCurLine--;
-            PRUint8* d = mDecodedBuffer + (mCurLine * mDirEntry.mWidth * GFXBYTESPERPIXEL);
+            PRUint32* d = mImageData + (mCurLine * mDirEntry.mWidth);
             PRUint8* p = mRow;
             PRUint32 lpos = mDirEntry.mWidth;
             switch (mBIH.bpp) {
               case 1:
                 while (lpos > 0) {
                   PRInt8 bit;
                   PRUint8 idx;
                   for (bit = 7; bit >= 0 && lpos > 0; bit--) {
@@ -440,19 +403,28 @@ nsresult nsICODecoder::ProcessData(const
               case 24:
                 while (lpos > 0) {
                   SetPixel(d, p[2], p[1], p[0]);
                   p += 3;
                   --lpos;
                 }
                 break;
               case 32:
+                // We assume that 32bit doesn't have alpha data until we
+                // find a non-zero alpha byte. If we find such a byte, 
+                // it means that all previous pixels are really clear (alphabyte=0).
+                // This working assumption prevents us having to premultiply afterwards.
                 while (lpos > 0) {
-                  SetPixel(d, p[2], p[1], p[0], p[3]);
-                  mHaveAlphaData |= p[3]; // Alpha value
+                  if (!mHaveAlphaData && p[3]) {
+                    // Non-zero alpha byte detected! Clear previous pixels from current row to end
+                    memset(mImageData + mCurLine * mDirEntry.mWidth, 0, 
+                           (mDirEntry.mHeight - mCurLine) * mDirEntry.mWidth * sizeof(PRUint32));
+                    mHaveAlphaData = PR_TRUE;
+                  }                        
+                  SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
                   p += 4;
                   --lpos;
                 }
                 break;
               default:
                 // This is probably the wrong place to check this...
                 return NS_ERROR_FAILURE;
             }
@@ -468,66 +440,52 @@ nsresult nsICODecoder::ProcessData(const
 
   if (mDecodingAndMask && !mHaveAlphaData) {
     PRUint32 rowSize = CalcAlphaRowSize();
 
     if (mPos == (1 + mImageOffset + BITMAPINFOSIZE + mNumColors*4)) {
       mPos++;
       mRowBytes = 0;
       mCurLine = mDirEntry.mHeight;
-      free(mRow);
-      mRow = (PRUint8*)malloc(rowSize);
+      mRow = (PRUint8*)realloc(mRow, rowSize);
       if (!mRow)
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Ensure memory has been allocated before decoding.
     NS_ASSERTION(mRow, "mRow is null");
-    NS_ASSERTION(mDecodedBuffer, "mDecodedBuffer is null");
-    if (!mRow || !mDecodedBuffer)
+    NS_ASSERTION(mImageData, "mImageData is null");
+    if (!mRow || !mImageData)
       return NS_ERROR_FAILURE;
 
-    PRUint32 toCopy;
-    do {
-        if (mCurLine == 0) {
-          return NS_OK;
-        }
-
-        toCopy = rowSize - mRowBytes;
-        if (toCopy) {
-            if (toCopy > aCount)
-                toCopy = aCount;
-            memcpy(mRow + mRowBytes, aBuffer, toCopy);
-            aCount -= toCopy;
-            aBuffer += toCopy;
-            mRowBytes += toCopy;
-        }
-        if ((rowSize - mRowBytes) == 0) {
-            mCurLine--;
+    while (mCurLine > 0 && aCount > 0) {
+      PRUint32 toCopy = PR_MIN(rowSize - mRowBytes, aCount);
+      if (toCopy) {
+        memcpy(mRow + mRowBytes, aBuffer, toCopy);
+        aCount -= toCopy;
+        aBuffer += toCopy;
+        mRowBytes += toCopy;
+      }
+      if (rowSize == mRowBytes) {
+        mCurLine--;
+        mRowBytes = 0;
 
-            PRUint8* decoded =
-              mDecodedBuffer + (mCurLine * mDirEntry.mWidth * GFXBYTESPERPIXEL);
-#ifdef IS_LITTLE_ENDIAN
-            decoded += 3;
-#endif
-            PRUint8* decoded_end =
-              decoded + mDirEntry.mWidth * GFXBYTESPERPIXEL;
-            for (PRUint8* p = mRow, *p_end = mRow + rowSize; p < p_end; ++p) {
-              PRUint8 idx = *p;
-              for (PRUint8 bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
-                // We complement the value, since our method of storing
-                // transparency is opposite what Win32 uses in its masks.
-                *decoded = (idx & bit) ? 0x00 : 0xff;
-                decoded += GFXBYTESPERPIXEL;
-              }
-            }
-
-            mRowBytes = 0;
+        PRUint32* decoded = mImageData + mCurLine * mDirEntry.mWidth;
+        PRUint32* decoded_end = decoded + mDirEntry.mWidth;
+        PRUint8* p = mRow, *p_end = mRow + rowSize; 
+        while (p < p_end) {
+          PRUint8 idx = *p++;
+          for (PRUint8 bit = 0x80; bit && decoded<decoded_end; bit >>= 1) {
+            // Clear pixel completely for transparency.
+            if (idx & bit) *decoded = 0;
+            decoded ++;
+          }
         }
-    } while (aCount > 0);
+      }
+    }
   }
 
   return NS_OK;
 }
 
 void
 nsICODecoder::ProcessDirEntry(IconDirEntry& aTarget)
 {
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.h
@@ -118,19 +118,17 @@ private:
 
   PRUint32 mNumColors;
   colorTable* mColors;
 
   PRUint8* mRow; // Holds one raw line of the image
   PRUint32 mRowBytes; // How many bytes of the row were already received
   PRInt32 mCurLine;
 
-  nsresult mStatus;
+  PRUint32* mImageData;
 
-  PRUint8* mDecodedBuffer;
-
-  PRUint8 mHaveAlphaData;
+  PRPackedBool mHaveAlphaData;
   PRPackedBool mIsCursor;
   PRPackedBool mDecodingAndMask;
 };
 
 
 #endif