Bug 716140 - Set metadata directly on frames. r=seth
authorJoe Drew <joe@drew.ca>
Wed, 27 Feb 2013 14:23:08 -0500
changeset 125639 e0683dc77a1b9e798c46b62904e8de8bc096949d
parent 125638 e47cbcb88803cd45add86dc3228f45a2e43a0032
child 125640 0a2fc100f05ffc7e8b6800c3c4aa69a52906d9c7
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)
reviewersseth
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 - Set metadata directly on frames. r=seth
image/decoders/nsICODecoder.cpp
image/src/Decoder.cpp
image/src/Decoder.h
image/src/RasterImage.cpp
image/src/RasterImage.h
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -311,17 +311,18 @@ nsICODecoder::WriteInternal(const char* 
 
     mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, 
                      PNGSIGNATURESIZE);
     if (mIsPNG) {
       mContainedDecoder = new nsPNGDecoder(mImage);
       mContainedDecoder->SetObserver(mObserver);
       mContainedDecoder->SetSizeDecode(IsSizeDecode());
       mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
-                                           mColormap, mColormapSize);
+                                           mColormap, mColormapSize,
+                                           mCurrentFrame);
       if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE)) {
         return;
       }
     }
   }
 
   // If we have a PNG, let the PNG decoder do all of the rest of the work
   if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) {
@@ -381,17 +382,18 @@ nsICODecoder::WriteInternal(const char* 
     // It will do everything except the AND mask which isn't present in bitmaps
     // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder
     nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage);
     mContainedDecoder = bmpDecoder;
     bmpDecoder->SetUseAlphaData(true);
     mContainedDecoder->SetObserver(mObserver);
     mContainedDecoder->SetSizeDecode(IsSizeDecode());
     mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
-                                         mColormap, mColormapSize);
+                                         mColormap, mColormapSize,
+                                         mCurrentFrame);
 
     // The ICO format when containing a BMP does not include the 14 byte
     // bitmap file header. To use the code of the BMP decoder we need to 
     // generate this header ourselves and feed it to the BMP decoder.
     int8_t bfhBuffer[BMPFILEHEADERSIZE];
     if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
       PostDataError();
       return;
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -10,16 +10,17 @@
 #include "nsIScriptError.h"
 #include "sampler.h"
 
 namespace mozilla {
 namespace image {
 
 Decoder::Decoder(RasterImage &aImage)
   : mImage(aImage)
+  , mCurrentFrame(nullptr)
   , mImageData(nullptr)
   , mColormap(nullptr)
   , mDecodeFlags(0)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mNeedsNewFrame(false)
@@ -55,26 +56,28 @@ Decoder::Init()
 
   mInitialized = true;
 }
 
 // Initializes a decoder whose image and observer is already being used by a
 // parent decoder
 void
 Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
-                           uint32_t* colormap, uint32_t colormapSize)
+                           uint32_t* colormap, uint32_t colormapSize,
+                           imgFrame* currentFrame)
 {
   // No re-initializing
   NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
   NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
 
   mImageData = imageData;
   mImageDataLength = imageDataLength;
   mColormap = colormap;
   mColormapSize = colormapSize;
+  mCurrentFrame = currentFrame;
 
   // Implementation-specific initialization
   InitInternal();
   mInitialized = true;
 }
 
 void
 Decoder::Write(const char* aBuffer, uint32_t aCount)
@@ -195,22 +198,22 @@ Decoder::AllocateFrame()
 
   nsresult rv;
   if (mNewFrameData.mPaletteDepth) {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
                             mNewFrameData.mPaletteDepth,
                             &mImageData, &mImageDataLength,
-                            &mColormap, &mColormapSize);
+                            &mColormap, &mColormapSize, &mCurrentFrame);
   } else {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
-                            &mImageData, &mImageDataLength);
+                            &mImageData, &mImageDataLength, &mCurrentFrame);
   }
 
   if (NS_SUCCEEDED(rv)) {
     PostFrameStart();
   }
 
   // Mark ourselves as not needing another frame before talking to anyone else
   // so they can tell us if they need yet another.
