Bug 1119774 (Part 3) - Make it possible to propagate a target size to the decoder. r=tn
authorSeth Fowler <seth@mozilla.com>
Sun, 18 Jan 2015 14:02:13 -0800
changeset 224461 08965492c7703dd9e76b746a049cca434044ee82
parent 224460 3ce28cb70ece9bc9677664817905cebee4a83dac
child 224462 dce1e228d32f90ad31cb6c88655d24a8f99f7600
push id28131
push usercbook@mozilla.com
push dateMon, 19 Jan 2015 15:10:25 +0000
treeherdermozilla-central@f8e4fdb89a05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1119774
milestone38.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 1119774 (Part 3) - Make it possible to propagate a target size to the decoder. r=tn
image/src/Decoder.cpp
image/src/Decoder.h
image/src/ImageMetadata.h
image/src/RasterImage.cpp
image/src/RasterImage.h
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -594,32 +594,35 @@ Decoder::PostFrameStop(Opacity aFrameOpa
 
   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
 
   mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
 
   // If we're not sending partial invalidations, then we send an invalidation
   // here when the first frame is complete.
   if (!mSendPartialInvalidations && !mIsAnimated) {
-    mInvalidRect.UnionRect(mInvalidRect, mCurrentFrame->GetRect());
+    mInvalidRect.UnionRect(mInvalidRect,
+                           nsIntRect(nsIntPoint(0, 0), GetSize()));
   }
 }
 
 void
-Decoder::PostInvalidation(nsIntRect& aRect)
+Decoder::PostInvalidation(const nsIntRect& aRect,
+                          const Maybe<nsIntRect>& aRectAtTargetSize
+                            /* = Nothing() */)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
 
   // Record this invalidation, unless we're not sending partial invalidations
   // or we're past the first frame.
   if (mSendPartialInvalidations && !mIsAnimated) {
     mInvalidRect.UnionRect(mInvalidRect, aRect);
-    mCurrentFrame->ImageUpdated(aRect);
+    mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
   }
 }
 
 void
 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -123,16 +123,34 @@ public:
   bool IsSizeDecode() { return mSizeDecode; }
   void SetSizeDecode(bool aSizeDecode)
   {
     MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
     mSizeDecode = aSizeDecode;
   }
 
   /**
+   * If this decoder supports downscale-during-decode, sets the target size that
+   * this image should be decoded to.
+   *
+   * If this decoder *doesn't* support downscale-during-decode, returns
+   * NS_ERROR_NOT_AVAILABLE. If the provided size is unacceptable, returns
+   * another error.
+   *
+   * Returning NS_OK from this method is a promise that the decoder will decode
+   * the image to the requested target size unless it encounters an error.
+   *
+   * This must be called before Init() is called.
+   */
+  virtual nsresult SetTargetSize(const nsIntSize& aSize)
+  {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  /**
    * Set whether should send partial invalidations.
    *
    * If @aSend is true, we'll send partial invalidations when decoding the first
    * frame of the image, so image notifications observers will be able to
    * gradually draw in the image as it downloads.
    *
    * If @aSend is false (the default), we'll only send an invalidation when we
    * complete the first frame.
@@ -228,16 +246,22 @@ public:
   bool HasSize() const { return mImageMetadata.HasSize(); }
   void SetSizeOnImage();
 
   void SetSize(const nsIntSize& aSize, const Orientation& aOrientation)
   {
     PostSize(aSize.width, aSize.height, aOrientation);
   }
 
+  nsIntSize GetSize() const
+  {
+    MOZ_ASSERT(HasSize());
+    return mImageMetadata.GetSize();
+  }
+
   // Use HistogramCount as an invalid Histogram ID
   virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
 
   ImageMetadata& GetImageMetadata() { return mImageMetadata; }
 
   /**
    * Returns a weak pointer to the image associated with this decoder.
    */
@@ -327,19 +351,29 @@ protected:
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
   // this frame.
   void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
                      DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
                      int32_t aTimeout = 0,
                      BlendMethod aBlendMethod = BlendMethod::OVER);
 
-  // Called by the decoders when they have a region to invalidate. We may not
-  // actually pass these invalidations on right away.
-  void PostInvalidation(nsIntRect& aRect);
+  /**
+   * Called by the decoders when they have a region to invalidate. We may not
+   * actually pass these invalidations on right away.
+   *
+   * @param aRect The invalidation rect in the coordinate system of the unscaled
+   *              image (that is, the image at its intrinsic size).
+   * @param aRectAtTargetSize If not Nothing(), the invalidation rect in the
+   *                          coordinate system of the scaled image (that is,
+   *                          the image at our target decoding size). This must
+   *                          be supplied if we're downscaling during decode.
+   */
+  void PostInvalidation(const nsIntRect& aRect,
+                        const Maybe<nsIntRect>& aRectAtTargetSize = Nothing());
 
   // Called by the decoders when they have successfully decoded the image. This
   // may occur as the result of the decoder getting to the appropriate point in
   // the stream, or by us calling FinishInternal().
   //
   // May not be called mid-frame.
   //
   // For animated images, specify the loop count. -1 means loop forever, 0
