Bug 1288040 (Part 10) - Determine the first frame refresh area of animated images while decoding them. r=edwin
authorSeth Fowler <mark.seth.fowler@gmail.com>
Tue, 19 Jul 2016 23:35:41 -0700
changeset 305900 7a652ffa8bfb8fc9a9d71f78573f7a639a129b00
parent 305899 ec558fefe42f16f4f8e1c0345762d9f59facd49b
child 305901 4dee7c0653260c0913348e7dd4ac2c284b553fc5
push id79704
push usermfowler@mozilla.com
push dateWed, 20 Jul 2016 23:31:20 +0000
treeherdermozilla-inbound@6bb7f6c316bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1288040
milestone50.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 1288040 (Part 10) - Determine the first frame refresh area of animated images while decoding them. r=edwin
image/Decoder.cpp
image/Decoder.h
image/FrameAnimator.cpp
image/FrameAnimator.h
image/ImageMetadata.h
image/RasterImage.cpp
image/RasterImage.h
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -337,45 +337,43 @@ Decoder::AllocateFrameInternal(uint32_t 
       // Another decoder beat us to decoding this frame. We abort this decoder
       // rather than treat this as a real error.
       mDecodeAborted = true;
       ref->Abort();
       return RawAccessFrameRef();
     }
   }
 
-  nsIntRect refreshArea;
-
   if (aFrameNum == 1) {
     MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
     aPreviousFrame->SetRawAccessOnly();
 
     // If we dispose of the first frame by clearing it, then the first frame's
     // refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
     AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
     if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
         previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
         previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
-      refreshArea = previousFrameData.mRect;
+      mFirstFrameRefreshArea = previousFrameData.mRect;
     }
   }
 
   if (aFrameNum > 0) {
     ref->SetRawAccessOnly();
 
     // 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.
-    refreshArea.UnionRect(refreshArea, frame->GetRect());
+    mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, frame->GetRect());
   }
 
   mFrameCount++;
 
   if (mImage) {
-    mImage->OnAddedFrame(mFrameCount, refreshArea);
+    mImage->OnAddedFrame(mFrameCount);
   }
 
   return ref;
 }
 
 /*
  * Hook stubs. Override these as necessary in decoder implementations.
  */
