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
This commit is contained in:
Joe Drew 2013-06-07 15:28:24 -04:00
parent aeaafd9ad2
commit 43351f7d0c
2 changed files with 54 additions and 0 deletions

View File

@ -529,6 +529,39 @@ RasterImage::Init(const char* aMimeType,
return NS_OK; 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<uint32_t>(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 bool
RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect) RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
{ {
@ -626,6 +659,19 @@ RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime(); 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<uint32_t>(delay.ToMilliseconds()) / loopTime;
mAnim->currentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime);
}
}
// Set currentAnimationFrameIndex at the last possible moment // Set currentAnimationFrameIndex at the last possible moment
mAnim->currentAnimationFrameIndex = nextFrameIndex; mAnim->currentAnimationFrameIndex = nextFrameIndex;

View File

@ -610,6 +610,14 @@ private:
*/ */
bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect); 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]. * Deletes and nulls out the frame in mFrames[framenum].
* *