Bug 1209705 - Propagate the DrawResult for temporary surfaces to the caller in ClippedImage. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 01 Oct 2015 11:31:41 -0700
changeset 265627 4d2c675a88302e9796334b53181260bce9fb1635
parent 265626 a473909f575931f67e836ae17dd739f480275e85
child 265628 ba3a9ec11d89c6b779f2db12a384e7e0cb22a724
push id29465
push usercbook@mozilla.com
push dateFri, 02 Oct 2015 09:56:33 +0000
treeherdermozilla-central@5f16c6c2b969 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1209705
milestone44.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 1209705 - Propagate the DrawResult for temporary surfaces to the caller in ClippedImage. r=tn
image/ClippedImage.cpp
image/ClippedImage.h
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -6,17 +6,20 @@
 #include <new>      // Workaround for bug in VS10; see bug 981264.
 #include <cmath>
 #include <utility>
 
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/Move.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Pair.h"
+#include "mozilla/Tuple.h"
 
 #include "ImageRegion.h"
 #include "Orientation.h"
 #include "SVGImageContext.h"
 
 #include "ClippedImage.h"
 
 namespace mozilla {
@@ -32,92 +35,110 @@ namespace image {
 
 class ClippedImageCachedSurface
 {
 public:
   ClippedImageCachedSurface(already_AddRefed<SourceSurface> aSurface,
                             const nsIntSize& aSize,
                             const Maybe<SVGImageContext>& aSVGContext,
                             float aFrame,
-                            uint32_t aFlags)
+                            uint32_t aFlags,
+                            DrawResult aDrawResult)
     : mSurface(aSurface)
     , mSize(aSize)
+    , mSVGContext(aSVGContext)
     , mFrame(aFrame)
     , mFlags(aFlags)
+    , mDrawResult(aDrawResult)
   {
     MOZ_ASSERT(mSurface, "Must have a valid surface");
-    if (aSVGContext) {
-      mSVGContext.emplace(*aSVGContext);
-    }
   }
 
   bool Matches(const nsIntSize& aSize,
                const Maybe<SVGImageContext>& aSVGContext,
                float aFrame,
-               uint32_t aFlags)
+               uint32_t aFlags) const
   {
     return mSize == aSize &&
            mSVGContext == aSVGContext &&
            mFrame == aFrame &&
            mFlags == aFlags;
   }
 
-  already_AddRefed<SourceSurface> Surface() {
+  already_AddRefed<SourceSurface> Surface() const
+  {
     RefPtr<SourceSurface> surf(mSurface);
     return surf.forget();
   }
 
+  DrawResult GetDrawResult() const
+  {
+    return mDrawResult;
+  }
+
+  bool NeedsRedraw() const
+  {
+    return mDrawResult != DrawResult::SUCCESS &&
+           mDrawResult != DrawResult::BAD_IMAGE;
+  }
+
 private:
   RefPtr<SourceSurface>  mSurface;
   const nsIntSize        mSize;
   Maybe<SVGImageContext> mSVGContext;
   const float            mFrame;
   const uint32_t         mFlags;
+  const DrawResult       mDrawResult;
 };
 
 class DrawSingleTileCallback : public gfxDrawingCallback
 {
 public:
   DrawSingleTileCallback(ClippedImage* aImage,
                          const nsIntSize& aSize,
                          const Maybe<SVGImageContext>& aSVGContext,
                          uint32_t aWhichFrame,
                          uint32_t aFlags)
     : mImage(aImage)
     , mSize(aSize)
     , mSVGContext(aSVGContext)
     , mWhichFrame(aWhichFrame)
     , mFlags(aFlags)
+    , mDrawResult(DrawResult::NOT_READY)
   {
     MOZ_ASSERT(mImage, "Must have an image to clip");
   }
 
   virtual bool operator()(gfxContext* aContext,
                           const gfxRect& aFillRect,
                           const GraphicsFilter& aFilter,
                           const gfxMatrix& aTransform)
   {
     MOZ_ASSERT(aTransform.IsIdentity(),
                "Caller is probably CreateSamplingRestrictedDrawable, "
                "which should not happen");
 
     // Draw the image. |gfxCallbackDrawable| always calls this function with
     // arguments that guarantee we never tile.
-    mImage->DrawSingleTile(aContext, mSize, ImageRegion::Create(aFillRect),
-                           mWhichFrame, aFilter, mSVGContext, mFlags);
+    mDrawResult =
+      mImage->DrawSingleTile(aContext, mSize, ImageRegion::Create(aFillRect),
+                             mWhichFrame, aFilter, mSVGContext, mFlags);
 
     return true;
   }
 
+  DrawResult GetDrawResult() { return mDrawResult; }
+
 private:
   nsRefPtr<ClippedImage>        mImage;
   const nsIntSize               mSize;
   const Maybe<SVGImageContext>& mSVGContext;
   const uint32_t                mWhichFrame;
   const uint32_t                mFlags;
+  DrawResult                    mDrawResult;
 };
 
 ClippedImage::ClippedImage(Image* aImage,
                            nsIntRect aClip)
   : ImageWrapper(aImage)
   , mClip(aClip)
 {
   MOZ_ASSERT(aImage != nullptr, "ClippedImage requires an existing Image");
@@ -212,76 +233,82 @@ ClippedImage::GetIntrinsicRatio(nsSize* 
   *aRatio = nsSize(mClip.width, mClip.height);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ClippedImage::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
-  return GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags);
+  DrawResult result;
+  RefPtr<SourceSurface> surface;
+  Tie(result, surface) = GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags);
+  return surface.forget();
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ClippedImage::GetFrameAtSize(const IntSize& aSize,
                              uint32_t aWhichFrame,
                              uint32_t aFlags)
 {
   // XXX(seth): It'd be nice to support downscale-during-decode for this case,
   // but right now we just fall back to the intrinsic size.
   return GetFrame(aWhichFrame, aFlags);
 }
 
-already_AddRefed<SourceSurface>
+Pair<DrawResult, RefPtr<SourceSurface>>
 ClippedImage::GetFrameInternal(const nsIntSize& aSize,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aWhichFrame,
                                uint32_t aFlags)
 {
   if (!ShouldClip()) {
-    return InnerImage()->GetFrame(aWhichFrame, aFlags);
+    RefPtr<SourceSurface> surface = InnerImage()->GetFrame(aWhichFrame, aFlags);
+    return MakePair(surface ? DrawResult::SUCCESS : DrawResult::NOT_READY,
+                    Move(surface));
   }
 
   float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
-  if (!mCachedSurface || !mCachedSurface->Matches(aSize,
-                                                  aSVGContext,
-                                                  frameToDraw,
-                                                  aFlags)) {
+  if (!mCachedSurface ||
+      !mCachedSurface->Matches(aSize, aSVGContext, frameToDraw, aFlags) ||
+      mCachedSurface->NeedsRedraw()) {
     // Create a surface to draw into.
     RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(aSize.width, aSize.height),
                                        SurfaceFormat::B8G8R8A8);
     if (!target) {
       NS_ERROR("Could not create a DrawTarget");
-      return nullptr;
+      return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
     }
 
     nsRefPtr<gfxContext> ctx = new gfxContext(target);
 
     // Create our callback.
-    nsRefPtr<gfxDrawingCallback> drawTileCallback =
+    nsRefPtr<DrawSingleTileCallback> drawTileCallback =
       new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags);
     nsRefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, aSize);
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
     gfxUtils::DrawPixelSnapped(ctx, drawable, aSize,
                                ImageRegion::Create(aSize),
                                SurfaceFormat::B8G8R8A8,
                                GraphicsFilter::FILTER_FAST,
                                imgIContainer::FLAG_CLAMP);
 
     // Cache the resulting surface.
-    mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(), aSize,
-                                                   aSVGContext, frameToDraw,
-                                                   aFlags);
+    mCachedSurface =
+      new ClippedImageCachedSurface(target->Snapshot(), aSize, aSVGContext,
+                                    frameToDraw, aFlags,
+                                    drawTileCallback->GetDrawResult());
   }
 
   MOZ_ASSERT(mCachedSurface, "Should have a cached surface now");