--- a/image/src/ImageMetadata.h
+++ b/image/src/ImageMetadata.h
@@ -48,16 +48,17 @@ public:
     }
   }
 
   bool HasSize() const { return mSize.isSome(); }
   bool HasOrientation() const { return mOrientation.isSome(); }
 
   int32_t GetWidth() const { return mSize->width; }
   int32_t GetHeight() const { return mSize->height; }
+  nsIntSize GetSize() const { return *mSize; }
   Orientation GetOrientation() const { return *mOrientation; }
 
 private:
   // The hotspot found on cursors, or -1 if none was found.
   int32_t mHotspotX;
   int32_t mHotspotY;
 
   // The loop count for animated images, or -1 for infinite loop.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -29,16 +29,17 @@
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
 #include "gfxContext.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Services.h"
 #include <stdint.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -332,18 +333,17 @@ RasterImage::Init(const char* aMimeType,
 #endif
 
   // Lock this image's surfaces in the SurfaceCache if we're not discardable.
   if (!mDiscardable) {
     SurfaceCache::LockImage(ImageKey(this));
   }
 
   // Create the initial size decoder.
-  nsresult rv = Decode(DecodeStrategy::ASYNC, DECODE_FLAGS_DEFAULT,
-                       /* aDoSizeDecode = */ true);
+  nsresult rv = Decode(DecodeStrategy::ASYNC, Nothing(), DECODE_FLAGS_DEFAULT);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   // Mark us as initialized
   mInitialized = true;
 
   return NS_OK;
@@ -504,21 +504,28 @@ RasterImage::LookupFrame(uint32_t aFrame
   if (!ref && IsOpaque() && aFrameNum == 0) {
     // We can use non-premultiplied alpha frames when premultipled alpha is
     // requested, or vice versa, if this image is opaque. Try again with the bit
     // toggled.
     ref = LookupFrameInternal(aFrameNum, aSize,
                               aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
   }
 
+  if (!ref && !mHasSize) {
+    // We can't request a decode without knowing our intrinsic size. Give up.
+    return DrawableFrameRef();
+  }
+
   if (!ref) {
     // The OS threw this frame away. We need to redecode if we can.
     MOZ_ASSERT(!mAnim, "Animated frames should be locked");
 
-    WantDecodedFrames(aFlags, aShouldSyncNotify);
+    // XXX(seth): We'll add downscale-during-decode-related logic here in a
+    // later part, but for now we just use our intrinsic size.
+    WantDecodedFrames(mSize, aFlags, aShouldSyncNotify);
 
     // If we were able to sync decode, we should already have the frame. If we
     // had to decode asynchronously, maybe we've gotten lucky.
     ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
 
     if (!ref) {
       // We didn't successfully redecode, so just fail.
       return DrawableFrameRef();
@@ -1124,18 +1131,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
   // Let decoders know that there won't be any more data coming.
   mSourceBuffer->Complete(aStatus);
 
   if (!mHasSize) {
     // We need to guarantee that we've gotten the image's size, or at least
     // determined that we won't be able to get it, before we deliver the load
     // event. That means we have to do a synchronous size decode here.
-    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, DECODE_FLAGS_DEFAULT,
-           /* aDoSizeDecode = */ true);
+    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Nothing(), DECODE_FLAGS_DEFAULT);
   }
 
   // Determine our final status, giving precedence to Necko failure codes. We
   // check after running the size decode above in case it triggered an error.
   nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
   if (NS_FAILED(aStatus)) {
     finalStatus = aStatus;
   }
@@ -1251,23 +1257,26 @@ RasterImage::Discard()
 bool
 RasterImage::CanDiscard() {
   return mHasSourceData &&       // ...have the source data...
          !mAnim;                 // Can never discard animated images
 }
 
 // Sets up a decoder for this image.
 already_AddRefed<Decoder>
-RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
+RasterImage::CreateDecoder(const Maybe<nsIntSize>& aSize, uint32_t aFlags)
 {
   // Make sure we actually get size before doing a full decode.
-  if (aDoSizeDecode) {
+  if (aSize) {
+    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
+    MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
+               "Can only decode to our intrinsic size if we're not allowed to "
+               "downscale-during-decode");
+  } else {
     MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
-  } else {
-    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
   }
 
   // Figure out which decoder we want.
   eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
   if (type == eDecoderType_unknown) {
     return nullptr;
   }
 
@@ -1298,37 +1307,46 @@ RasterImage::CreateDecoder(bool aDoSizeD
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
   }
 
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
-  decoder->SetSizeDecode(aDoSizeDecode);
+  decoder->SetSizeDecode(!aSize);
   decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
   decoder->SetImageIsTransient(mTransient);
   decoder->SetDecodeFlags(DecodeFlags(aFlags));
-  if (!aDoSizeDecode) {
+  if (aSize) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
     decoder->SetSize(mSize, mOrientation);
-    decoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
+    decoder->NeedNewFrame(0, 0, 0, aSize->width, aSize->height,
                           SurfaceFormat::B8G8R8A8);
     decoder->AllocateFrame();
   }
   decoder->SetIterator(mSourceBuffer->Iterator());
+
+  // Set a target size for downscale-during-decode if applicable.
+  if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
+    DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
+    MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
+               "We're downscale-during-decode but decoder doesn't support it?");
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
+  }
+
   decoder->Init();
 
   if (NS_FAILED(decoder->GetDecoderError())) {
     return nullptr;
   }
 
-  if (!aDoSizeDecode) {
+  if (!aSize) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
 
     if (mDecodeCount > sMaxDecodeCount) {
       // Don't subtract out 0 from the histogram, because that causes its count
       // to go negative, which is not kosher.
       if (sMaxDecodeCount > 0) {
@@ -1338,35 +1356,36 @@ RasterImage::CreateDecoder(bool aDoSizeD
       Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount);
     }
   }
 
   return decoder.forget();
 }
 
 void
-RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
+RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                               bool aShouldSyncNotify)
 {
   if (aShouldSyncNotify) {
     // We can sync notify, which means we can also sync decode.
     if (aFlags & FLAG_SYNC_DECODE) {
-      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, aFlags);
+      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
       return;
     }
 
     // Here we are explicitly trading off flashing for responsiveness in the
     // case that we're redecoding an image (see bug 845147).
     Decode(mHasBeenDecoded ? DecodeStrategy::ASYNC
                            : DecodeStrategy::SYNC_FOR_SMALL_IMAGES,
-           aFlags);
+           Some(aSize), aFlags);
     return;
   }
 
   // We can't sync notify, so do an async decode.
-  Decode(DecodeStrategy::ASYNC, aFlags);
+  Decode(DecodeStrategy::ASYNC, Some(aSize), aFlags);
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
 RasterImage::RequestDecode()
 {
   return RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
@@ -1421,39 +1440,40 @@ bool
 RasterImage::IsDecoded()
 {
   // XXX(seth): We need to get rid of this; it's not reliable.
   return mHasBeenDecoded || mError;
 }
 
 NS_IMETHODIMP
 RasterImage::Decode(DecodeStrategy aStrategy,
-                    uint32_t aFlags,
-                    bool aDoSizeDecode /* = false */)
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags)
 {
-  MOZ_ASSERT(aDoSizeDecode || NS_IsMainThread());
+  MOZ_ASSERT(!aSize || NS_IsMainThread());
 
   if (mError) {
     return NS_ERROR_FAILURE;
   }
 
   // If we don't have a size yet, we can't do any other decoding.
-  if (!mHasSize && !aDoSizeDecode) {
+  if (!mHasSize && aSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
   // Create a decoder.
-  nsRefPtr<Decoder> decoder = CreateDecoder(aDoSizeDecode, aFlags);
+  nsRefPtr<Decoder> decoder = CreateDecoder(aSize, aFlags);
   if (!decoder) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!aDoSizeDecode) {
-    // Send out early notifications right away.
+  if (aSize) {
+    // This isn't a size decode (which doesn't send any early notifications), so
+    // send out notifications right away.
     NotifyProgress(decoder->TakeProgress(),
                    decoder->TakeInvalidRect(),
                    decoder->GetDecodeFlags());
 
     // Lock the image while we're decoding, so that it doesn't get evicted from
     // the SurfaceCache before we have a chance to realize that it's animated.
     // The corresponding unlock happens in FinalizeDecoder.
     LockImage();
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -320,22 +320,36 @@ private:
   // that case we use our animation consumers count as a proxy for lock count.
   bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
-  already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
+  /**
+   * Creates and runs a decoder, either synchronously or asynchronously
+   * according to @aStrategy. Passes the provided target size @aSize and decode
+   * flags @aFlags to CreateDecoder. If a size decode is desired, pass Nothing
+   * for @aSize.
+   */
+  NS_IMETHOD Decode(DecodeStrategy aStrategy,
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags);
 
-  void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
+  /**
+   * Creates a new decoder with a target size of @aSize and decode flags
+   * specified by @aFlags. If a size decode is desired, pass Nothing() for
+   * @aSize.
+   */
+  already_AddRefed<Decoder> CreateDecoder(const Maybe<nsIntSize>& aSize,
+                                          uint32_t aFlags);
 
-  NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
-                    bool aDoSizeDecode = false);
+  void WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                         bool aShouldSyncNotify);
 
 private: // data
   nsIntSize                  mSize;
   Orientation                mOrientation;
 
   nsCOMPtr<nsIProperties>   mProperties;
 
   /// If this image is animated, a FrameAnimator which manages its animation.