Bug 1530774 - Part 1. Remove support in FrameAnimator for blending partial/paletted frames. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Mon, 11 Mar 2019 13:20:49 -0400
changeset 464771 e3315d7842089083b389fc666b51c693c05924fc
parent 464728 891a30b8eee20648df8b62217304fb0a0636349d
child 464772 6dd55ee8961128d0c9e314d60e7509604e7548b2
push id35725
push userrgurzau@mozilla.com
push dateMon, 18 Mar 2019 21:38:44 +0000
treeherdermozilla-central@fe798624cda0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1530774
milestone68.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 1530774 - Part 1. Remove support in FrameAnimator for blending partial/paletted frames. r=tnikkel Differential Revision: https://phabricator.services.mozilla.com/D23714
gfx/thebes/gfxPrefs.h
image/FrameAnimator.cpp
image/FrameAnimator.h
image/Image.cpp
image/RasterImage.cpp
image/imgFrame.cpp
image/imgFrame.h
modules/libpref/init/all.js
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -561,17 +561,16 @@ class gfxPrefs final {
   DECL_GFX_PREF(Live, "gl.multithreaded",                      GLMultithreaded, bool, false);
 #endif
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
   DECL_GFX_PREF(Live, "gl.use-tls-is-current",                 UseTLSIsCurrent, int32_t, 0);
 
   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.decode-on-demand.recycle", ImageAnimatedDecodeOnDemandRecycle, bool, false);
-  DECL_GFX_PREF(Once, "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(Live, "image.cache.max-rasterized-svg-threshold-kb", ImageCacheMaxRasterizedSVGThresholdKB, int32_t, 90*1024);
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.infer-src-animation.threshold-ms", ImageInferSrcAnimationThresholdMS, uint32_t, 2000);
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -1,27 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrameAnimator.h"
 
-#include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/CheckedInt.h"
 #include "imgIContainer.h"
 #include "LookupResult.h"
-#include "MainThreadUtils.h"
 #include "RasterImage.h"
 #include "gfxPrefs.h"
 
-#include "pixman.h"
-#include <algorithm>
-
 namespace mozilla {
 
 using namespace gfx;
 
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnimationState implementation.
@@ -274,40 +269,17 @@ RefreshResult FrameAnimator::AdvanceFram
     return ret;
   }
 
   if (nextFrame->GetTimeout() == FrameTimeout::Forever()) {
     ret.mAnimationFinished = true;
   }
 
   if (nextFrameIndex == 0) {
-    MOZ_ASSERT(nextFrame->IsFullFrame());
     ret.mDirtyRect = aState.FirstFrameRefreshArea();
-  } else if (!nextFrame->IsFullFrame()) {
-    MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1);
-    RawAccessFrameRef currentRef =
-        aCurrentFrame->RawAccessRef(/* aFinished */ true);
-    RawAccessFrameRef nextRef = nextFrame->RawAccessRef(/* aFinished */ true);
-
-    // Change frame
-    if (!DoBlend(currentRef, nextRef, nextFrameIndex, &ret.mDirtyRect)) {
-      // something went wrong, move on to next
-      NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
-      nextFrame->SetCompositingFailed(true);
-      aState.mCurrentAnimationFrameTime =
-          GetCurrentImgFrameEndTime(aState, aCurrentFrame->GetTimeout());
-      aState.mCurrentAnimationFrameIndex = nextFrameIndex;
-      aState.mCompositedFrameRequested = false;
-      aCurrentFrame = std::move(nextFrame);
-      aFrames.Advance(nextFrameIndex);
-
-      return ret;
-    }
-
-    nextFrame->SetCompositingFailed(false);
   } else {
     ret.mDirtyRect = nextFrame->GetDirtyRect();
   }
 
   aState.mCurrentAnimationFrameTime =
       GetCurrentImgFrameEndTime(aState, aCurrentFrame->GetTimeout());
 
   // If we can get closer to the current time by a multiple of the image's loop
@@ -454,39 +426,30 @@ RefreshResult FrameAnimator::RequestRefr
   // composited frame was previously invalid (so we may need to repaint
   // everything) and either the frame index is valid (to know we were doing
   // blending on the main thread, instead of on the decoder threads in advance),
   // or the current frame is a full frame (blends off the main thread).
   //
   // If for some reason we forget to reset aState.mCompositedFrameInvalid, then
   // GetCompositedFrame will fail, even if we have all the data available for
   // display.
-  if (currentFrameEndTime > aTime && aState.mCompositedFrameInvalid &&
-      (mLastCompositedFrameIndex >= 0 || currentFrame->IsFullFrame())) {
+  if (currentFrameEndTime > aTime && aState.mCompositedFrameInvalid) {
     aState.mCompositedFrameInvalid = false;
     ret.mDirtyRect = IntRect(IntPoint(0, 0), mSize);
   }
 
   MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid);
 
   return ret;
 }
 
 LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState,
                                                bool aMarkUsed) {
   aState.mCompositedFrameRequested = true;
 
-  // If we have a composited version of this frame, return that.
-  if (!aState.mCompositedFrameInvalid && mLastCompositedFrameIndex >= 0 &&
-      (uint32_t(mLastCompositedFrameIndex) ==
-       aState.mCurrentAnimationFrameIndex)) {
-    return LookupResult(DrawableSurface(mCompositingFrame->DrawableRef()),
-                        MatchType::EXACT);
-  }
-
   LookupResult result = SurfaceCache::Lookup(
       ImageKey(mImage),
       RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated),
       aMarkUsed);
 
   if (aState.mCompositedFrameInvalid) {
     MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
     MOZ_ASSERT(aState.GetHasRequestedDecode());
@@ -507,481 +470,13 @@ LookupResult FrameAnimator::GetComposite
   // get the frame we're looking for; treat this as if the lookup failed.
   if (NS_FAILED(result.Surface().Seek(aState.mCurrentAnimationFrameIndex))) {
     if (result.Type() == MatchType::NOT_FOUND) {
       return result;
     }
     return LookupResult(MatchType::PENDING);
   }
 
-  MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
-             "About to return a paletted frame");
-
   return result;
 }
 
