Bug 1363092. Don't update the state of an animated image that requires an invalidation when creating a new decoder because we may not be able to send invalidations. r=aosmond

This commit is contained in:
Timothy Nikkel 2017-06-01 02:19:55 -05:00
parent eadf9e9411
commit 5dbb032f4f
3 changed files with 39 additions and 40 deletions

View File

@ -30,7 +30,8 @@ namespace image {
const gfx::IntRect
AnimationState::UpdateState(bool aAnimationFinished,
RasterImage *aImage,
const gfx::IntSize& aSize)
const gfx::IntSize& aSize,
bool aAllowInvalidation /* = true */)
{
LookupResult result =
SurfaceCache::Lookup(ImageKey(aImage),
@ -44,7 +45,8 @@ AnimationState::UpdateState(bool aAnimationFinished,
const gfx::IntRect
AnimationState::UpdateStateInternal(LookupResult& aResult,
bool aAnimationFinished,
const gfx::IntSize& aSize)
const gfx::IntSize& aSize,
bool aAllowInvalidation /* = true */)
{
// Update mDiscarded and mIsCurrentlyDecoded.
if (aResult.Type() == MatchType::NOT_FOUND) {
@ -78,31 +80,33 @@ AnimationState::UpdateStateInternal(LookupResult& aResult,
gfx::IntRect ret;
// Update the value of mCompositedFrameInvalid.
if (mIsCurrentlyDecoded || aAnimationFinished) {
// Animated images that have finished their animation (ie because it is a
// finite length animation) don't have RequestRefresh called on them, and so
// mCompositedFrameInvalid would never get cleared. We clear it here (and
// also in RasterImage::Decode when we create a decoder for an image that
// has finished animated so it can display sooner than waiting until the
// decode completes). We also do it if we are fully decoded. This is safe
// to do for images that aren't finished animating because before we paint
// the refresh driver will call into us to advance to the correct frame,
// and that will succeed because we have all the frames.
if (mCompositedFrameInvalid) {
// Invalidate if we are marking the composited frame valid.
ret.SizeTo(aSize);
}
mCompositedFrameInvalid = false;
} else if (aResult.Type() == MatchType::NOT_FOUND ||
aResult.Type() == MatchType::PENDING) {
if (mHasBeenDecoded) {
MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
mCompositedFrameInvalid = true;
if (aAllowInvalidation) {
// Update the value of mCompositedFrameInvalid.
if (mIsCurrentlyDecoded || aAnimationFinished) {
// Animated images that have finished their animation (ie because it is a
// finite length animation) don't have RequestRefresh called on them, and so
// mCompositedFrameInvalid would never get cleared. We clear it here (and
// also in RasterImage::Decode when we create a decoder for an image that
// has finished animated so it can display sooner than waiting until the
// decode completes). We also do it if we are fully decoded. This is safe
// to do for images that aren't finished animating because before we paint
// the refresh driver will call into us to advance to the correct frame,
// and that will succeed because we have all the frames.
if (mCompositedFrameInvalid) {
// Invalidate if we are marking the composited frame valid.
ret.SizeTo(aSize);
}
mCompositedFrameInvalid = false;
} else if (aResult.Type() == MatchType::NOT_FOUND ||
aResult.Type() == MatchType::PENDING) {
if (mHasBeenDecoded) {
MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
mCompositedFrameInvalid = true;
}
}
// Otherwise don't change the value of mCompositedFrameInvalid, it will be
// updated by RequestRefresh.
}
// Otherwise don't change the value of mCompositedFrameInvalid, it will be
// updated by RequestRefresh.
return ret;
}

View File

@ -42,16 +42,18 @@ public:
/**
* Call this whenever a decode completes, a decode starts, or the image is
* discarded. It will update the internal state. Specifically mDiscarded,
* mCompositedFrameInvalid, and mIsCurrentlyDecoded. Returns a rect to
* invalidate.
* mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
* is true then returns a rect to invalidate.
*/
const gfx::IntRect UpdateState(bool aAnimationFinished,
RasterImage *aImage,
const gfx::IntSize& aSize);
const gfx::IntSize& aSize,
bool aAllowInvalidation = true);
private:
const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
bool aAnimationFinished,
const gfx::IntSize& aSize);
const gfx::IntSize& aSize,
bool aAllowInvalidation = true);
public:
/**

View File

@ -1261,20 +1261,13 @@ RasterImage::Decode(const IntSize& aSize,
task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
mSourceBuffer, mSize,
decoderFlags, surfaceFlags);
// We may not be able to send an invalidation right here because of async
// notifications but that shouldn't be a problem because we shouldn't be
// getting a non-empty rect back from UpdateState. This is because UpdateState
// will only return a non-empty rect if we are currently decoded, or the
// animation is finished. We can't be decoded because we are creating a decoder
// here. If the animation is finished then the composited frame would have
// been valid when the animation finished, and it's not possible to mark
// the composited frame as invalid when the animation is finished. So
// the composited frame can't change from invalid to valid in this UpdateState
// call, and hence no rect can be returned.
// We pass false for aAllowInvalidation because we may be asked to use
// async notifications. Any potential invalidation here will be sent when
// RequestRefresh is called, or NotifyDecodeComplete.
#ifdef DEBUG
gfx::IntRect rect =
#endif
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
MOZ_ASSERT(rect.IsEmpty());
} else {
task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),