Bug 1181863 (Part 2) - Add ImageOps::DecodeToSurface() to allow image decoding without involving any main-thread-only objects. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Fri, 31 Jul 2015 18:10:31 -0700
changeset 255728 44dc3228c00b1f809629d7d88c9df7749186d734
parent 255727 3e8dd82cbf50aa3a3471bde6d8d2919fa96fe769
child 255729 4d7cd400d799e73e54c7d81d8379df7c097fb3c4
push id63130
push usermfowler@mozilla.com
push dateSat, 01 Aug 2015 01:11:21 +0000
treeherdermozilla-inbound@4d7cd400d799 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1181863
milestone42.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 1181863 (Part 2) - Add ImageOps::DecodeToSurface() to allow image decoding without involving any main-thread-only objects. r=tn
image/Decoder.cpp
image/Decoder.h
image/DecoderFactory.cpp
image/DecoderFactory.h
image/ImageOps.cpp
image/ImageOps.h
image/imgTools.cpp
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -77,16 +77,21 @@ Decoder::~Decoder()
  */
 
 void
 Decoder::Init()
 {
   // No re-initializing
   MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
 
+  // It doesn't make sense to decode anything but the first frame if we can't
+  // store anything in the SurfaceCache, since only the last frame we decode
+  // will be retrievable.
+  MOZ_ASSERT(ShouldUseSurfaceCache() || IsFirstFrameDecode());
+
   // Implementation-specific initialization
   InitInternal();
 
   mInitialized = true;
 }
 
 nsresult
 Decoder::Decode()
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -285,19 +285,16 @@ public:
 
   ImageMetadata& GetImageMetadata() { return mImageMetadata; }
 
   /**
    * Returns a weak pointer to the image associated with this decoder.
    */
   RasterImage* GetImage() const { MOZ_ASSERT(mImage); return mImage.get(); }
 
