diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 3a3ef4e99b05..19b7bbc7c4ed 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -543,6 +543,7 @@ private: DECL_GFX_PREF(Live, "image.animated.decode-on-demand.threshold-kb", ImageAnimatedDecodeOnDemandThresholdKB, uint32_t, 20480); DECL_GFX_PREF(Live, "image.animated.decode-on-demand.batch-size", ImageAnimatedDecodeOnDemandBatchSize, uint32_t, 6); + DECL_GFX_PREF(Live, "image.animated.generate-full-frames", ImageAnimatedGenerateFullFrames, bool, false); DECL_GFX_PREF(Live, "image.animated.resume-from-last-displayed", ImageAnimatedResumeFromLastDisplayed, bool, false); DECL_GFX_PREF(Live, "image.cache.factor2.threshold-surfaces", ImageCacheFactor2ThresholdSurfaces, int32_t, -1); DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024); diff --git a/image/AnimationSurfaceProvider.cpp b/image/AnimationSurfaceProvider.cpp index d60e1861c879..697cc4292dc1 100644 --- a/image/AnimationSurfaceProvider.cpp +++ b/image/AnimationSurfaceProvider.cpp @@ -32,10 +32,10 @@ AnimationSurfaceProvider::AnimationSurfaceProvider(NotNull aImage, MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(), "Use DecodedSurfaceProvider for single-frame image decodes"); - // We still produce paletted surfaces for GIF which means the frames are - // smaller than one would expect for APNG. This may be removed if/when - // bug 1337111 lands and it is enabled by default. - size_t pixelSize = aDecoder->GetType() == DecoderType::GIF + // We may produce paletted surfaces for GIF which means the frames are smaller + // than one would expect. + size_t pixelSize = !aDecoder->ShouldBlendAnimation() && + aDecoder->GetType() == DecoderType::GIF ? sizeof(uint8_t) : sizeof(uint32_t); // Calculate how many frames we need to decode in this animation before we diff --git a/image/Decoder.cpp b/image/Decoder.cpp index 72086b9440e1..37d19b3d5be5 100644 --- a/image/Decoder.cpp +++ b/image/Decoder.cpp @@ -343,7 +343,7 @@ Decoder::AllocateFrameInternal(const gfx::IntSize& aOutputSize, bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA); if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat, aPaletteDepth, nonPremult, - aAnimParams))) { + aAnimParams, ShouldBlendAnimation()))) { NS_WARNING("imgFrame::Init should succeed"); return RawAccessFrameRef(); } diff --git a/image/FrameAnimator.cpp b/image/FrameAnimator.cpp index 317effd507bc..280e77f32dc8 100644 --- a/image/FrameAnimator.cpp +++ b/image/FrameAnimator.cpp @@ -316,10 +316,10 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, } if (nextFrameIndex == 0) { + MOZ_ASSERT(nextFrame->IsFullFrame()); ret.mDirtyRect = aState.FirstFrameRefreshArea(); - } else { + } else if (!nextFrame->IsFullFrame()) { MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1); - // Change frame if (!DoBlend(aCurrentFrame, nextFrame, nextFrameIndex, &ret.mDirtyRect)) { // something went wrong, move on to next @@ -336,6 +336,8 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, } nextFrame->SetCompositingFailed(false); + } else { + ret.mDirtyRect = nextFrame->GetDirtyRect(); } aState.mCurrentAnimationFrameTime = @@ -483,8 +485,13 @@ FrameAnimator::RequestRefresh(AnimationState& aState, } } - // Advanced to the correct frame, the composited frame is now valid to be drawn. - if (currentFrameEndTime > aTime) { + // We should only mark the composited frame as valid and reset the dirty rect + // if we advanced (meaning the next frame was actually produced somehow), the + // composited frame was previously invalid (so we may need to repaint + // everything) and the frame index is valid (to know we were doing blending + // on the main thread, instead of on the decoder threads in advance). + if (currentFrameEndTime > aTime && aState.mCompositedFrameInvalid && + mLastCompositedFrameIndex >= 0) { aState.mCompositedFrameInvalid = false; ret.mDirtyRect = IntRect(IntPoint(0,0), mSize); } diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 88b093aea377..d4f9e2bd29cc 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -1286,6 +1286,10 @@ RasterImage::Decode(const IntSize& aSize, nsresult rv; bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated; if (animated) { + if (gfxPrefs::ImageAnimatedGenerateFullFrames()) { + decoderFlags |= DecoderFlags::BLEND_ANIMATION; + } + size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex(); rv = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index d415b95980b4..3e1f3927b294 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -213,6 +213,7 @@ imgFrame::imgFrame() , mPalettedImageData(nullptr) , mPaletteDepth(0) , mNonPremult(false) + , mIsFullFrame(false) , mCompositingFailed(false) { } @@ -235,7 +236,8 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize, SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */, bool aNonPremult /* = false */, - const Maybe& aAnimParams /* = Nothing() */) + const Maybe& aAnimParams /* = Nothing() */, + bool aIsFullFrame /* = false */) { // Assert for properties that should be verified by decoders, // warn for properties related to bad content. @@ -258,8 +260,10 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize, mTimeout = aAnimParams->mTimeout; mBlendMethod = aAnimParams->mBlendMethod; mDisposalMethod = aAnimParams->mDisposalMethod; + mIsFullFrame = aAnimParams->mFrameNum == 0 || aIsFullFrame; } else { mBlendRect = aRect; + mIsFullFrame = true; } // We only allow a non-trivial frame rect (i.e., a frame rect that doesn't diff --git a/image/imgFrame.h b/image/imgFrame.h index 873fe4078d79..a5ea327872df 100644 --- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -59,7 +59,8 @@ public: SurfaceFormat aFormat, uint8_t aPaletteDepth = 0, bool aNonPremult = false, - const Maybe& aAnimParams = Nothing()); + const Maybe& aAnimParams = Nothing(), + bool aIsFullFrame = false); nsresult InitForAnimator(const nsIntSize& aSize, SurfaceFormat aFormat) @@ -68,8 +69,12 @@ public: AnimationParams animParams { frameRect, FrameTimeout::Forever(), /* aFrameNum */ 1, BlendMethod::OVER, DisposalMethod::NOT_SPECIFIED }; - return InitForDecoder(aSize, frameRect, - aFormat, 0, false, Some(animParams)); + // We set aIsFullFrame to false because we don't want the compositing frame + // to be allocated into shared memory for WebRender. mIsFullFrame is only + // otherwise used for frames produced by Decoder, so it isn't relevant. + return InitForDecoder(aSize, frameRect, aFormat, /* aPaletteDepth */ 0, + /* aNonPremult */ false, Some(animParams), + /* aIsFullFrame */ false); } @@ -191,6 +196,8 @@ public: const IntRect& GetDirtyRect() const { return mDirtyRect; } void SetDirtyRect(const IntRect& aDirtyRect) { mDirtyRect = aDirtyRect; } + bool IsFullFrame() const { return mIsFullFrame; } + bool GetCompositingFailed() const; void SetCompositingFailed(bool val); @@ -340,6 +347,9 @@ private: // data bool mNonPremult; + //! True if the frame has all of the data stored in it, false if it needs to + //! be combined with another frame (e.g. the previous frame) to be complete. + bool mIsFullFrame; ////////////////////////////////////////////////////////////////////////////// // Main-thread-only mutable data. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 118a52edae45..c64f9a128bd8 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4608,6 +4608,10 @@ pref("image.animated.decode-on-demand.threshold-kb", 20480); // animation's currently displayed frame. pref("image.animated.decode-on-demand.batch-size", 6); +// Whether we should generate full frames at decode time or partial frames which +// are combined at display time (historical behavior and default). +pref("image.animated.generate-full-frames", false); + // Resume an animated image from the last displayed frame rather than // advancing when out of view. pref("image.animated.resume-from-last-displayed", true);