From 43351f7d0c7ae10090b69e9fe6d654c6a087e4a6 Mon Sep 17 00:00:00 2001 From: Joe Drew Date: Fri, 7 Jun 2013 15:28:24 -0400 Subject: [PATCH] Bug 876499 - If a looping image is being asked to advance to a time that is more than one loop in the future, skip the intermediate frames and simply jump to the next multiple of the loop count. r=seth When there is a long delay between calls to RequestRefresh(), for example because an animated image has been scrolled off the screen, the current animation frame time can be significantly behind the current time, requiring a huge number of composites to catch up. This patch makes us skip those intermediate composites, jumping to the closest multiple of the image's loop time. --HG-- extra : rebase_source : 3e9ef5bd3ad3f1e9d95def99e6bb546474a583c9 --- image/src/RasterImage.cpp | 46 +++++++++++++++++++++++++++++++++++++++ image/src/RasterImage.h | 8 +++++++ 2 files changed, 54 insertions(+) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 6cd5bb37a9b1..aa5a39aaacea 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -529,6 +529,39 @@ RasterImage::Init(const char* aMimeType, return NS_OK; } +uint32_t +RasterImage::GetSingleLoopTime() const +{ + if (!mAnim) { + return 0; + } + + // If we aren't done decoding, we don't know the image's full play time. + if (!mHasBeenDecoded) { + return 0; + } + + // If we're not looping, a single loop time has no meaning + if (mLoopCount == 0) { + return 0; + } + + uint32_t looptime = 0; + for (uint32_t i = 0; i < mFrames.Length(); ++i) { + int32_t timeout = mFrames[i]->GetTimeout(); + if (timeout > 0) { + looptime += static_cast(timeout); + } else { + // If we have a frame that never times out, we're probably in an error + // case, but let's handle it more gracefully. + NS_WARNING("Negative frame timeout - how did this happen?"); + return 0; + } + } + + return looptime; +} + bool RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect) { @@ -626,6 +659,19 @@ RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect) mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime(); + // If we can get closer to the current time by a multiple of the image's loop + // time, we should. + uint32_t loopTime = GetSingleLoopTime(); + if (loopTime > 0) { + TimeDuration delay = aTime - mAnim->currentAnimationFrameTime; + if (delay.ToMilliseconds() > loopTime) { + // Explicitly use integer division to get the floor of the number of + // loops. + uint32_t loops = static_cast(delay.ToMilliseconds()) / loopTime; + mAnim->currentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime); + } + } + // Set currentAnimationFrameIndex at the last possible moment mAnim->currentAnimationFrameIndex = nextFrameIndex; diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 15a8eefe712b..ccb076ebfb44 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -610,6 +610,14 @@ private: */ bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect); + /** + * Gets the length of a single loop of this image, in milliseconds. + * + * If this image is not finished decoding, is not animated, or it is animated + * but does not loop, returns 0. + */ + uint32_t GetSingleLoopTime() const; + /** * Deletes and nulls out the frame in mFrames[framenum]. *