-static void DoCollectSizeOfCompositingSurfaces(
-    const RawAccessFrameRef& aSurface, SurfaceMemoryCounterType aType,
-    nsTArray<SurfaceMemoryCounter>& aCounters, MallocSizeOf aMallocSizeOf) {
-  // Concoct a SurfaceKey for this surface.
-  SurfaceKey key = RasterSurfaceKey(
-      aSurface->GetImageSize(), DefaultSurfaceFlags(), PlaybackType::eStatic);
-
-  // Extract the surface's memory usage information.
-  aSurface->AddSizeOfExcludingThis(
-      aMallocSizeOf, [&](imgFrame::AddSizeOfCbData& aMetadata) {
-        // Create a counter for this surface.
-        SurfaceMemoryCounter counter(key, /* aIsLocked = */ true,
-                                     /* aCannotSubstitute */ false,
-                                     /* aIsFactor2 */ false, aType);
-
-        // Record it.
-        counter.Values().SetDecodedHeap(aMetadata.heap);
-        counter.Values().SetDecodedNonHeap(aMetadata.nonHeap);
-        counter.Values().SetExternalHandles(aMetadata.handles);
-        counter.Values().SetFrameIndex(aMetadata.index);
-        counter.Values().SetExternalId(aMetadata.externalId);
-
-        aCounters.AppendElement(counter);
-      });
-}
-
-void FrameAnimator::CollectSizeOfCompositingSurfaces(
-    nsTArray<SurfaceMemoryCounter>& aCounters,
-    MallocSizeOf aMallocSizeOf) const {
-  if (mCompositingFrame) {
-    DoCollectSizeOfCompositingSurfaces(mCompositingFrame,
-                                       SurfaceMemoryCounterType::COMPOSITING,
-                                       aCounters, aMallocSizeOf);
-  }
-
-  if (mCompositingPrevFrame) {
-    DoCollectSizeOfCompositingSurfaces(
-        mCompositingPrevFrame, SurfaceMemoryCounterType::COMPOSITING_PREV,
-        aCounters, aMallocSizeOf);
-  }
-}
-
-//******************************************************************************
-// DoBlend gets called when the timer for animation get fired and we have to
-// update the composited frame of the animation.
-bool FrameAnimator::DoBlend(const RawAccessFrameRef& aPrevFrame,
-                            const RawAccessFrameRef& aNextFrame,
-                            uint32_t aNextFrameIndex, IntRect* aDirtyRect) {
-  if (!aPrevFrame || !aNextFrame) {
-    MOZ_ASSERT_UNREACHABLE("Should have RawAccessFrameRefs to blend!");
-    return false;
-  }
-
-  DisposalMethod prevDisposalMethod = aPrevFrame->GetDisposalMethod();
-  bool prevHasAlpha = aPrevFrame->FormatHasAlpha();
-  if (prevDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
-      !mCompositingPrevFrame) {
-    prevDisposalMethod = DisposalMethod::CLEAR;
-  }
-
-  IntRect prevRect = aPrevFrame->GetBoundedBlendRect();
-  bool isFullPrevFrame = prevRect.IsEqualRect(0, 0, mSize.width, mSize.height);
-
-  // Optimization: DisposeClearAll if the previous frame is the same size as
-  //               container and it's clearing itself
-  if (isFullPrevFrame && (prevDisposalMethod == DisposalMethod::CLEAR)) {
-    prevDisposalMethod = DisposalMethod::CLEAR_ALL;
-  }
-
-  DisposalMethod nextDisposalMethod = aNextFrame->GetDisposalMethod();
-  bool nextHasAlpha = aNextFrame->FormatHasAlpha();
-
-  IntRect nextRect = aNextFrame->GetBoundedBlendRect();
-  bool isFullNextFrame = nextRect.IsEqualRect(0, 0, mSize.width, mSize.height);
-
-  if (!aNextFrame->GetIsPaletted()) {
-    // Optimization: Skip compositing if the previous frame wants to clear the
-    //               whole image
-    if (prevDisposalMethod == DisposalMethod::CLEAR_ALL) {
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      return true;
-    }
-
-    // Optimization: Skip compositing if this frame is the same size as the
-    //               container and it's fully drawing over prev frame (no alpha)
-    if (isFullNextFrame &&
-        (nextDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
-        !nextHasAlpha) {
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      return true;
-    }
-  }
-
-  // Calculate area that needs updating
-  switch (prevDisposalMethod) {
-    default:
-      MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
-    case DisposalMethod::NOT_SPECIFIED:
-    case DisposalMethod::KEEP:
-      *aDirtyRect = nextRect;
-      break;
-
-    case DisposalMethod::CLEAR_ALL:
-      // Whole image container is cleared
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      break;
-
-    case DisposalMethod::CLEAR:
-      // Calc area that needs to be redrawn (the combination of previous and
-      // this frame)
-      // XXX - This could be done with multiple framechanged calls
-      //       Having aPrevFrame way at the top of the image, and aNextFrame
-      //       way at the bottom, and both frames being small, we'd be
-      //       telling framechanged to refresh the whole image when only two
-      //       small areas are needed.
-      aDirtyRect->UnionRect(nextRect, prevRect);
-      break;
-
-    case DisposalMethod::RESTORE_PREVIOUS:
-      aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
-      break;
-  }
-
-  // Optimization:
-  //   Skip compositing if the last composited frame is this frame
-  //   (Only one composited frame was made for this animation.  Example:
-  //    Only Frame 3 of a 10 frame image required us to build a composite frame
-  //    On the second loop, we do not need to rebuild the frame
-  //    since it's still sitting in compositingFrame)
-  if (mLastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
-    return true;
-  }
-
-  bool needToBlankComposite = false;
-
-  // Create the Compositing Frame
-  if (!mCompositingFrame) {
-    RefPtr<imgFrame> newFrame = new imgFrame;
-    nsresult rv = newFrame->InitForAnimator(mSize, SurfaceFormat::B8G8R8A8);
-    if (NS_FAILED(rv)) {
-      mCompositingFrame.reset();
-      return false;
-    }
-    mCompositingFrame = newFrame->RawAccessRef();
-    needToBlankComposite = true;
-  } else if (int32_t(aNextFrameIndex) != mLastCompositedFrameIndex + 1) {
-    // If we are not drawing on top of last composited frame,
-    // then we are building a new composite frame, so let's clear it first.
-    needToBlankComposite = true;
-  }
-
-  // More optimizations possible when next frame is not transparent
-  // But if the next frame has DisposalMethod::RESTORE_PREVIOUS,
-  // this "no disposal" optimization is not possible,
-  // because the frame in "after disposal operation" state
-  // needs to be stored in compositingFrame, so it can be
-  // copied into compositingPrevFrame later.
-  bool doDisposal = true;
-  if (!nextHasAlpha && nextDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
-    if (isFullNextFrame) {
-      // Optimization: No need to dispose prev.frame when
-      // next frame is full frame and not transparent.
-      doDisposal = false;
-      // No need to blank the composite frame
-      needToBlankComposite = false;
-    } else {
-      if ((prevRect.X() >= nextRect.X()) && (prevRect.Y() >= nextRect.Y()) &&
-          (prevRect.XMost() <= nextRect.XMost()) &&
-          (prevRect.YMost() <= nextRect.YMost())) {
-        // Optimization: No need to dispose prev.frame when
-        // next frame fully overlaps previous frame.
-        doDisposal = false;
-      }
-    }
-  }
-
-  if (doDisposal) {
-    // Dispose of previous: clear, restore, or keep (copy)
-    switch (prevDisposalMethod) {
-      case DisposalMethod::CLEAR:
-        if (needToBlankComposite) {
-          // If we just created the composite, it could have anything in its
-          // buffer. Clear whole frame
-          ClearFrame(mCompositingFrame.Data(), mCompositingFrame->GetRect());
-        } else {
-          // Only blank out previous frame area (both color & Mask/Alpha)
-          ClearFrame(mCompositingFrame.Data(), mCompositingFrame->GetRect(),
-                     prevRect);
-        }
-        break;
-
-      case DisposalMethod::CLEAR_ALL:
-        ClearFrame(mCompositingFrame.Data(), mCompositingFrame->GetRect());
-        break;
-
-      case DisposalMethod::RESTORE_PREVIOUS:
-        // It would be better to copy only the area changed back to
-        // compositingFrame.
-        if (mCompositingPrevFrame) {
-          CopyFrameImage(
-              mCompositingPrevFrame.Data(), mCompositingPrevFrame->GetRect(),
-              mCompositingFrame.Data(), mCompositingFrame->GetRect());
-
-          // destroy only if we don't need it for this frame's disposal
-          if (nextDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
-            mCompositingPrevFrame.reset();
-          }
-        } else {
-          ClearFrame(mCompositingFrame.Data(), mCompositingFrame->GetRect());
-        }
-        break;
-
-      default:
-        MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
-      case DisposalMethod::NOT_SPECIFIED:
-      case DisposalMethod::KEEP:
-        // Copy previous frame into compositingFrame before we put the new
-        // frame on top
-        // Assumes that the previous frame represents a full frame (it could be
-        // smaller in size than the container, as long as the frame before it
-        // erased itself)
-        // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
-        // will always be a valid frame number.
-        if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
-          if (isFullPrevFrame && !aPrevFrame->GetIsPaletted()) {
-            // Just copy the bits
-            CopyFrameImage(aPrevFrame.Data(), prevRect,
-                           mCompositingFrame.Data(),
-                           mCompositingFrame->GetRect());
-          } else {
-            if (needToBlankComposite) {
-              // Only blank composite when prev is transparent or not full.
-              if (prevHasAlpha || !isFullPrevFrame) {
-                ClearFrame(mCompositingFrame.Data(),
-                           mCompositingFrame->GetRect());
-              }
-            }
-            DrawFrameTo(aPrevFrame.Data(), aPrevFrame->GetRect(),
-                        aPrevFrame.PaletteDataLength(), prevHasAlpha,
-                        mCompositingFrame.Data(), mCompositingFrame->GetRect(),
-                        aPrevFrame->GetBlendMethod(),
-                        aPrevFrame->GetBlendRect());
-          }
-        }
-    }
-  } else if (needToBlankComposite) {
-    // If we just created the composite, it could have anything in its
-    // buffers. Clear them
-    ClearFrame(mCompositingFrame.Data(), mCompositingFrame->GetRect());
-  }
-
-  // Check if the frame we are composing wants the previous image restored after
-  // it is done. Don't store it (again) if last frame wanted its image restored
-  // too
-  if ((nextDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
-      (prevDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
-    // We are storing the whole image.
-    // It would be better if we just stored the area that aNextFrame is going to
-    // overwrite.
-    if (!mCompositingPrevFrame) {
-      RefPtr<imgFrame> newFrame = new imgFrame;
-      nsresult rv = newFrame->InitForAnimator(mSize, SurfaceFormat::B8G8R8A8);
-      if (NS_FAILED(rv)) {
-        mCompositingPrevFrame.reset();
-        return false;
-      }
-
-      mCompositingPrevFrame = newFrame->RawAccessRef();
-    }
-
-    CopyFrameImage(mCompositingFrame.Data(), mCompositingFrame->GetRect(),
-                   mCompositingPrevFrame.Data(),
-                   mCompositingPrevFrame->GetRect());
-
-    mCompositingPrevFrame->Finish();
-  }
-
-  // blit next frame into it's correct spot
-  DrawFrameTo(aNextFrame.Data(), aNextFrame->GetRect(),
-              aNextFrame.PaletteDataLength(), nextHasAlpha,
-              mCompositingFrame.Data(), mCompositingFrame->GetRect(),
-              aNextFrame->GetBlendMethod(), aNextFrame->GetBlendRect());
-
-  // Tell the image that it is fully 'downloaded'.
-  mCompositingFrame->Finish();
-
-  mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
-
-  return true;
-}
-
-//******************************************************************************
-// Fill aFrame with black. Does also clears the mask.
-void FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect) {
-  if (!aFrameData) {
-    return;
-  }
-
-  memset(aFrameData, 0, aFrameRect.Width() * aFrameRect.Height() * 4);
-}
-
-//******************************************************************************
-void FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect,
-                               const IntRect& aRectToClear) {
-  if (!aFrameData || aFrameRect.Width() <= 0 || aFrameRect.Height() <= 0 ||
-      aRectToClear.Width() <= 0 || aRectToClear.Height() <= 0) {
-    return;
-  }
-
-  IntRect toClear = aFrameRect.Intersect(aRectToClear);
-  if (toClear.IsEmpty()) {
-    return;
-  }
-
-  uint32_t bytesPerRow = aFrameRect.Width() * 4;
-  for (int row = toClear.Y(); row < toClear.YMost(); ++row) {
-    memset(aFrameData + toClear.X() * 4 + row * bytesPerRow, 0,
-           toClear.Width() * 4);
-  }
-}
-
-//******************************************************************************
-// Whether we succeed or fail will not cause a crash, and there's not much
-// we can do about a failure, so there we don't return a nsresult
-bool FrameAnimator::CopyFrameImage(const uint8_t* aDataSrc,
-                                   const IntRect& aRectSrc, uint8_t* aDataDest,
-                                   const IntRect& aRectDest) {
-  uint32_t dataLengthSrc = aRectSrc.Width() * aRectSrc.Height() * 4;
-  uint32_t dataLengthDest = aRectDest.Width() * aRectDest.Height() * 4;
-
-  if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
-    return false;
-  }
-
-  memcpy(aDataDest, aDataSrc, dataLengthDest);
-
-  return true;
-}
-
-nsresult FrameAnimator::DrawFrameTo(const uint8_t* aSrcData,
-                                    const IntRect& aSrcRect,
-                                    uint32_t aSrcPaletteLength,
-                                    bool aSrcHasAlpha, uint8_t* aDstPixels,
-                                    const IntRect& aDstRect,
-                                    BlendMethod aBlendMethod,
-                                    const IntRect& aBlendRect) {
-  NS_ENSURE_ARG_POINTER(aSrcData);
-  NS_ENSURE_ARG_POINTER(aDstPixels);
-
-  // According to both AGIF and APNG specs, offsets are unsigned
-  if (aSrcRect.X() < 0 || aSrcRect.Y() < 0) {
-    NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
-    return NS_ERROR_FAILURE;
-  }
-
-  // Outside the destination frame, skip it
-  if ((aSrcRect.X() > aDstRect.Width()) || (aSrcRect.Y() > aDstRect.Height())) {
-    return NS_OK;
-  }
-
-  if (aSrcPaletteLength) {
-    // Larger than the destination frame, clip it
-    int32_t width = std::min(aSrcRect.Width(), aDstRect.Width() - aSrcRect.X());
-    int32_t height =
-        std::min(aSrcRect.Height(), aDstRect.Height() - aSrcRect.Y());
-
-    // The clipped image must now fully fit within destination image frame
-    NS_ASSERTION((aSrcRect.X() >= 0) && (aSrcRect.Y() >= 0) &&
-                     (aSrcRect.X() + width <= aDstRect.Width()) &&
-                     (aSrcRect.Y() + height <= aDstRect.Height()),
-                 "FrameAnimator::DrawFrameTo: Invalid aSrcRect");
-
-    // clipped image size may be smaller than source, but not larger
-    NS_ASSERTION(
-        (width <= aSrcRect.Width()) && (height <= aSrcRect.Height()),
-        "FrameAnimator::DrawFrameTo: source must be smaller than dest");
-
-    // Get pointers to image data
-    const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
-    uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
-    const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);
-
-    // Skip to the right offset
-    dstPixels += aSrcRect.X() + (aSrcRect.Y() * aDstRect.Width());
-    if (!aSrcHasAlpha) {
-      for (int32_t r = height; r > 0; --r) {
-        for (int32_t c = 0; c < width; c++) {
-          dstPixels[c] = colormap[srcPixels[c]];
-        }
-        // Go to the next row in the source resp. destination image
-        srcPixels += aSrcRect.Width();
-        dstPixels += aDstRect.Width();
-      }
-    } else {
-      for (int32_t r = height; r > 0; --r) {
-        for (int32_t c = 0; c < width; c++) {
-          const uint32_t color = colormap[srcPixels[c]];
-          if (color) {
-            dstPixels[c] = color;
-          }
-        }
-        // Go to the next row in the source resp. destination image
-        srcPixels += aSrcRect.Width();
-        dstPixels += aDstRect.Width();
-      }
-    }
-  } else {
-    pixman_image_t* src = pixman_image_create_bits(
-        aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, aSrcRect.Width(),
-        aSrcRect.Height(),
-        reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
-        aSrcRect.Width() * 4);
-    if (!src) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    pixman_image_t* dst = pixman_image_create_bits(
-        PIXMAN_a8r8g8b8, aDstRect.Width(), aDstRect.Height(),
-        reinterpret_cast<uint32_t*>(aDstPixels), aDstRect.Width() * 4);
-    if (!dst) {
-      pixman_image_unref(src);
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    // XXX(seth): This is inefficient but we'll remove it quite soon when we
-    // move frame compositing into SurfacePipe. For now we need this because
-    // RemoveFrameRectFilter has transformed PNG frames with frame rects into
-    // imgFrame's with no frame rects, but with a region of 0 alpha where the
-    // frame rect should be. This works really nicely if we're using
-    // BlendMethod::OVER, but BlendMethod::SOURCE will result in that frame rect
-    // area overwriting the previous frame, which makes the animation look
-    // wrong. This quick hack fixes that by first compositing the whle new frame
-    // with BlendMethod::OVER, and then recopying the area that uses
-    // BlendMethod::SOURCE if needed. To make this work, the decoder has to
-    // provide a "blend rect" that tells us where to do this. This is just the
-    // frame rect, but hidden in a way that makes it invisible to most of the
-    // system, so we can keep eliminating dependencies on it.
-    auto op =
-        aBlendMethod == BlendMethod::SOURCE ? PIXMAN_OP_SRC : PIXMAN_OP_OVER;
-
-    if (aBlendMethod == BlendMethod::OVER ||
-        (aBlendMethod == BlendMethod::SOURCE &&
-         aSrcRect.IsEqualEdges(aBlendRect))) {
-      // We don't need to do anything clever. (Or, in the case where no blend
-      // rect was specified, we can't.)
-      pixman_image_composite32(op, src, nullptr, dst, 0, 0, 0, 0, aSrcRect.X(),
-                               aSrcRect.Y(), aSrcRect.Width(),
-                               aSrcRect.Height());
-    } else {
-      // We need to do the OVER followed by SOURCE trick above.
-      pixman_image_composite32(PIXMAN_OP_OVER, src, nullptr, dst, 0, 0, 0, 0,
-                               aSrcRect.X(), aSrcRect.Y(), aSrcRect.Width(),
-                               aSrcRect.Height());
-      pixman_image_composite32(PIXMAN_OP_SRC, src, nullptr, dst, aBlendRect.X(),
-                               aBlendRect.Y(), 0, 0, aBlendRect.X(),
-                               aBlendRect.Y(), aBlendRect.Width(),
-                               aBlendRect.Height());
-    }
-
-    pixman_image_unref(src);
-    pixman_image_unref(dst);
-  }
-
-  return NS_OK;
-}
-
 }  // namespace image
 }  // namespace mozilla
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_FrameAnimator_h
 #define mozilla_image_FrameAnimator_h
 
 #include "mozilla/Maybe.h"
-#include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "gfxTypes.h"
 #include "imgFrame.h"
 #include "nsCOMPtr.h"
 #include "nsRect.h"
 #include "SurfaceCache.h"
 #include "gfxPrefs.h"
 
@@ -274,17 +273,17 @@ struct RefreshResult {
 
   // Whether the animation has finished playing.
   bool mAnimationFinished : 1;
 };
 
 class FrameAnimator {
  public:
   FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
-      : mImage(aImage), mSize(aSize), mLastCompositedFrameIndex(-1) {
+      : mImage(aImage), mSize(aSize) {
     MOZ_COUNT_CTOR(FrameAnimator);
   }
 
   ~FrameAnimator() { MOZ_COUNT_DTOR(FrameAnimator); }
 
   /**
    * Call when you need to re-start animating. Ensures we start from the first
    * frame.
@@ -303,25 +302,16 @@ class FrameAnimator {
 
   /**
    * Get the full frame for the current frame of the animation (it may or may
    * not have required compositing). It may not be available because it hasn't
    * been decoded yet, in which case we return an empty LookupResult.
    */
   LookupResult GetCompositedFrame(AnimationState& aState, bool aMarkUsed);
 
-  /**
-   * Collect an accounting of the memory occupied by the compositing surfaces we
-   * use during animation playback. All of the actual animation frames are
-   * stored in the SurfaceCache, so we don't need to report them here.
-   */
-  void CollectSizeOfCompositingSurfaces(
-      nsTArray<SurfaceMemoryCounter>& aCounters,
-      MallocSizeOf aMallocSizeOf) const;
-
  private:  // methods
   /**
    * Advances the animation. Typically, this will advance a single frame, but it
    * may advance multiple frames. This may happen if we have infrequently
    * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
    * lived animation frames.
    *
    * @param aTime the time that the animation should advance to. This will
@@ -341,86 +331,20 @@ class FrameAnimator {
    * Get the time the frame we're currently displaying is supposed to end.
    *
    * In the error case (like if the requested frame is not currently
    * decoded), returns None().
    */
   TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState,
                                       FrameTimeout aCurrentTimeout) const;
 
-  bool DoBlend(const RawAccessFrameRef& aPrevFrame,
-               const RawAccessFrameRef& aNextFrame, uint32_t aNextFrameIndex,
-               gfx::IntRect* aDirtyRect);
-
-  /** Clears an area of <aFrame> with transparent black.
-   *
-   * @param aFrameData Target Frame data
-   * @param aFrameRect The rectangle of the data pointed ot by aFrameData
-   *
-   * @note Does also clears the transparency mask
-   */
-  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect);
-
-  //! @overload
-  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect,
-                         const gfx::IntRect& aRectToClear);
-
-  //! Copy one frame's image and mask into another
-  static bool CopyFrameImage(const uint8_t* aDataSrc,
-                             const gfx::IntRect& aRectSrc, uint8_t* aDataDest,
-                             const gfx::IntRect& aRectDest);
-
-  /**
-   * Draws one frame's image to into another, at the position specified by
-   * aSrcRect.
-   *
-   * @aSrcData the raw data of the current frame being drawn
-   * @aSrcRect the size of the source frame, and the position of that frame in
-   *           the composition frame
-   * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
-   *                    of the source data (0 if image is not paletted)
-   * @aSrcHasAlpha whether the source data represents an image with alpha
-   * @aDstPixels the raw data of the composition frame where the current frame
-   *             is drawn into (32-bit ARGB)
-   * @aDstRect the size of the composition frame
-   * @aBlendMethod the blend method for how to blend src on the composition
-   * frame.
-   */
-  static nsresult DrawFrameTo(const uint8_t* aSrcData,
-                              const gfx::IntRect& aSrcRect,
-                              uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
-                              uint8_t* aDstPixels, const gfx::IntRect& aDstRect,
-                              BlendMethod aBlendMethod,
-                              const gfx::IntRect& aBlendRect);
-
  private:  // data
   //! A weak pointer to our owning image.
   RasterImage* mImage;
 
   //! The intrinsic size of the image.
   gfx::IntSize mSize;
-
-  /** For managing blending of frames
-   *
-   * Some animations will use the compositingFrame to composite images
-   * and just hand this back to the caller when it is time to draw the frame.
-   * NOTE: When clearing compositingFrame, remember to set
-   *       lastCompositedFrameIndex to -1.  Code assume that if
-   *       lastCompositedFrameIndex >= 0 then compositingFrame exists.
-   */
-  RawAccessFrameRef mCompositingFrame;
-
-  /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
-   *
-   * The Previous Frame (all frames composited up to the current) needs to be
-   * stored in cases where the image specifies it wants the last frame back
-   * when it's done with the current frame.
-   */
-  RawAccessFrameRef mCompositingPrevFrame;
-
-  //! Track the last composited frame for Optimizations (See DoComposite code)
-  int32_t mLastCompositedFrameIndex;
 };
 
 }  // namespace image
 }  // namespace mozilla
 
 #endif  // mozilla_image_FrameAnimator_h
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -92,21 +92,20 @@ void ImageResource::SetCurrentImage(Imag
       image, TimeStamp(), mLastFrameID++, mImageProducerID));
 
   if (aDirtyRect) {
     aContainer->SetCurrentImagesInTransaction(imageList);
   } else {
     aContainer->SetCurrentImages(imageList);
   }
 
-  // If we are generating full frames, and we are animated, then we should
-  // request that the image container be treated as such, to avoid display
-  // list rebuilding to update frames for WebRender.
-  if (gfxPrefs::ImageAnimatedGenerateFullFrames() &&
-      mProgressTracker->GetProgress() & FLAG_IS_ANIMATED) {
+  // If we are animated, then we should request that the image container be
+  // treated as such, to avoid display list rebuilding to update frames for
+  // WebRender.
+  if (mProgressTracker->GetProgress() & FLAG_IS_ANIMATED) {
     if (aDirtyRect) {
       layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
                                                    aDirtyRect.ref());
     } else {
       IntRect dirtyRect(IntPoint(0, 0), aSurface->GetSize());
       layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
                                                    dirtyRect);
     }
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -367,24 +367,16 @@ LookupResult RasterImage::LookupFrame(co
     }
   }
 
   if (!result) {
     // We still weren't able to get a frame. Give up.
     return result;
   }
 
-  if (result.Surface()->GetCompositingFailed()) {
-    DrawableSurface tmp = std::move(result.Surface());
-    return result;
-  }
-
-  MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
-             "Should not have a paletted frame");
-
   // Sync decoding guarantees that we got the frame, but if it's owned by an
   // async decoder that's currently running, the contents of the frame may not
   // be available yet. Make sure we get everything.
   if (mAllSourceData && (aFlags & FLAG_SYNC_DECODE)) {
     result.Surface()->WaitUntilFinished();
   }
 
   // If we could have done some decoding in this function we need to check if
