Bug 1054079 (Part 2) - Make it possible to initialize an imgFrame with a gfxDrawable. r=tn,mwu,mattwoodrow
authorSeth Fowler <seth@mozilla.com>
Sun, 14 Sep 2014 15:22:45 -0700
changeset 205267 721e6ae47dbc41804480f7c66debdd9efaaabc11
parent 205266 6cbcfd3c335fb5820321a3a2bf50fea6aa91a17b
child 205268 3bfa3ac98ba1fb74a8dcea08e0af1a3c39f5b731
push id49120
push usermfowler@mozilla.com
push dateSun, 14 Sep 2014 22:23:19 +0000
treeherdermozilla-inbound@3bfa3ac98ba1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, mwu, mattwoodrow
bugs1054079
milestone35.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 1054079 (Part 2) - Make it possible to initialize an imgFrame with a gfxDrawable. r=tn,mwu,mattwoodrow
image/src/FrameBlender.cpp
image/src/RasterImage.cpp
image/src/imgFrame.cpp
image/src/imgFrame.h
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -6,16 +6,19 @@
 #include "FrameBlender.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "MainThreadUtils.h"
 
 #include "pixman.h"
 
 namespace mozilla {
+
+using namespace gfx;
+
 namespace image {
 
 FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
  : mFrames(aSequenceToUse)
  , mAnim(nullptr)
  , mLoopCount(-1)
 {
   if (!mFrames) {
@@ -253,18 +256,18 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
     return true;
   }
 
   bool needToBlankComposite = false;
 
   // Create the Compositing Frame
   if (!mAnim->compositingFrame) {
     mAnim->compositingFrame.SetFrame(new imgFrame());
-    nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
-                                                gfx::SurfaceFormat::B8G8R8A8);
+    nsresult rv =
+      mAnim->compositingFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
     if (NS_FAILED(rv)) {
       mAnim->compositingFrame.SetFrame(nullptr);
       return false;
     }
     mAnim->compositingFrame.LockAndGetData();
     needToBlankComposite = true;
   } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
 
@@ -383,18 +386,19 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   // too
   if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
       (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
     // We are storing the whole image.
     // It would be better if we just stored the area that nextFrame is going to
     // overwrite.
     if (!mAnim->compositingPrevFrame) {
       mAnim->compositingPrevFrame.SetFrame(new imgFrame());
-      nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
-                                                      gfx::SurfaceFormat::B8G8R8A8);
+      nsresult rv =
+        mAnim->compositingPrevFrame->InitForDecoder(mSize,
+                                                    SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
         mAnim->compositingPrevFrame.SetFrame(nullptr);
         return false;
       }
 
       mAnim->compositingPrevFrame.LockAndGetData();
     }
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -218,18 +218,17 @@ public:
       if (NS_FAILED(image->LockImage())) {
         return false;
       }
 
       // We'll need a destination frame. It's unconditionally ARGB32 because
       // that's what the scaler outputs.
       nsRefPtr<imgFrame> tentativeDstFrame = new imgFrame();
       nsresult rv =
-        tentativeDstFrame->Init(0, 0, dstSize.width, dstSize.height,
-                                SurfaceFormat::B8G8R8A8);
+        tentativeDstFrame->InitForDecoder(dstSize, SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
         return false;
       }
 
       // We need a strong reference to the raw data for the destination frame.
       // (We already got one for the source frame in the constructor.)
       RawAccessFrameRef tentativeDstRef = tentativeDstFrame->RawAccessRef();
       if (!tentativeDstRef) {
@@ -1169,17 +1168,18 @@ RasterImage::InternalAddFrame(uint32_t f
   NS_ABORT_IF_FALSE(mDecoder, "Only decoders may add frames!");
 
   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
   if (framenum > GetNumFrames())
     return NS_ERROR_INVALID_ARG;
 
   nsRefPtr<imgFrame> frame(new imgFrame());
 
-  nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
+  nsIntRect frameRect(aX, aY, aWidth, aHeight);
+  nsresult rv = frame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
   if (!(mSize.width > 0 && mSize.height > 0))
     NS_WARNING("Shouldn't call InternalAddFrame with zero size");
   if (!NS_SUCCEEDED(rv))
     NS_WARNING("imgFrame::Init should succeed");
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We know we are in a decoder. Therefore, we must unlock the previous frame
   // when we move on to decoding into the next frame.
@@ -1353,17 +1353,18 @@ RasterImage::EnsureFrame(uint32_t aFrame
   // Not reusable, so replace the frame directly.
 
   // We know this frame is already locked, because it's the one we're currently
   // writing to.
   frame->UnlockImageData();
 
   mFrameBlender.RemoveFrame(aFrameNum);
   nsRefPtr<imgFrame> newFrame(new imgFrame());
-  nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
+  nsIntRect frameRect(aX, aY, aWidth, aHeight);
+  nsresult rv = newFrame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
   NS_ENSURE_SUCCESS(rv, rv);
   return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
                                 paletteData, paletteLength, aRetFrame);
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -138,27 +138,30 @@ imgFrame::~imgFrame()
   moz_free(mPalettedImageData);
   mPalettedImageData = nullptr;
 
   if (mInformedDiscardTracker) {
     DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
   }
 }
 
-nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
-                        SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
+nsresult
+imgFrame::InitForDecoder(const nsIntRect& aRect,
+                         SurfaceFormat aFormat,
+                         uint8_t aPaletteDepth /* = 0 */)
 {
-  // assert for properties that should be verified by decoders, warn for properties related to bad content
-  if (!AllowedImageSize(aWidth, aHeight)) {
+  // Assert for properties that should be verified by decoders,
+  // warn for properties related to bad content.
+  if (!AllowedImageSize(aRect.width, aRect.height)) {
     NS_WARNING("Should have legal image size");
     return NS_ERROR_FAILURE;
   }
 
-  mOffset.MoveTo(aX, aY);
-  mSize.SizeTo(aWidth, aHeight);
+  mOffset.MoveTo(aRect.x, aRect.y);
+  mSize.SizeTo(aRect.width, aRect.height);
 
   mFormat = aFormat;
   mPaletteDepth = aPaletteDepth;
 
   if (aPaletteDepth != 0) {
     // We're creating for a paletted image.
     if (aPaletteDepth > 8) {
       NS_WARNING("Should have legal palette depth");
@@ -167,42 +170,131 @@ nsresult imgFrame::Init(int32_t aX, int3
     }
 
     // Use the fallible allocator here
     mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength());
     if (!mPalettedImageData)
       NS_WARNING("moz_malloc for paletted image data should succeed");
     NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
   } else {
+    MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
+
     // Inform the discard tracker that we are going to allocate some memory.
-    if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
-      NS_WARNING("Exceed the hard limit of decode image size");
+    mInformedDiscardTracker =
+      DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
+    if (!mInformedDiscardTracker) {
+      NS_WARNING("Exceeded the image decode size hard limit");
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    if (!mImageSurface) {
-      mVBuf = AllocateBufferForImage(mSize, mFormat);
-      if (!mVBuf) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      if (mVBuf->OnHeap()) {
-        int32_t stride = VolatileSurfaceStride(mSize, mFormat);
-        VolatileBufferPtr<uint8_t> ptr(mVBuf);
-        memset(ptr, 0, stride * mSize.height);
-      }
-      mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
+    mVBuf = AllocateBufferForImage(mSize, mFormat);
+    if (!mVBuf) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
+    if (mVBuf->OnHeap()) {
+      int32_t stride = VolatileSurfaceStride(mSize, mFormat);
+      VolatileBufferPtr<uint8_t> ptr(mVBuf);
+      memset(ptr, 0, stride * mSize.height);
+    }
+    mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
 
     if (!mImageSurface) {
       NS_WARNING("Failed to create VolatileDataSourceSurface");
-      // Image surface allocation is failed, need to return
-      // the booked buffer size.
-      DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    mInformedDiscardTracker = true;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
+                           const nsIntSize& aSize,
+                           const SurfaceFormat aFormat,
+                           GraphicsFilter aFilter,
+                           uint32_t aImageFlags)
+{
+  // Assert for properties that should be verified by decoders,
+  // warn for properties related to bad content.
+  if (!AllowedImageSize(aSize.width, aSize.height)) {
+    NS_WARNING("Should have legal image size");
+    return NS_ERROR_FAILURE;
+  }
+
+  mOffset.MoveTo(0, 0);
+  mSize.SizeTo(aSize.width, aSize.height);
+
+  mFormat = aFormat;
+  mPaletteDepth = 0;
+
+  // Inform the discard tracker that we are going to allocate some memory.
+  mInformedDiscardTracker =
+    DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
+  if (!mInformedDiscardTracker) {
+    NS_WARNING("Exceed the image decode size hard limit");
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  RefPtr<DrawTarget> target;
+
+  bool canUseDataSurface =
+    gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();
+
+  if (canUseDataSurface) {
+    // It's safe to use data surfaces for content on this platform, so we can
+    // get away with using volatile buffers.
+    MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?");
+
+    mVBuf = AllocateBufferForImage(mSize, mFormat);
+    if (!mVBuf) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    int32_t stride = VolatileSurfaceStride(mSize, mFormat);
+    VolatileBufferPtr<uint8_t> ptr(mVBuf);
+    if (!ptr) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    if (mVBuf->OnHeap()) {
+      memset(ptr, 0, stride * mSize.height);
+    }
+    mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
+
+    target = gfxPlatform::GetPlatform()->
+      CreateDrawTargetForData(ptr, mSize, stride, mFormat);
+  } else {
+    // We can't use data surfaces for content, so we'll create an offscreen
+    // surface instead.  This means if someone later calls RawAccessRef(), we
+    // may have to do an expensive readback, but we warned callers about that in
+    // the documentation for this method.
+    MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
+
+    target = gfxPlatform::GetPlatform()->
+        CreateOffscreenContentDrawTarget(mSize, mFormat);
+  }
+
+  if (!target) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // Draw using the drawable the caller provided.
+  nsIntRect imageRect(0, 0, mSize.width, mSize.height);
+  nsRefPtr<gfxContext> ctx = new gfxContext(target);
+  gfxUtils::DrawPixelSnapped(ctx, aDrawable, ThebesIntSize(mSize),
+                             ImageRegion::Create(imageRect),
+                             mFormat, aFilter, aImageFlags);
+
+  if (canUseDataSurface && !mImageSurface) {
+    NS_WARNING("Failed to create VolatileDataSourceSurface");
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!canUseDataSurface) {
+    // We used an offscreen surface, which is an "optimized" surface from
+    // imgFrame's perspective.
+    mOptSurface = target->Snapshot();
   }
 
   return NS_OK;
 }
 
 nsresult imgFrame::Optimize()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -31,17 +31,54 @@ class imgFrame
   typedef gfx::SurfaceFormat SurfaceFormat;
 
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(imgFrame)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgFrame)
 
   imgFrame();
 
-  nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
+  /**
+   * Initialize this imgFrame with an empty surface and prepare it for being
+   * written to by a decoder.
+   *
+   * This is appropriate for use with decoded images, but it should not be used
+   * when drawing content into an imgFrame, as it may use a different graphics
+   * backend than normal content drawing.
+   */
+  nsresult InitForDecoder(const nsIntRect& aRect,
+                          SurfaceFormat aFormat,
+                          uint8_t aPaletteDepth = 0);
+
+  nsresult InitForDecoder(const nsIntSize& aSize,
+                          SurfaceFormat aFormat,
+                          uint8_t aPaletteDepth = 0)
+  {
+    return InitForDecoder(nsIntRect(0, 0, aSize.width, aSize.height),
+                          aFormat, aPaletteDepth);
+  }
+
+
+  /**
+   * Initialize this imgFrame with a new surface and draw the provided
+   * gfxDrawable into it.
+   *
+   * This is appropriate to use when drawing content into an imgFrame, as it
+   * uses the same graphics backend as normal content drawing. The downside is
+   * that the underlying surface may not be stored in a volatile buffer on all
+   * platforms, and raw access to the surface (using RawAccessRef() or
+   * LockImageData()) may be much more expensive than in the InitForDecoder()
+   * case.
+   */
+  nsresult InitWithDrawable(gfxDrawable* aDrawable,
+                            const nsIntSize& aSize,
+                            const SurfaceFormat aFormat,
+                            GraphicsFilter aFilter,
+                            uint32_t aImageFlags);
+
   nsresult Optimize();
 
   DrawableFrameRef DrawableRef();
   RawAccessFrameRef RawAccessRef();
 
   bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
             const nsIntMargin& aPadding, GraphicsFilter aFilter,
             uint32_t aImageFlags);