Bug 716140 - Make mImageData and mColormap (and their sizes) protected members of mozilla::image::Decoder rather than the leaf classes.
authorJoe Drew <joe@drew.ca>
Mon, 28 Jan 2013 12:26:36 -0500
changeset 125631 ef71ebfb90a03ab8921c5c2ec024129d545e25d5
parent 125630 7d0a6850c8875b94d2c2303a2266972242c1776e
child 125632 c86eb1ff89168e43b8668fc41f3e22f3c0eb5fbd
push id24461
push useremorley@mozilla.com
push dateThu, 21 Mar 2013 11:51:51 +0000
treeherdermozilla-central@a73a2b5c423b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs716140
milestone22.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 716140 - Make mImageData and mColormap (and their sizes) protected members of mozilla::image::Decoder rather than the leaf classes.
image/decoders/nsBMPDecoder.cpp
image/decoders/nsBMPDecoder.h
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
image/decoders/nsIconDecoder.cpp
image/decoders/nsIconDecoder.h
image/decoders/nsJPEGDecoder.h
image/decoders/nsPNGDecoder.h
image/decoders/nsWBMPDecoder.cpp
image/decoders/nsWBMPDecoder.h
image/src/Decoder.cpp
image/src/Decoder.h
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/imgStatusTracker.cpp
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -35,17 +35,16 @@ GetBMPLog()
 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
 
 nsBMPDecoder::nsBMPDecoder(RasterImage &aImage)
  : Decoder(aImage)
 {
   mColors = nullptr;
   mRow = nullptr;
-  mImageData = nullptr;
   mCurPos = mPos = mNumColors = mRowBytes = 0;
   mOldLine = mCurLine = 1; // Otherwise decoder will never start
   mState = eRLEStateInitial;
   mStateData = 0;
   mLOH = WIN_V3_HEADER_LENGTH;
   mUseAlphaData = mHaveAlphaData = false;
 }
 