@@ -677,19 +669,16 @@ size_t RasterImage::SizeOfSourceWithComp
   return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
       aState.mMallocSizeOf);
 }
 
 void RasterImage::CollectSizeOfSurfaces(
     nsTArray<SurfaceMemoryCounter>& aCounters,
     MallocSizeOf aMallocSizeOf) const {
   SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
-  if (mFrameAnimator) {
-    mFrameAnimator->CollectSizeOfCompositingSurfaces(aCounters, aMallocSizeOf);
-  }
 }
 
 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
                               bool aFromMetadataDecode) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mError) {
     return true;
@@ -1202,19 +1191,17 @@ bool RasterImage::Decode(const IntSize& 
     surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
   }
 
   // Create a decoder.
   RefPtr<IDecodingTask> task;
   nsresult rv;
   bool animated = mAnimationState && aPlaybackType == PlaybackType::eAnimated;
   if (animated) {
-    if (gfxPrefs::ImageAnimatedGenerateFullFrames()) {
-      decoderFlags |= DecoderFlags::BLEND_ANIMATION;
-    }
+    decoderFlags |= DecoderFlags::BLEND_ANIMATION;
 
     size_t currentFrame = mAnimationState->GetCurrentAnimationFrameIndex();
     rv = DecoderFactory::CreateAnimationDecoder(
         mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, decoderFlags,
         surfaceFlags, currentFrame, getter_AddRefs(task));
   } else {
     rv = DecoderFactory::CreateDecoder(
         mDecoderType, WrapNotNull(this), mSourceBuffer, mSize, aSize,
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -202,18 +202,17 @@ imgFrame::imgFrame()
       mShouldRecycle(false),
       mTimeout(FrameTimeout::FromRawMilliseconds(100)),
       mDisposalMethod(DisposalMethod::NOT_SPECIFIED),
       mBlendMethod(BlendMethod::OVER),
       mFormat(SurfaceFormat::UNKNOWN),
       mPalettedImageData(nullptr),
       mPaletteDepth(0),
       mNonPremult(false),
-      mIsFullFrame(false),
-      mCompositingFailed(false) {}
+      mIsFullFrame(false) {}
 
 imgFrame::~imgFrame() {
 #ifdef DEBUG
   MonitorAutoLock lock(mMonitor);
   MOZ_ASSERT(mAborted || AreAllPixelsWritten());
   MOZ_ASSERT(mAborted || mFinished);
 #endif
 
@@ -1036,26 +1035,16 @@ void imgFrame::WaitUntilFinished() const
   }
 }
 
 bool imgFrame::AreAllPixelsWritten() const {
   mMonitor.AssertCurrentThreadOwns();
   return mDecoded.IsEqualInterior(mFrameRect);
 }
 
-bool imgFrame::GetCompositingFailed() const {
-  MOZ_ASSERT(NS_IsMainThread());
-  return mCompositingFailed;
-}
-
-void imgFrame::SetCompositingFailed(bool val) {
-  MOZ_ASSERT(NS_IsMainThread());
-  mCompositingFailed = val;
-}
-
 void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                       const AddSizeOfCb& aCallback) const {
   MonitorAutoLock lock(mMonitor);
 
   AddSizeOfCbData metadata;
   if (mPalettedImageData) {
     metadata.heap += aMallocSizeOf(mPalettedImageData);
   }
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -51,29 +51,16 @@ class imgFrame {
    * backend than normal content drawing.
    */
   nsresult InitForDecoder(const nsIntSize& aImageSize, const nsIntRect& aRect,
                           SurfaceFormat aFormat, uint8_t aPaletteDepth,
                           bool aNonPremult,
                           const Maybe<AnimationParams>& aAnimParams,
                           bool aIsFullFrame, bool aShouldRecycle);
 
-  nsresult InitForAnimator(const nsIntSize& aSize, SurfaceFormat aFormat) {
-    nsIntRect frameRect(0, 0, aSize.width, aSize.height);
-    AnimationParams animParams{frameRect, FrameTimeout::Forever(),
-                               /* aFrameNum */ 1, BlendMethod::OVER,
-                               DisposalMethod::NOT_SPECIFIED};
-    // 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, /* aShouldRecycle */ false);
-  }
-
   /**
    * Reinitialize this imgFrame with the new parameters, but otherwise retain
    * the underlying buffer.
    *
    * This is appropriate for use with animated images, where the decoder was
    * given an IDecoderFrameRecycler object which may yield a recycled imgFrame
    * that was discarded to save memory.
    */
@@ -195,19 +182,16 @@ class imgFrame {
   uint32_t* GetPaletteData() const;
   uint8_t GetPaletteDepth() const { return mPaletteDepth; }
 
   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);
-
   void SetOptimizable();
 
   void FinalizeSurface();
   already_AddRefed<SourceSurface> GetSourceSurface();
 
   struct AddSizeOfCbData {
     AddSizeOfCbData()
         : heap(0), nonHeap(0), handles(0), index(0), externalId(0) {}
@@ -373,22 +357,16 @@ class imgFrame {
   uint8_t* mPalettedImageData;
   uint8_t mPaletteDepth;
 
   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.
-  //////////////////////////////////////////////////////////////////////////////
-
-  bool mCompositingFailed;
 };
 
 /**
  * A reference to an imgFrame that holds the imgFrame's surface in memory,
  * allowing drawing. If you have a DrawableFrameRef |ref| and |if (ref)| returns
  * true, then calls to Draw() and GetSourceSurface() are guaranteed to succeed.
  */
 class DrawableFrameRef final {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4795,20 +4795,16 @@ pref("image.animated.decode-on-demand.th
 // animation's currently displayed frame.
 pref("image.animated.decode-on-demand.batch-size", 6);
 
 // Whether we should recycle already displayed frames instead of discarding
 // them. This saves on the allocation itself, and may be able to reuse the
 // contents as well. Only applies if generating full frames.
 pref("image.animated.decode-on-demand.recycle", true);
 
-// 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", true);
-
 // Resume an animated image from the last displayed frame rather than
 // advancing when out of view.
 pref("image.animated.resume-from-last-displayed", true);
 
 // Maximum number of surfaces for an image before entering "factor of 2" mode.
 // This in addition to the number of "native" sizes of an image. A native size
 // is a size for which we can decode a frame without up or downscaling. Most
 // images only have 1, but some (i.e. ICOs) may have multiple frames for the