@@ -308,49 +311,54 @@ Decoder::PostFrameStart()
 void
 Decoder::PostFrameStop(RasterImage::FrameAlpha aFrameAlpha /* = RasterImage::kFrameHasAlpha */,
                        RasterImage::FrameDisposalMethod aDisposalMethod /* = RasterImage::kDisposeKeep */,
                        int32_t aTimeout /* = 0 */,
                        RasterImage::FrameBlendMethod aBlendMethod /* = RasterImage::kBlendOver */)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
+  NS_ABORT_IF_FALSE(mCurrentFrame, "Stopping frame when we don't have one!");
 
   // Update our state
   mInFrame = false;
 
   if (aFrameAlpha == RasterImage::kFrameOpaque) {
-    mImage.SetFrameHasNoAlpha(mFrameCount - 1);
+    mCurrentFrame->SetHasNoAlpha();
   }
 
-  mImage.SetFrameDisposalMethod(mFrameCount - 1, aDisposalMethod);
-  mImage.SetFrameTimeout(mFrameCount - 1, aTimeout);
-  mImage.SetFrameBlendMethod(mFrameCount - 1, aBlendMethod);
+  mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
+  mCurrentFrame->SetTimeout(aTimeout);
+  mCurrentFrame->SetBlendMethod(aBlendMethod);
 
   // Flush any invalidations before we finish the frame
   FlushInvalidations();
 
+  mCurrentFrame = nullptr;
+
   // Fire notifications
   if (mObserver) {
     mObserver->OnStopFrame();
     if (mFrameCount > 1 && !mIsAnimated) {
       mIsAnimated = true;
       mObserver->OnImageIsAnimated();
     }
   }
 }
 
 void
 Decoder::PostInvalidation(nsIntRect& aRect)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
+  NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
 
   // Account for the new region
   mInvalidRect.UnionRect(mInvalidRect, aRect);
+  mCurrentFrame->ImageUpdated(aRect);
 }
 
 void
 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
   NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -30,17 +30,18 @@ public:
 
   /**
    * Initializes a decoder whose image and observer is already being used by a
    * parent decoder. Decoders may not be re-initialized.
    *
    * Notifications Sent: TODO
    */
   void InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