@@ -84,17 +83,17 @@ nsBMPDecoder::GetHeight() const
 {
   return abs(mBIH.height);
 }
 
 // Obtains the internal output image buffer
 uint32_t* 
 nsBMPDecoder::GetImageData() 
 {
-  return mImageData;
+  return reinterpret_cast<uint32_t*>(mImageData);
 }
 
 // Obtains the size of the compressed image resource
 int32_t 
 nsBMPDecoder::GetCompressedImageSize() const
 {
   // For everything except BI_RGB the header field must be defined
   if (mBIH.compression != BI_RGB) {
@@ -424,17 +423,17 @@ nsBMPDecoder::WriteInternal(const char* 
                     memcpy(mRow + mRowBytes, aBuffer, toCopy);
                     aCount -= toCopy;
                     aBuffer += toCopy;
                     mRowBytes += toCopy;
                 }
                 if (rowSize == mRowBytes) {
                     // Collected a whole row into mRow, process it
                     uint8_t* p = mRow;
-                    uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, 0);
+                    uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, 0);
                     uint32_t lpos = mBIH.width;
                     switch (mBIH.bpp) {
                       case 1:
                         while (lpos > 0) {
                           int8_t bit;
                           uint8_t idx;
                           for (bit = 7; bit >= 0 && lpos > 0; bit--) {
                               idx = (*p >> bit) & 1;
@@ -482,17 +481,17 @@ nsBMPDecoder::WriteInternal(const char* 
                             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.
-                              uint32_t* start = mImageData + GetWidth() * (mCurLine - 1);
+                              uint32_t* start = reinterpret_cast<uint32_t*>(mImageData) + GetWidth() * (mCurLine - 1);
                               uint32_t heightDifference = GetHeight() - mCurLine + 1;
                               uint32_t pixelCount = GetWidth() * heightDifference;
 
                               memset(start, 0, pixelCount * sizeof(uint32_t));
 
                               mHaveAlphaData = true;
                             }
                             SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
@@ -542,17 +541,17 @@ nsBMPDecoder::WriteInternal(const char* 
                             // 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
                             mState = eRLEStateInitial;
                             uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos, mStateData);
                             if (pixelsNeeded) {
-                                uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                                uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
                                 mCurPos += pixelsNeeded;
                                 if (mBIH.compression == BI_RLE8) {
                                     do {
                                         SetPixel(d, byte, mColors);
                                         pixelsNeeded --;
                                     } while (pixelsNeeded);
                                 } else {
                                     do {
@@ -630,17 +629,17 @@ nsBMPDecoder::WriteInternal(const char* 
 
                     case eRLEStateAbsoluteMode: // Absolute Mode
                     case eRLEStateAbsoluteModePadded:
                         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.
-                            uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                            uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
                             uint32_t* oldPos = d;
                             if (mBIH.compression == BI_RLE8) {
                                 while (aCount > 0 && mStateData > 0) {
                                     byte = *aBuffer++;
                                     aCount--;
                                     SetPixel(d, byte, mColors);
                                     mStateData--;
                                 }
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -62,17 +62,16 @@ private:
 
     uint32_t mLOH; ///< Length of the header
 
     uint32_t mNumColors; ///< The number of used colors, i.e. the number of entries in mColors
     colorTable *mColors;
 
     bitFields mBitFields;
 
-    uint32_t *mImageData; ///< Pointer to the image data for the frame
     uint8_t *mRow;      ///< Holds one raw line of the image
     uint32_t mRowBytes; ///< How many bytes of the row were already received
     int32_t mCurLine;   ///< Index of the line of the image that's currently being decoded: [height,1]
     int32_t mOldLine;   ///< Previous index of the line 
     int32_t mCurPos;    ///< Index in the current line of the image
 
     ERLEState mState;   ///< Maintains the current state of the RLE decoding
     uint32_t mStateData;///< Decoding information that is needed depending on mState
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -71,18 +71,16 @@ namespace image {
 #define GETINT16(p)   ((p)[1]<<8|(p)[0])
 //////////////////////////////////////////////////////////////////////
 // GIF Decoder Implementation
 
 nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage)
   : Decoder(aImage)
   , mCurrentRow(-1)
   , mLastFlushedRow(-1)
-  , mImageData(nullptr)
-  , mColormap(nullptr)
   , mOldColor(0)
   , mCurrentFrame(-1)
   , mCurrentPass(0)
   , mLastFlushedPass(0)
   , mGIFOpen(false)
   , mSawTransparency(false)
 {
   // Clear out the structure, excluding the arrays
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -44,19 +44,16 @@ private:
   uint32_t  OutputRow();
   bool      DoLzw(const uint8_t *q);
 
   inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
 
   int32_t mCurrentRow;
   int32_t mLastFlushedRow;
 
-  uint8_t *mImageData;       // Pointer to image data in either Cairo or 8bit format
-  uint32_t *mColormap;       // Current colormap to be used in Cairo format
-  uint32_t mColormapSize;
   uint32_t mOldColor;        // The old value of the transparent pixel
 
   // The frame number of the currently-decoding frame when we're in the middle
   // of decoding it, and -1 otherwise.
   int32_t mCurrentFrame;
 
   uint8_t mCurrentPass;
   uint8_t mLastFlushedPass;
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -16,18 +16,16 @@
 namespace mozilla {
 namespace image {
 
 nsIconDecoder::nsIconDecoder(RasterImage &aImage)
  : Decoder(aImage),
    mWidth(-1),
    mHeight(-1),
    mPixBytesRead(0),
-   mPixBytesTotal(0),
-   mImageData(nullptr),
    mState(iconStateStart)
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
@@ -36,20 +34,16 @@ nsIconDecoder::WriteInternal(const char 
 {
   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
 
   // We put this here to avoid errors about crossing initialization with case
   // jumps on linux.
   uint32_t bytesToRead = 0;
   nsresult rv;
 
-  // Performance isn't critical here, so our update rectangle is 
-  // always the full icon
-  nsIntRect r(0, 0, mWidth, mHeight);
-
   // Loop until the input data is gone
   while (aCount > 0) {
     switch (mState) {
       case iconStateStart:
 
         // Grab the width
         mWidth = (uint8_t)*aBuffer;
 
@@ -76,54 +70,60 @@ nsIconDecoder::WriteInternal(const char 
         if (IsSizeDecode()) {
           mState = iconStateFinished;
           break;
         }
 
         // Add the frame and signal
         rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
                                 gfxASurface::ImageFormatARGB32,
-                                &mImageData, &mPixBytesTotal);
+                                &mImageData, &mImageDataLength);
         if (NS_FAILED(rv)) {
           PostDecoderError(rv);
           return;
         }
 
         // Tell the superclass we're starting a frame
         PostFrameStart();
 
         // Book Keeping
         aBuffer++;
         aCount--;
         mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels:
+      {
 
         // How many bytes are we reading?
-        bytesToRead = std::min(aCount, mPixBytesTotal - mPixBytesRead);
+        bytesToRead = std::min(aCount, mImageDataLength - mPixBytesRead);
 
         // Copy the bytes
         memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
 
+        // Performance isn't critical here, so our update rectangle is
+        // always the full icon
+        nsIntRect r(0, 0, mWidth, mHeight);
+
         // Invalidate
         PostInvalidation(r);
 
         // Book Keeping
         aBuffer += bytesToRead;
         aCount -= bytesToRead;
         mPixBytesRead += bytesToRead;
 
         // If we've got all the pixel bytes, we're finished
-        if (mPixBytesRead == mPixBytesTotal) {
+        if (mPixBytesRead == mImageDataLength) {
           PostFrameStop();
           PostDecodeDone();
           mState = iconStateFinished;
         }
         break;
+      }
 
       case iconStateFinished:
 
         // Consume all excess data silently
         aCount = 0;
 
         break;
     }
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -41,18 +41,16 @@ public:
   nsIconDecoder(RasterImage &aImage);
   virtual ~nsIconDecoder();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
 
   uint8_t mWidth;
   uint8_t mHeight;
   uint32_t mPixBytesRead;
-  uint32_t mPixBytesTotal;
-  uint8_t* mImageData;
   uint32_t mState;
 };
 
 enum {
   iconStateStart      = 0,
   iconStateHaveHeight = 1,
   iconStateReadPixels = 2,
   iconStateFinished   = 3
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -60,18 +60,16 @@ public:
 
   virtual Telemetry::ID SpeedHistogram();
   void NotifyDone();
 
 protected:
   void OutputScanlines(bool* suspend);
 
 public:
-  uint8_t *mImageData;
-
   struct jpeg_decompress_struct mInfo;
   struct jpeg_source_mgr mSourceMgr;
   decoder_error_mgr mErr;
   jstate mState;
 
   uint32_t mBytesToSkip;
 
   const JOCTET *mSegment;   // The current segment we are decoding from
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -67,17 +67,16 @@ public:
   }
 
 public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t *mCMSLine;
   uint8_t *interlacebuf;
-  uint8_t *mImageData;
   qcms_profile *mInProfile;
   qcms_transform *mTransform;
 
   gfxASurface::gfxImageFormat format;
 
   // For size decodes
   uint8_t *mHeaderBuf;
   uint32_t mHeaderBytesRead;
--- a/image/decoders/nsWBMPDecoder.cpp
+++ b/image/decoders/nsWBMPDecoder.cpp
@@ -65,17 +65,16 @@ static WbmpIntDecodeStatus DecodeEncoded
   // Out of data but in the middle of an encoded int.
   return IntParseInProgress;
 }
 
 nsWBMPDecoder::nsWBMPDecoder(RasterImage &aImage)
  : Decoder(aImage),
    mWidth(0),
    mHeight(0),
-   mImageData(nullptr),
    mRow(nullptr),
    mRowBytes(0),
    mCurLine(0),
    mState(WbmpStateStart)
 {
   // Nothing to do
 }
 
@@ -170,21 +169,20 @@ nsWBMPDecoder::WriteInternal(const char 
           }
 
           // If We're doing a size decode, we're done
           if (IsSizeDecode()) {
             mState = WbmpStateFinished;
             return;
           }
 
-          uint32_t imageLength;
           // Add the frame and signal
           nsresult rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
                                            gfxASurface::ImageFormatRGB24,
-                                           (uint8_t**)&mImageData, &imageLength);
+                                           (uint8_t**)&mImageData, &mImageDataLength);
 
           if (NS_FAILED(rv) || !mImageData) {
             PostDecoderError(NS_ERROR_FAILURE);
             mState = DecodingFailed;
             return;
           }
 
           // Create mRow, the buffer that holds one line of the raw image data
@@ -232,17 +230,17 @@ nsWBMPDecoder::WriteInternal(const char 
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
           }
 
           // If there is a filled buffered row of raw data, process the row.
           if (rowSize == mRowBytes) {
             uint8_t *p = mRow;
-            uint32_t *d = mImageData + (mWidth * mCurLine); // position of the first pixel at mCurLine
+            uint32_t *d = reinterpret_cast<uint32_t*>(mImageData) + (mWidth * mCurLine); // position of the first pixel at mCurLine
             uint32_t lpos = 0;
 
             while (lpos < mWidth) {
               for (int8_t bit = 7; bit >= 0; bit--) {
                 if (lpos >= mWidth)
                   break;
                 bool pixelWhite = (*p >> bit) & 1;
                 SetPixel(d, pixelWhite);
--- a/image/decoders/nsWBMPDecoder.h
+++ b/image/decoders/nsWBMPDecoder.h
@@ -44,18 +44,16 @@ public:
   virtual ~nsWBMPDecoder();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
 
 private:
   uint32_t mWidth;
   uint32_t mHeight;
 
-  uint32_t *mImageData;
-
   uint8_t* mRow;                    // Holds one raw line of the image
   uint32_t mRowBytes;               // How many bytes of the row were already received
   uint32_t mCurLine;                // The current line being decoded (0 to mHeight - 1)
 
   WbmpDecodingState mState;         // Describes what part of the file we are decoding now.
 };
 
 } // namespace image
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -10,16 +10,18 @@
 #include "nsIScriptError.h"
 #include "sampler.h"
 
 namespace mozilla {
 namespace image {
 
 Decoder::Decoder(RasterImage &aImage)
   : mImage(aImage)
+  , mImageData(nullptr)
+  , mColormap(nullptr)
   , mDecodeFlags(0)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mInitialized(false)
   , mSizeDecode(false)
   , mInFrame(false)
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -187,16 +187,21 @@ protected:
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
   RefPtr<imgDecoderObserver> mObserver;
   ImageMetadata mImageMetadata;
 
+  uint8_t* mImageData;       // Pointer to image data in either Cairo or 8bit format
+  uint32_t mImageDataLength;
+  uint32_t* mColormap;       // Current colormap to be used in Cairo format
+  uint32_t mColormapSize;
+
   uint32_t mDecodeFlags;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1843,17 +1843,17 @@ RasterImage::DoImageDataComplete()
 
   // This call should come straight from necko - no reentrancy allowed
   NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
 
   // If we're not storing any source data, then all the data was written
   // directly to the decoder in the AddSourceData() calls. This means we're
   // done, so we can shut down the decoder.
   if (!StoringSourceData()) {
-    RasterImage::FinishedSomeDecoding(this);
+    FinishedSomeDecoding();
   }
 
   // If there's a decoder open, synchronously decode the beginning of the image
   // to check for errors and get the image's size.  (If we already have the
   // image's size, this does nothing.)  Then kick off an async decode of the
   // rest of the image.
   if (mDecoder) {
     nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
@@ -2821,17 +2821,17 @@ RasterImage::RequestDecodeCore(RequestDe
   }
 
 
   // If we have a size decode open, interrupt it and shut it down; or if
   // the decoder has different flags than what we need
   if (mDecoder &&
       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
   {
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
   }
 
   // If we don't have a decoder, create one 
   if (!mDecoder) {
     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
     rv = InitDecoder(/* aDoSizeDecode = */ false);
 
     CONTAINER_ENSURE_SUCCESS(rv);
@@ -2880,17 +2880,17 @@ RasterImage::SyncDecode()
   // disallow this type of call in the API, and check for it in API methods.
   NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
 
   // If we have a size decoder open, or one with different flags than
   // what we need, shut it down
   if (mDecoder &&
       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
   {
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
   }
 
   // If we don't have a decoder, create one 
   if (!mDecoder) {
     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
@@ -2911,17 +2911,17 @@ RasterImage::SyncDecode()
 
   // If we finished the decode, shutdown the decoder
   if (mDecoder && IsDecodeFinished()) {
     // We have to shut down the decoder *now*, so we explicitly shut down the
     // decoder, and let FinishedSomeDecoding handle the rest for us.
     nsRefPtr<DecodeRequest> request = mDecodeRequest;
     nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
     CONTAINER_ENSURE_SUCCESS(rv);
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_Done, request);
+    FinishedSomeDecoding(eShutdownIntent_Done, request);
   }
 
   // All good if no errors!
   return mError ? NS_ERROR_FAILURE : NS_OK;
 }
 
 static inline bool
 IsDownscale(const gfxSize& scale)
@@ -3200,17 +3200,17 @@ RasterImage::UnlockImage()
   // and our lock count is now zero (so nothing is forcing us to keep the
   // decoded data around), try to cancel the decode and throw away whatever
   // we've decoded.
   if (mHasBeenDecoded && mDecoder &&
       mLockCount == 0 && CanForciblyDiscard()) {
     PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
            ("RasterImage[0x%p] canceling decode because image "
             "is now unlocked.", this));
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
     ForceDiscard();
     return NS_OK;
   }
 
   // Otherwise, we might still be a candidate for discarding in the future.  If
   // we are, add ourselves to the discard tracker.
   if (CanDiscard()) {
     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
@@ -3298,17 +3298,17 @@ void
 RasterImage::DoError()
 {
   // If we've flagged an error before, we have nothing to do
   if (mError)
     return;
 
   // If we're mid-decode, shut down the decoder.
   if (mDecoder) {
-    FinishedSomeDecoding(this, eShutdownIntent_Error);
+    FinishedSomeDecoding(eShutdownIntent_Error);
   }
 
   // Put the container in an error state
   mError = true;
 
   // Log our error
   LOG_CONTAINER_ERROR;
 }
@@ -3358,42 +3358,42 @@ RasterImage::GetFramesNotified(uint32_t 
   NS_ENSURE_ARG_POINTER(aFramesNotified);
 
   *aFramesNotified = mFramesNotified;
 
   return NS_OK;
 }
 #endif
 
-/* static */ void
-RasterImage::FinishedSomeDecoding(RasterImage* aImage,
-                                  eShutdownIntent aIntent /* = eShutdownIntent_Done */,
+void
+RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
                                   DecodeRequest* aRequest /* = nullptr */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsRefPtr<DecodeRequest> request;
   if (aRequest) {
     request = aRequest;
   } else {
-    request = aImage->mDecodeRequest;
+    request = mDecodeRequest;
   }
 
   // Ensure that, if the decoder is the last reference to the image, we don't
   // destroy it by destroying the decoder.
-  nsRefPtr<RasterImage> image = aImage;
+  nsRefPtr<RasterImage> image(this);
 
   bool done = false;
 
   if (image->mDecoder) {
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
-    // If the decode finished, tell the image and shut down the decoder.
+    // If the decode finished, or we're specifically being told to shut down,
+    // tell the image and shut down the decoder.
     if (image->mDecoder->GetDecodeDone() || image->IsDecodeFinished() ||
         aIntent != eShutdownIntent_Done) {
       done = true;
 
       // Hold on to a reference to the decoder until we're done with it
       nsRefPtr<Decoder> decoder = image->mDecoder;
 
       // We need to shut down the decoder first, in order to ensure all
@@ -3415,27 +3415,27 @@ RasterImage::FinishedSomeDecoding(Raster
           int32_t KBps = int32_t(request->mImage->mBytesDecoded /
                                  (1024 * request->mDecodeTime.ToSeconds()));
           Telemetry::Accumulate(id, KBps);
         }
       }
     }
   }
 
-  // If it have any frames, tell the image what happened to the most recent
+  // If it has any frames, tell the image what happened to the most recent
   // one. This will be better when frames are published to decoders instead of
   // decoders requesting them.
   if (image->GetNumFrames()) {
     nsIntRect rect;
     if (request) {
       rect = request->mStatusTracker->GetInvalidRect();
     } else {
       rect = image->CurrentStatusTracker().GetInvalidRect();
     }
-    aImage->FrameUpdated(image->GetNumFrames() - 1, rect);
+    image->FrameUpdated(image->GetNumFrames() - 1, rect);
   }
 
   // Then, tell the observers what happened in the decoder.
   // If we have no request, we have not yet created a decoder, but we still
   // need to send out notifications.
   if (request) {
     image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker);
   } else {
@@ -3456,24 +3456,24 @@ RasterImage::DecodeWorker::Singleton()
 
 RasterImage::DecodeWorker::~DecodeWorker()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!");
 
   // Shut down all the decoders since we're going away.
   DecodeRequest* request = mASAPDecodeRequests.getFirst();
   while (request) {
-    RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
+    request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
 
     request = request->getNext();
   }
 
   request = mNormalDecodeRequests.getFirst();
   while (request) {
-    RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
+    request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
 
     request = request->getNext();
   }
 }
 
 void
 RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
 {
@@ -3539,17 +3539,17 @@ RasterImage::DecodeWorker::RequestDecode
 }
 
 void
 RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   DecodeSomeOfImage(aImg);
-  RasterImage::FinishedSomeDecoding(aImg);
+  aImg->FinishedSomeDecoding();
 
   // If we aren't yet finished decoding and we have more data in hand, add
   // this request to the back of the priority list.
   if (aImg->mDecoder &&
       !aImg->mError &&
       !aImg->IsDecodeFinished() &&
       aImg->mSourceData.Length() > aImg->mBytesDecoded) {
     RequestDecode(aImg);
@@ -3612,21 +3612,21 @@ RasterImage::DecodeWorker::Run()
     if (image->mDecoder &&
         !image->mError &&
         !image->IsDecodeFinished() &&
         bytesDecoded < request->mBytesToDecode) {
       AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded);
 
       // If we have a new frame, let everybody know about it.
       if (image->mDecoder->GetFrameCount() != oldCount) {
-        DecodeDoneWorker::DidSomeDecoding(image, request);
+        DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
       }
     } else {
       // Nothing more for us to do - let everyone know what happened.
-      DecodeDoneWorker::DidSomeDecoding(image, request);
+      DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
     }
 
   } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
 
   // If decode requests are pending, re-post ourself to the event loop.
   if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
     EnsurePendingInEventLoop();
   }
@@ -3639,17 +3639,17 @@ RasterImage::DecodeWorker::Run()
 
 nsresult
 RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
 
-  RasterImage::FinishedSomeDecoding(aImg);
+  aImg->FinishedSomeDecoding();
 
   return rv;
 }
 
 nsresult
 RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
                                              DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
                                              uint32_t bytesToDecode /* = 0 */)
@@ -3748,26 +3748,26 @@ RasterImage::DecodeWorker::DecodeSomeOfI
 }
 
 RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request)
  : mImage(image)
  , mRequest(request)
 {}
 
 void
-RasterImage::DecodeDoneWorker::DidSomeDecoding(RasterImage* image, DecodeRequest* request)
+RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
 {
   nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
   NS_DispatchToMainThread(worker);
 }
 
 NS_IMETHODIMP
 RasterImage::DecodeDoneWorker::Run()
 {
-  RasterImage::FinishedSomeDecoding(mImage, eShutdownIntent_Done, mRequest);
+  mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
 
   // If we didn't finish decoding yet, try again
   if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
       mImage->mSourceData.Length() > mImage->mBytesDecoded) {
     DecodeWorker::Singleton()->RequestDecode(mImage);
   }
 
   return NS_OK;
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -528,32 +528,31 @@ private:
   public:
     /**
      * Called by the DecodeWorker with an image when it's done some significant
      * portion of decoding that needs to be notified about.
      *
      * Ensures the decode state accumulated by the decoding process gets
      * applied to the image.
      */
-    static void DidSomeDecoding(RasterImage* image, DecodeRequest* request);
+    static void NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request);
 
     NS_IMETHOD Run();
 
   private: /* methods */
     DecodeDoneWorker(RasterImage* image, DecodeRequest* request);
 
   private: /* members */
 
     nsRefPtr<RasterImage> mImage;
     nsRefPtr<DecodeRequest> mRequest;
   };
 
-  static void FinishedSomeDecoding(RasterImage* image,
-                                   eShutdownIntent intent = eShutdownIntent_Done,
-                                   DecodeRequest* request = nullptr);
+  void FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
+                            DecodeRequest* request = nullptr);
 
   void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     gfxPattern::GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage);
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -521,24 +521,22 @@ imgStatusTracker::SyncAndSyncNotifyDiffe
   mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
   mState |= diffState | loadState;
   if (unblockedOnload) {
     mState &= ~stateBlockingOnload;
   }
   mImageStatus = other->mImageStatus;
   mIsMultipart = other->mIsMultipart;
   mHadLastPart = other->mHadLastPart;
+  mImageStatus |= other->mImageStatus;
 
   // The error state is sticky and overrides all other bits.
-  if (mImageStatus == imgIRequest::STATUS_ERROR ||
-      other->mImageStatus == imgIRequest::STATUS_ERROR) {
+  if (mImageStatus & imgIRequest::STATUS_ERROR) {
     mImageStatus = imgIRequest::STATUS_ERROR;
   } else {
-    mImageStatus |= other->mImageStatus;
-
     // Unset the bits that can get unset as part of the decoding process.
     if (!(other->mImageStatus & imgIRequest::STATUS_DECODE_STARTED)) {
       mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
     }
   }
 
   SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart);