Bug 682688 - Fixed regression on transparency for 32BPP ICOs which don't use the alpha data byte. r=joe
authorBrian R. Bondy <netzen@gmail.com>
Fri, 09 Sep 2011 11:24:23 -0400
changeset 78149 72bace03b6ce24ddf4d8ae9b3051cfa76a35cc46
parent 78148 898b0317fc01d5c1849ce3326c7c4bb09cf4c89a
child 78150 984570d15e87a4f81a5c2c04af78bd4966582ca8
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs682688
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 682688 - Fixed regression on transparency for 32BPP ICOs which don't use the alpha data byte. r=joe
modules/libpr0n/decoders/nsBMPDecoder.cpp
modules/libpr0n/decoders/nsBMPDecoder.h
modules/libpr0n/decoders/nsICODecoder.cpp
--- a/modules/libpr0n/decoders/nsBMPDecoder.cpp
+++ b/modules/libpr0n/decoders/nsBMPDecoder.cpp
@@ -68,17 +68,17 @@ nsBMPDecoder::nsBMPDecoder()
     mColors = nsnull;
     mRow = nsnull;
     mImageData = nsnull;
     mCurPos = mPos = mNumColors = mRowBytes = 0;
     mOldLine = mCurLine = 1; // Otherwise decoder will never start
     mState = eRLEStateInitial;
     mStateData = 0;
     mLOH = WIN_HEADER_LENGTH;
-    mUseAlphaData = PR_FALSE;
+    mUseAlphaData = mHaveAlphaData = PR_FALSE;
 }
 
 nsBMPDecoder::~nsBMPDecoder()
 {
   delete[] mColors;
   if (mRow) {
       moz_free(mRow);
   }
@@ -137,16 +137,25 @@ nsBMPDecoder::GetCompressedImageSize() c
   }
 
   // The height should be the absolute value of what the height is in the BIH.
   // If positive the bitmap is stored bottom to top, otherwise top to bottom
   PRInt32 pixelArraySize = rowSize * abs(mBIH.height); 
   return pixelArraySize;
 }
 
+// Obtains whether or not a BMP file had alpha data in its 4th byte
+// for 32BPP bitmaps.  Only use after the bitmap has been processed.
+PRBool 
+nsBMPDecoder::HasAlphaData() const 
+{
+  return mHaveAlphaData;
+}
+
+
 void
 nsBMPDecoder::FinishInternal()
 {
     // We shouldn't be called in error cases
     NS_ABORT_IF_FALSE(!HasError(), "Can't call FinishInternal on error!");
 
     // We should never make multiple frames
     NS_ABORT_IF_FALSE(GetFrameCount() <= 1, "Multiple BMP frames?");
@@ -483,19 +492,31 @@ nsBMPDecoder::WriteInternal(const char* 
                           p += 2;
                           --lpos;
                           ++p;
                         }
                         break;
                       case 32:
                         while (lpos > 0) {
                           if (mUseAlphaData) {
-                            SetPixel(d, p[2], p[1], p[0], p[3]);
-                          }
-                          else {
+                            if (!mHaveAlphaData && p[3]) {
+                              // Non-zero alpha byte detected! Clear previous
+                              // pixels that we have already processed.
+                              // This works because we know that if we 
+                              // are reaching here then the alpha data in byte 
+                              // 4 has been right all along.  And we know it
+                              // has been set to 0 the whole time, so that 
+                              // means that everything is transparent so far.
+                              memset(mImageData + (mCurLine - 1) * GetWidth(), 0, 
+                                     (GetHeight() - mCurLine + 1) * 
+                                     GetWidth() * sizeof(PRUint32));
+                              mHaveAlphaData = PR_TRUE;
+                            }
+                            SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
+                          } else {
                             SetPixel(d, p[2], p[1], p[0]);
                           }
                           p += 4;
                           --lpos;
                         }
                         break;
                       default:
                         NS_NOTREACHED("Unsupported color depth, but earlier check didn't catch it");
--- a/modules/libpr0n/decoders/nsBMPDecoder.h
+++ b/modules/libpr0n/decoders/nsBMPDecoder.h
@@ -71,16 +71,19 @@ public:
     // Obtains the width from the internal BIH header
     PRInt32 GetWidth() const;
     // Obtains the height from the internal BIH header
     PRInt32 GetHeight() const;
     // Obtains the internal output image buffer
     PRUint32* GetImageData();
     // Obtains the size of the compressed image resource
     PRInt32 GetCompressedImageSize() const;
+    // Obtains whether or not a BMP file had alpha data in its 4th byte
+    // for 32BPP bitmaps.  Only use after the bitmap has been processed.
+    PRBool HasAlphaData() const;
 
     virtual void WriteInternal(const char* aBuffer, PRUint32 aCount);
     virtual void FinishInternal();
 
 private:
 
     /** Calculates the red-, green- and blueshift in mBitFields using
      * the bitmasks from mBitFields */
@@ -111,25 +114,27 @@ private:
 
     /** 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();
 
-    // Stores whether the image data stores alpha data, or if
-    // the alpha data is unspecified and filled with a 
-    // padding byte of 0.
+    // Stores whether the image data may store alpha data, or if
+    // the alpha data is unspecified and filled with a padding byte of 0. 
     // When a 32BPP bitmap is stored in an ICO or CUR file, its 4th byte
     // is used for alpha transparency.  When it is stored in a BMP, its
     // 4th byte is reserved and is always 0.
     // Reference: 
     // http://en.wikipedia.org/wiki/ICO_(file_format)#cite_note-9
+    // Bitmaps where the alpha bytes are all 0 should be fully visible.
     PRPackedBool mUseAlphaData;
+    // Whether the 4th byte alpha data was found to be non zero and hence used.
+    PRPackedBool mHaveAlphaData;
 };
 
 /** Sets the pixel data in aDecoded to the given values.
  * @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)
 {
     *aDecoded++ = GFX_PACKED_PIXEL(aAlpha, aRed, aGreen, aBlue);
--- a/modules/libpr0n/decoders/nsICODecoder.cpp
+++ b/modules/libpr0n/decoders/nsICODecoder.cpp
@@ -470,17 +470,18 @@ nsICODecoder::WriteInternal(const char* 
   
     // If the bitmap is fully processed, treat any left over data as the ICO's
     // 'AND buffer mask' which appears after the bitmap resource.
     if (!mIsPNG && mPos >= bmpDataEnd) {
       // There may be an optional AND bit mask after the data.  This is
       // only used if the alpha data is not already set. The alpha data 
       // is used for 32bpp bitmaps as per the comment in ICODecoder.h
       // The alpha mask should be checked in all other cases.
-      if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32) {
+      if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->GetBitsPerPixel() != 32 || 
+          !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->HasAlphaData()) {
         PRUint32 rowSize = ((mDirEntry.mWidth + 31) / 32) * 4; // + 31 to round up
         if (mPos == bmpDataEnd) {
           mPos++;
           mRowBytes = 0;
           mCurLine = mDirEntry.mHeight;
           mRow = (PRUint8*)moz_realloc(mRow, rowSize);
           if (!mRow) {
             PostDecoderError(NS_ERROR_OUT_OF_MEMORY);