-                         uint32_t* colormap, uint32_t colormapSize);
+                         uint32_t* colormap, uint32_t colormapSize,
+                         imgFrame* currentFrame);
 
   /**
    * Writes data to the decoder.
    *
    * @param aBuffer buffer containing the data to be written
    * @param aCount the number of bytes to write
    *
    * Any errors are reported by setting the appropriate state on the decoder.
@@ -201,16 +202,17 @@ protected:
   // status code from that attempt. Clears mNewFrameData.
   nsresult AllocateFrame();
 
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
+  imgFrame* mCurrentFrame;
   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;
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1248,48 +1248,52 @@ RasterImage::DeleteImgFrame(uint32_t fra
 
   delete mFrames[framenum];
   mFrames[framenum] = nullptr;
 }
 
 nsresult
 RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
                                     uint8_t **imageData, uint32_t *imageLength,
-                                    uint32_t **paletteData, uint32_t *paletteLength)
+                                    uint32_t **paletteData, uint32_t *paletteLength,
+                                    imgFrame** aRetFrame)
 {
   NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
   if (framenum > mFrames.Length())
     return NS_ERROR_INVALID_ARG;
 
   nsAutoPtr<imgFrame> frame(aFrame);
 
   // We are in the middle of decoding. This will be unlocked when we finish the
   // decoder->Write() call.
   frame->LockImageData();
 
   if (paletteData && paletteLength)
     frame->GetPaletteData(paletteData, paletteLength);
 
   frame->GetImageData(imageData, imageLength);
 
+  *aRetFrame = frame;
+
   mFrames.InsertElementAt(framenum, frame.forget());
 
   return NS_OK;
 }
-                                  
+
 nsresult
 RasterImage::InternalAddFrame(uint32_t framenum,
                               int32_t aX, int32_t aY,
                               int32_t aWidth, int32_t aHeight,
                               gfxASurface::gfxImageFormat aFormat,
                               uint8_t aPaletteDepth,
                               uint8_t **imageData,
                               uint32_t *imageLength,
                               uint32_t **paletteData,
-                              uint32_t *paletteLength)
+                              uint32_t *paletteLength,
+                              imgFrame** aRetFrame)
 {
   // We assume that we're in the middle of decoding because we unlock the
   // previous frame when we create a new frame, and only when decoding do we
   // lock frames.
   NS_ABORT_IF_FALSE(mInDecoder, "Only decoders may add frames!");
 
   NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Invalid frame index!");
   if (framenum > mFrames.Length())
@@ -1304,17 +1308,17 @@ RasterImage::InternalAddFrame(uint32_t f
   // when we move on to decoding into the next frame.
   if (mFrames.Length() > 0) {
     imgFrame *prevframe = mFrames.ElementAt(mFrames.Length() - 1);
     prevframe->UnlockImageData();
   }
 
   if (mFrames.Length() == 0) {
     return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 
-                                  paletteData, paletteLength);
+                                  paletteData, paletteLength, aRetFrame);
   }
 
   if (mFrames.Length() == 1) {
     // Since we're about to add our second frame, initialize animation stuff
     EnsureAnimExists();
     
     // If we dispose of the first frame by clearing it, then the
     // First Frame's refresh area is all of itself.
@@ -1328,17 +1332,17 @@ RasterImage::InternalAddFrame(uint32_t f
   // Calculate firstFrameRefreshArea
   // Some gifs are huge but only have a small area that they animate
   // We only need to refresh that small area when Frame 0 comes around again
   nsIntRect frameRect = frame->GetRect();
   mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea, 
                                          frameRect);
   
   rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
-                              paletteData, paletteLength);
+                              paletteData, paletteLength, aRetFrame);
   
   // We may be able to start animating, if we now have enough frames
   EvaluateAnimation();
   
   return rv;
 }
 
 bool
@@ -1395,55 +1399,59 @@ RasterImage::SetSize(int32_t aWidth, int
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
                          gfxASurface::gfxImageFormat aFormat,
                          uint8_t aPaletteDepth,
                          uint8_t **imageData, uint32_t *imageLength,
-                         uint32_t **paletteData, uint32_t *paletteLength)
+                         uint32_t **paletteData, uint32_t *paletteLength,
+                         imgFrame** aRetFrame)
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(imageData);
   NS_ENSURE_ARG_POINTER(imageLength);
+  NS_ENSURE_ARG_POINTER(aRetFrame);
   NS_ABORT_IF_FALSE(aFrameNum <= mFrames.Length(), "Invalid frame index!");
 
   if (aPaletteDepth > 0) {
     NS_ENSURE_ARG_POINTER(paletteData);
     NS_ENSURE_ARG_POINTER(paletteLength);
   }
 
   if (aFrameNum > mFrames.Length())
     return NS_ERROR_INVALID_ARG;
 
   // Adding a frame that doesn't already exist.
   if (aFrameNum == mFrames.Length())
     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
                             aPaletteDepth, imageData, imageLength,
-                            paletteData, paletteLength);
+                            paletteData, paletteLength, aRetFrame);
 
   imgFrame *frame = GetImgFrame(aFrameNum);
   if (!frame)
     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat, 
                             aPaletteDepth, imageData, imageLength,
-                            paletteData, paletteLength);
+                            paletteData, paletteLength, aRetFrame);
 
   // See if we can re-use the frame that already exists.
   nsIntRect rect = frame->GetRect();
   if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
       rect.height == aHeight && frame->GetFormat() == aFormat &&
       frame->GetPaletteDepth() == aPaletteDepth) {
     frame->GetImageData(imageData, imageLength);
     if (paletteData) {
       frame->GetPaletteData(paletteData, paletteLength);
     }
 
+    *aRetFrame = frame;
+
     // We can re-use the frame if it has image data.
     if (*imageData && paletteData && *paletteData) {
       return NS_OK;
     }
     if (*imageData && !paletteData) {
       return NS_OK;
     }
   }
@@ -1455,29 +1463,32 @@ RasterImage::EnsureFrame(uint32_t aFrame
   frame->UnlockImageData();
 
   DeleteImgFrame(aFrameNum);
   mFrames.RemoveElementAt(aFrameNum);
   nsAutoPtr<imgFrame> newFrame(new imgFrame());
   nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
   NS_ENSURE_SUCCESS(rv, rv);
   return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
-                                imageLength, paletteData, paletteLength);
+                                imageLength, paletteData, paletteLength,
+                                aRetFrame);
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
                          gfxASurface::gfxImageFormat aFormat,
-                         uint8_t** imageData, uint32_t* imageLength)
+                         uint8_t** imageData, uint32_t* imageLength,
+                         imgFrame** aFrame)
 {
   return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
                      /* aPaletteDepth = */ 0, imageData, imageLength,
                      /* aPaletteData = */ nullptr,