@@ -473,21 +471,23 @@ Decoder::PostDecodeDone(int32_t aLoopCou
 {
   MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
   MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
   MOZ_ASSERT(!mDecodeDone, "Decode already done!");
   mDecodeDone = true;
 
   mImageMetadata.SetLoopCount(aLoopCount);
 
-  // Let the image know how long a single loop through this image is. If this is
-  // a first-frame-only decode, our accumulated loop length only includes the
-  // first frame, so it's not correct and we don't record it.
+  // Some metadata that we track should take into account every frame in the
+  // image. If this is a first-frame-only decode, our accumulated loop length
+  // and first frame refresh area only includes the first frame, so it's not
+  // correct and we don't record it.
   if (!IsFirstFrameDecode()) {
     mImageMetadata.SetLoopLength(mLoopLength);
+    mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea);
   }
 
   mProgress |= FLAG_DECODE_COMPLETE;
 }
 
 void
 Decoder::PostError()
 {
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -423,16 +423,18 @@ private:
   Maybe<SourceBufferIterator> mIterator;
   RawAccessFrameRef mCurrentFrame;
   ImageMetadata mImageMetadata;
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
   Progress mProgress;
 
   uint32_t mFrameCount; // Number of frames, including anything in-progress
   FrameTimeout mLoopLength;  // Length of a single loop of this image.
+  gfx::IntRect mFirstFrameRefreshArea;  // The area of the image that needs to
+                                        // be invalidated when the animation loops.
 
   // Telemetry data for this decoder.
   TimeDuration mDecodeTime;
 
   DecoderFlags mDecoderFlags;
   SurfaceFlags mSurfaceFlags;
 
   bool mInitialized : 1;
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -38,19 +38,19 @@ AnimationState::ResetAnimation()
 
 void
 AnimationState::SetAnimationMode(uint16_t aAnimationMode)
 {
   mAnimationMode = aAnimationMode;
 }
 
 void
-AnimationState::UnionFirstFrameRefreshArea(const nsIntRect& aRect)
+AnimationState::SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea)
 {
-  mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, aRect);
+  mFirstFrameRefreshArea = aRefreshArea;
 }
 
 void
 AnimationState::InitAnimationFrameTimeIfNecessary()
 {
   if (mCurrentAnimationFrameTime.IsNull()) {
     mCurrentAnimationFrameTime = TimeStamp::Now();
   }
@@ -63,22 +63,16 @@ AnimationState::SetAnimationFrameTime(co
 }
 
 uint32_t
 AnimationState::GetCurrentAnimationFrameIndex() const
 {
   return mCurrentAnimationFrameIndex;
 }
 
-nsIntRect
-AnimationState::GetFirstFrameRefreshArea() const
-{
-  return mFirstFrameRefreshArea;
-}
-
 FrameTimeout
 AnimationState::LoopLength() const
 {
   // If we don't know the loop length yet, we have to treat it as infinite.
   if (!mLoopLength) {
     return FrameTimeout::Forever();
   }
 
@@ -198,17 +192,17 @@ FrameAnimator::AdvanceFrame(AnimationSta
     return ret;
   }
 
   if (GetTimeoutForFrame(nextFrameIndex) == FrameTimeout::Forever()) {
     ret.animationFinished = true;
   }
 
   if (nextFrameIndex == 0) {
-    ret.dirtyRect = aState.mFirstFrameRefreshArea;
+    ret.dirtyRect = aState.FirstFrameRefreshArea();
   } else {
     MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1);
 
     // Change frame
     if (!DoBlend(&ret.dirtyRect, currentFrameIndex, nextFrameIndex)) {
       // something went wrong, move on to next
       NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
       nextFrame->SetCompositingFailed(true);
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -49,20 +49,21 @@ public:
   /**
    * The animation mode of the image.
    *
    * Constants defined in imgIContainer.idl.
    */
   void SetAnimationMode(uint16_t aAnimationMode);
 
   /**
-   * Union the area to refresh when we loop around to the first frame with this
-   * rect.
+   * Get or set the area of the image to invalidate when we loop around to the
+   * first frame.
    */
-  void UnionFirstFrameRefreshArea(const nsIntRect& aRect);
+  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
+  gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
 
   /**
    * If the animation frame time has not yet been set, set it to
    * TimeStamp::Now().
    */
   void InitAnimationFrameTimeIfNecessary();
 
   /**
@@ -70,21 +71,16 @@ public:
    */
   void SetAnimationFrameTime(const TimeStamp& aTime);
 
   /**
    * The current frame we're on, from 0 to (numFrames - 1).
    */
   uint32_t GetCurrentAnimationFrameIndex() const;
 
-  /**
-   * Get the area we refresh when we loop around to the first frame.
-   */
-  nsIntRect GetFirstFrameRefreshArea() const;
-
   /*
    * Set number of times to loop the image.
    * @note -1 means loop forever.
    */
   void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
   int32_t LoopCount() const { return mLoopCount; }
 
   /// Set the @aLength of a single loop through this image.
--- a/image/ImageMetadata.h
+++ b/image/ImageMetadata.h
@@ -42,16 +42,23 @@ public:
 
   void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
   FrameTimeout GetLoopLength() const { return *mLoopLength; }
   bool HasLoopLength() const { return mLoopLength.isSome(); }
 
   void SetFirstFrameTimeout(FrameTimeout aTimeout) { mFirstFrameTimeout = aTimeout; }
   FrameTimeout GetFirstFrameTimeout() const { return mFirstFrameTimeout; }
 
+  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea)
+  {
+    mFirstFrameRefreshArea = Some(aRefreshArea);
+  }
+  gfx::IntRect GetFirstFrameRefreshArea() const { return *mFirstFrameRefreshArea; }
+  bool HasFirstFrameRefreshArea() const { return mFirstFrameRefreshArea.isSome(); }
+
   void SetSize(int32_t width, int32_t height, Orientation orientation)
   {
     if (!HasSize()) {
       mSize.emplace(nsIntSize(width, height));
       mOrientation.emplace(orientation);
     }
   }
   nsIntSize GetSize() const { return *mSize; }
@@ -70,16 +77,20 @@ private:
   int32_t mLoopCount;
 
   // The total length of a single loop through an animated image.
   Maybe<FrameTimeout> mLoopLength;
 
   /// The timeout of an animated image's first frame.
   FrameTimeout mFirstFrameTimeout;
 
+  // The area of the image that needs to be invalidated when the animation
+  // loops.
+  Maybe<gfx::IntRect> mFirstFrameRefreshArea;
+
   Maybe<nsIntSize> mSize;
   Maybe<Orientation> mOrientation;
 
   bool mHasAnimation : 1;
 };
 
 } // namespace image
 } // namespace mozilla
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -392,27 +392,16 @@ RasterImage::GetCurrentFrameIndex() cons
 }
 
 uint32_t
 RasterImage::GetRequestedFrameIndex(uint32_t aWhichFrame) const
 {
   return aWhichFrame == FRAME_FIRST ? 0 : GetCurrentFrameIndex();
 }
 