-  return mCachedSurface->Surface();
+  RefPtr<SourceSurface> surface = mCachedSurface->Surface();
+  return MakePair(mCachedSurface->GetDrawResult(), Move(surface));
 }
 
 NS_IMETHODIMP_(bool)
 ClippedImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   if (!ShouldClip()) {
     return InnerImage()->IsImageContainerAvailable(aManager, aFlags);
   }
@@ -332,31 +359,34 @@ ClippedImage::Draw(gfxContext* aContext,
                               aFilter, aSVGContext, aFlags);
   }
 
   // Check for tiling. If we need to tile then we need to create a
   // gfxCallbackDrawable to handle drawing for us.
   if (MustCreateSurface(aContext, aSize, aRegion, aFlags)) {
     // Create a temporary surface containing a single tile of this image.
     // GetFrame will call DrawSingleTile internally.
-    RefPtr<SourceSurface> surface =
+    DrawResult result;
+    RefPtr<SourceSurface> surface;
+    Tie(result, surface) =
       GetFrameInternal(aSize, aSVGContext, aWhichFrame, aFlags);
     if (!surface) {
-      return DrawResult::TEMPORARY_ERROR;
+      MOZ_ASSERT(result != DrawResult::SUCCESS);
+      return result;
     }
 
     // Create a drawable from that surface.
     nsRefPtr<gfxSurfaceDrawable> drawable =
       new gfxSurfaceDrawable(surface, aSize);
 
     // Draw.
     gfxUtils::DrawPixelSnapped(aContext, drawable, aSize, aRegion,
                                SurfaceFormat::B8G8R8A8, aFilter);
 
-    return DrawResult::SUCCESS;
+    return result;
   }
 
   return DrawSingleTile(aContext, aSize, aRegion, aWhichFrame,
                         aFilter, aSVGContext, aFlags);
 }
 
 static SVGImageContext
 UnclipViewport(const SVGImageContext& aOldContext,
--- a/image/ClippedImage.h
+++ b/image/ClippedImage.h
@@ -63,17 +63,17 @@ public:
                                     uint32_t aFlags) override;
 
 protected:
   ClippedImage(Image* aImage, nsIntRect aClip);
 
   virtual ~ClippedImage();
 
 private:
-  already_AddRefed<SourceSurface>
+  Pair<DrawResult, RefPtr<SourceSurface>>
     GetFrameInternal(const nsIntSize& aSize,
                      const Maybe<SVGImageContext>& aSVGContext,
                      uint32_t aWhichFrame,
                      uint32_t aFlags);
   bool ShouldClip();
   DrawResult DrawSingleTile(gfxContext* aContext,
                             const nsIntSize& aSize,
                             const ImageRegion& aRegion,