-                     /* aPaletteLength = */ nullptr);
+                     /* aPaletteLength = */ nullptr,
+                     aFrame);
 }
 
 void
 RasterImage::FrameUpdated(uint32_t aFrameNum, nsIntRect &aUpdatedRect)
 {
   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
 
   imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
@@ -1487,94 +1498,16 @@ RasterImage::FrameUpdated(uint32_t aFram
     
   if (aFrameNum == GetCurrentImgFrameIndex() &&
       !IsInUpdateImageContainer()) {
     mImageContainer = nullptr;
   }
 }
 
 nsresult
-RasterImage::SetFrameDisposalMethod(uint32_t aFrameNum,
-                                    int32_t aDisposalMethod)
-{
-  if (mError)
-    return NS_ERROR_FAILURE;
-
-  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
-  if (aFrameNum >= mFrames.Length())
-    return NS_ERROR_INVALID_ARG;
-
-  imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
-  NS_ABORT_IF_FALSE(frame,
-                    "Calling SetFrameDisposalMethod on frame that doesn't exist!");
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  frame->SetFrameDisposalMethod(aDisposalMethod);
-
-  return NS_OK;
-}
-
-nsresult
-RasterImage::SetFrameTimeout(uint32_t aFrameNum, int32_t aTimeout)
-{
-  if (mError)
-    return NS_ERROR_FAILURE;
-
-  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
-  if (aFrameNum >= mFrames.Length())
-    return NS_ERROR_INVALID_ARG;
-
-  imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
-  NS_ABORT_IF_FALSE(frame, "Calling SetFrameTimeout on frame that doesn't exist!");
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  frame->SetTimeout(aTimeout);
-
-  return NS_OK;
-}
-
-nsresult
-RasterImage::SetFrameBlendMethod(uint32_t aFrameNum, int32_t aBlendMethod)
-{
-  if (mError)
-    return NS_ERROR_FAILURE;
-
-  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
-  if (aFrameNum >= mFrames.Length())
-    return NS_ERROR_INVALID_ARG;
-
-  imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
-  NS_ABORT_IF_FALSE(frame, "Calling SetFrameBlendMethod on frame that doesn't exist!");
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  frame->SetBlendMethod(aBlendMethod);
-
-  return NS_OK;
-}
-
-nsresult
-RasterImage::SetFrameHasNoAlpha(uint32_t aFrameNum)
-{
-  if (mError)
-    return NS_ERROR_FAILURE;
-
-  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
-  if (aFrameNum >= mFrames.Length())
-    return NS_ERROR_INVALID_ARG;
-
-  imgFrame *frame = GetImgFrameNoDecode(aFrameNum);
-  NS_ABORT_IF_FALSE(frame, "Calling SetFrameHasNoAlpha on frame that doesn't exist!");
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  frame->SetHasNoAlpha();
-
-  return NS_OK;
-}
-
-nsresult
 RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
   if (aFrameNum >= mFrames.Length())
     return NS_ERROR_INVALID_ARG;
@@ -3459,29 +3392,16 @@ RasterImage::FinishedSomeDecoding(eShutd
           int32_t KBps = int32_t(request->mImage->mBytesDecoded /
                                  (1024 * request->mDecodeTime.ToSeconds()));
           Telemetry::Accumulate(id, KBps);
         }
       }
     }
   }
 