-  // XXX(seth): This should be removed once we can optimize imgFrame objects
-  // off-main-thread. It only exists to support the code in Finish() for
-  // nsICODecoder.
   RawAccessFrameRef GetCurrentFrameRef()
   {
     return mCurrentFrame ? mCurrentFrame->RawAccessRef()
                          : RawAccessFrameRef();
   }
 
   /**
    * Writes data to the decoder. Only public for the benefit of nsICODecoder;
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -176,10 +176,44 @@ DecoderFactory::CreateMetadataDecoder(De
   decoder->Init();
   if (NS_FAILED(decoder->GetDecoderError())) {
     return nullptr;
   }
 
   return decoder.forget();
 }
 
+/* static */ already_AddRefed<Decoder>
+DecoderFactory::CreateAnonymousDecoder(DecoderType aType,
+                                       SourceBuffer* aSourceBuffer,
+                                       uint32_t aFlags)
+{
+  if (aType == DecoderType::UNKNOWN) {
+    return nullptr;
+  }
+
+  nsRefPtr<Decoder> decoder =
+    GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
+  MOZ_ASSERT(decoder, "Should have a decoder now");
+
+  // Initialize the decoder.
+  decoder->SetMetadataDecode(false);
+  decoder->SetIterator(aSourceBuffer->Iterator());
+  decoder->SetFlags(aFlags);
+  decoder->SetImageIsTransient(true);
+
+  // Without an image, the decoder can't store anything in the SurfaceCache, so
+  // callers will only be able to retrieve the most recent frame via
+  // Decoder::GetCurrentFrame(). That means that anonymous decoders should
+  // always be first-frame-only decoders, because nobody ever wants the *last*
+  // frame.
+  decoder->SetIsFirstFrameDecode();
+
+  decoder->Init();
+  if (NS_FAILED(decoder->GetDecoderError())) {
+    return nullptr;
+  }
+
+  return decoder.forget();
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/DecoderFactory.h
+++ b/image/DecoderFactory.h
@@ -96,16 +96,21 @@ public:
    */
   static already_AddRefed<Decoder>
   CreateMetadataDecoder(DecoderType aType,
                         RasterImage* aImage,
                         SourceBuffer* aSourceBuffer,
                         int aSampleSize,
                         const gfx::IntSize& aResolution);
 
+  static already_AddRefed<Decoder>
+  CreateAnonymousDecoder(DecoderType aType,
+                         SourceBuffer* aSourceBuffer,
+                         uint32_t aFlags);
+
 private:
   virtual ~DecoderFactory() = 0;
 
   /**
    * An internal method which allocates a new decoder of the requested @aType.
    */
   static already_AddRefed<Decoder> GetDecoder(DecoderType aType,
                                               RasterImage* aImage,
--- a/image/ImageOps.cpp
+++ b/image/ImageOps.cpp
@@ -1,22 +1,31 @@
 /* -*- 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 "imgIContainer.h"
+#include "ImageOps.h"
+
+#include "mozilla/gfx/2D.h"
+
 #include "ClippedImage.h"
+#include "DecodePool.h"
+#include "Decoder.h"
+#include "DecoderFactory.h"
 #include "DynamicImage.h"
 #include "FrozenImage.h"
+#include "Image.h"
+#include "imgIContainer.h"
+#include "nsStreamUtils.h"
 #include "OrientedImage.h"
-#include "Image.h"
+#include "SourceBuffer.h"
 
-#include "ImageOps.h"
+using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace image {
 
 /* static */ already_AddRefed<Image>
 ImageOps::Freeze(Image* aImage)
 {
   nsRefPtr<Image> frozenImage = new FrozenImage(aImage);
@@ -63,10 +72,75 @@ ImageOps::Orient(imgIContainer* aImage, 
 
 /* static */ already_AddRefed<imgIContainer>
 ImageOps::CreateFromDrawable(gfxDrawable* aDrawable)
 {
   nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
   return drawableImage.forget();
 }
 
+/* static */ already_AddRefed<gfx::SourceSurface>
+ImageOps::DecodeToSurface(nsIInputStream* aInputStream,
+                          const nsACString& aMimeType,
+                          uint32_t aFlags)
+{
+  MOZ_ASSERT(aInputStream);
+
+  nsresult rv;
+
+  // Prepare the input stream.
+  nsCOMPtr<nsIInputStream> inputStream = aInputStream;
+  if (!NS_InputStreamIsBuffered(aInputStream)) {
+    nsCOMPtr<nsIInputStream> bufStream;
+    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
+                                   aInputStream, 1024);
+    if (NS_SUCCEEDED(rv)) {
+      inputStream = bufStream;
+    }
+  }
+
+  // Figure out how much data we've been passed.
+  uint64_t length;
+  rv = inputStream->Available(&length);
+  if (NS_FAILED(rv) || length > UINT32_MAX) {
+    return nullptr;
+  }
+
+  // Write the data into a SourceBuffer.
+  nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
+  sourceBuffer->ExpectLength(length);
+  rv = sourceBuffer->AppendFromInputStream(inputStream, length);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+  sourceBuffer->Complete(NS_OK);
+
+  // Create a decoder.
+  DecoderType decoderType =
+    DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
+  nsRefPtr<Decoder> decoder =
+    DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, aFlags);
+  if (!decoder) {
+    return nullptr;
+  }
+
+  // Run the decoder synchronously.
+  decoder->Decode();
+  if (!decoder->GetDecodeDone() || decoder->HasError()) {
+    return nullptr;
+  }
+
+  // Pull out the surface.
+  RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
+  if (!frame) {
+    return nullptr;
+  }
+
+  RefPtr<SourceSurface> surface = frame->GetSurface();
+  if (!surface) {
+    return nullptr;
+  }
+
+  return surface.forget();
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/ImageOps.h
+++ b/image/ImageOps.h
@@ -7,18 +7,24 @@
 #ifndef mozilla_image_ImageOps_h
 #define mozilla_image_ImageOps_h
 
 #include "nsCOMPtr.h"
 #include "nsRect.h"
 
 class gfxDrawable;
 class imgIContainer;
+class nsIInputStream;
 
 namespace mozilla {
+
+namespace gfx {
+class SourceSurface;
+}
+
 namespace image {
 
 class Image;
 struct Orientation;
 
 class ImageOps
 {
 public:
@@ -56,16 +62,33 @@ public:
   /**
    * Creates an image from a gfxDrawable.
    *
    * @param aDrawable      The gfxDrawable.
    */
   static already_AddRefed<imgIContainer>
   CreateFromDrawable(gfxDrawable* aDrawable);
 
+  /**
+   * Decodes an image from an nsIInputStream directly into a SourceSurface,
+   * without ever creating an Image or imgIContainer (which are mostly
+   * main-thread-only). That means that this function may be called
+   * off-main-thread.
+   *
+   * @param aInputStream An input stream containing an encoded image.
+   * @param aMimeType The MIME type of the image.
+   * @param aFlags Flags of the imgIContainer::FLAG_DECODE_* variety.
+   * @return A SourceSurface containing the first frame of the image at its
+   *         intrinsic size, or nullptr if the image cannot be decoded.
+   */
+  already_AddRefed<gfx::SourceSurface>
+  DecodeToSurface(nsIInputStream* aInputStream,
+                  const nsACString& aMimeType,
+                  uint32_t aFlags);
+
 private:
   // This is a static utility class, so disallow instantiation.
   virtual ~ImageOps() = 0;
 };
 
 } // namespace image
 } // namespace mozilla
 
--- a/image/imgTools.cpp
+++ b/image/imgTools.cpp
@@ -56,16 +56,18 @@ imgTools::DecodeImageData(nsIInputStream
   return DecodeImage(aInStr, aMimeType, aContainer);
 }
 
 NS_IMETHODIMP
 imgTools::DecodeImage(nsIInputStream* aInStr,
                       const nsACString& aMimeType,
                       imgIContainer** aContainer)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsresult rv;
 
   NS_ENSURE_ARG_POINTER(aInStr);
 
   // Create a new image container to hold the decoded data.
   nsAutoCString mimeType(aMimeType);
   nsRefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType);
   nsRefPtr<ProgressTracker> tracker = image->GetProgressTracker();