-IntRect
-RasterImage::GetFirstFrameRect()
-{
-  if (mAnimationState && mHasBeenDecoded) {
-    return mAnimationState->GetFirstFrameRefreshArea();
-  }
-
-  // Fall back to our size. This is implicitly zero-size if !mHasSize.
-  return IntRect(IntPoint(0,0), mSize);
-}
-
 NS_IMETHODIMP_(bool)
 RasterImage::IsOpaque()
 {
   if (mError) {
     return false;
   }
 
   Progress progress = mProgressTracker->GetProgress();
@@ -757,45 +746,40 @@ RasterImage::CollectSizeOfSurfaces(nsTAr
   if (mFrameAnimator) {
     mFrameAnimator->CollectSizeOfCompositingSurfaces(aCounters, aMallocSizeOf);
   }
 }
 
 class OnAddedFrameRunnable : public Runnable
 {
 public:
-  OnAddedFrameRunnable(RasterImage* aImage,
-                       uint32_t aNewFrameCount,
-                       const IntRect& aNewRefreshArea)
+  OnAddedFrameRunnable(RasterImage* aImage, uint32_t aNewFrameCount)
     : mImage(aImage)
     , mNewFrameCount(aNewFrameCount)
-    , mNewRefreshArea(aNewRefreshArea)
   {
     MOZ_ASSERT(aImage);
   }
 
   NS_IMETHOD Run()
   {
-    mImage->OnAddedFrame(mNewFrameCount, mNewRefreshArea);
+    mImage->OnAddedFrame(mNewFrameCount);
     return NS_OK;
   }
 
 private:
   RefPtr<RasterImage> mImage;
   uint32_t mNewFrameCount;
-  IntRect mNewRefreshArea;
 };
 
 void
-RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
-                          const IntRect& aNewRefreshArea)
+RasterImage::OnAddedFrame(uint32_t aNewFrameCount)
 {
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> runnable =
-      new OnAddedFrameRunnable(this, aNewFrameCount, aNewRefreshArea);
+      new OnAddedFrameRunnable(this, aNewFrameCount);
     NS_DispatchToMainThread(runnable);
     return;
   }
 
   MOZ_ASSERT(aNewFrameCount <= mFrameCount + 1, "Skipped a frame?");
 
   if (mError) {
     return;  // We're in an error state, possibly due to OOM. Bail.
@@ -807,19 +791,16 @@ RasterImage::OnAddedFrame(uint32_t aNewF
     if (aNewFrameCount == 2) {
       MOZ_ASSERT(mAnimationState, "Should already have animation state");
 
       // We may be able to start animating.
       if (mPendingAnimation && ShouldAnimate()) {
         StartAnimation();
       }
     }
-    if (aNewFrameCount > 1) {
-      mAnimationState->UnionFirstFrameRefreshArea(aNewRefreshArea);
-    }
   }
 }
 
 bool
 RasterImage::SetMetadata(const ImageMetadata& aMetadata,
                          bool aFromMetadataDecode)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -873,16 +854,20 @@ RasterImage::SetMetadata(const ImageMeta
 
   if (mAnimationState) {
     mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
     mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
 
     if (aMetadata.HasLoopLength()) {
       mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
     }
+    if (aMetadata.HasFirstFrameRefreshArea()) {
+      mAnimationState
+        ->SetFirstFrameRefreshArea(aMetadata.GetFirstFrameRefreshArea());
+    }
   }
 
   if (aMetadata.HasHotspot()) {
     IntPoint hotspot = aMetadata.GetHotspot();
 
     nsCOMPtr<nsISupportsPRUint32> intwrapx =
       do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     nsCOMPtr<nsISupportsPRUint32> intwrapy =
@@ -974,17 +959,17 @@ RasterImage::ResetAnimation()
 
   if (mAnimating) {
     StopAnimation();
   }
 
   MOZ_ASSERT(mAnimationState, "Should have AnimationState");
   mAnimationState->ResetAnimation();
 
-  NotifyProgress(NoProgress, mAnimationState->GetFirstFrameRefreshArea());
+  NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
 
   // Start the animation again. It may not have been running before, if
   // mAnimationFinished was true before entering this function.
   EvaluateAnimation();
 
   return NS_OK;
 }
 
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -173,17 +173,17 @@ public:
   /* Triggers discarding. */
   void Discard();
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoder callbacks.
   //////////////////////////////////////////////////////////////////////////////
 
-  void OnAddedFrame(uint32_t aNewFrameCount, const nsIntRect& aNewRefreshArea);
+  void OnAddedFrame(uint32_t aNewFrameCount);
 
   /**
    * Sends the provided progress notifications to ProgressTracker.
    *
    * Main-thread only.
    *
    * @param aProgress    The progress notifications to send.
    * @param aInvalidRect An invalidation rect to send.
@@ -270,18 +270,16 @@ private:
                                    const gfx::IntSize& aSize,
                                    uint32_t aFlags);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
-  nsIntRect GetFirstFrameRect();
-
   Pair<DrawResult, RefPtr<layers::Image>>
     GetCurrentImage(layers::ImageContainer* aContainer, uint32_t aFlags);
 
   void UpdateImageContainer();
 
   // We would like to just check if we have a zero lock count, but we can't do
   // that for animated images because in EnsureAnimExists we lock the image and
   // never unlock so that animated images always have their lock count >= 1. In