-  // 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();
-    }
-    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 {
     image->mStatusTracker->SyncNotifyDecodeState();
   }
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -179,21 +179,16 @@ public:
   virtual size_t NonHeapSizeOfDecoded() const;
   virtual size_t OutOfProcessSizeOfDecoded() const;
 
   /* Triggers discarding. */
   void Discard(bool force = false);
   void ForceDiscard() { Discard(/* force = */ true); }
 
   /* Callbacks for decoders */
-  nsresult SetFrameDisposalMethod(uint32_t aFrameNum,
-                                  int32_t aDisposalMethod);
-  nsresult SetFrameTimeout(uint32_t aFrameNum, int32_t aTimeout);
-  nsresult SetFrameBlendMethod(uint32_t aFrameNum, int32_t aBlendMethod);
-  nsresult SetFrameHasNoAlpha(uint32_t aFrameNum);
   nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult);
 
   /**
    * Sets the size of the container. This should only be called by the
    * decoder. This function may be called multiple times, but will throw an
    * error if subsequent calls do not match the first.
    */
   nsresult SetSize(int32_t aWidth, int32_t aHeight);
@@ -207,27 +202,29 @@ public:
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
                        gfxASurface::gfxImageFormat aFormat,
                        uint8_t aPaletteDepth,
                        uint8_t** imageData,
                        uint32_t* imageLength,
                        uint32_t** paletteData,
-                       uint32_t* paletteLength);
+                       uint32_t* paletteLength,
+                       imgFrame** aFrame);
 
   /**
    * A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData
    * and paletteLength set to null.
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
                        gfxASurface::gfxImageFormat aFormat,
                        uint8_t** imageData,
-                       uint32_t* imageLength);
+                       uint32_t* imageLength,
+                       imgFrame** aFrame);
 
   void FrameUpdated(uint32_t aFrameNum, nsIntRect& aUpdatedRect);
 
   /* notification that the entire image has been decoded */
   nsresult DecodingComplete();
 
   /**
    * Number of times to loop the image.
@@ -659,21 +656,23 @@ private:
    * @param aRect      The position and size to draw the image
    */
   static nsresult DrawFrameTo(imgFrame *aSrcFrame,
                               imgFrame *aDstFrame,
                               nsIntRect& aRect);
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
-                                  uint32_t **paletteData, uint32_t *paletteLength);
+                                  uint32_t **paletteData, uint32_t *paletteLength,
+                                  imgFrame** aRetFrame);
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
                             gfxASurface::gfxImageFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
-                            uint32_t **paletteData, uint32_t *paletteLength);
+                            uint32_t **paletteData, uint32_t *paletteLength,
+                            imgFrame** aRetFrame);
 
   nsresult DoImageDataComplete();
 
   bool ApplyDecodeFlags(uint32_t aNewFlags);
 
   already_AddRefed<layers::Image> GetCurrentImage();
   void UpdateImageContainer();