Bug 1119774 (Part 4) - Add downscale-during-decode support to RasterImage::LookupFrame. r=tn
☠☠ backed out by 2aeba12466eb ☠ ☠
authorSeth Fowler <seth@mozilla.com>
Mon, 12 Jan 2015 03:24:26 -0800
changeset 249171 02d04afc91b51ffd0be8ded22595e5bba47286c1
parent 249170 9e9c62f86ca390cf701cfdf8a2aefcce68dfbd3a
child 249172 560042809ee78b57ed4fa3873c586530ca7fc446
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1119774
milestone37.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 4) - Add downscale-during-decode support to RasterImage::LookupFrame. r=tn
image/src/RasterImage.cpp
image/src/RasterImage.h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -504,47 +504,56 @@ RasterImage::LookupFrameInternal(uint32_
 DrawableFrameRef
 RasterImage::LookupFrame(uint32_t aFrameNum,
                          const nsIntSize& aSize,
                          uint32_t aFlags,
                          bool aShouldSyncNotify /* = true */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
+  nsIntSize requestedSize =
+    CanDownscaleDuringDecode(aSize, aFlags) ? aSize : mSize;
+
+  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
 
   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,
+    ref = LookupFrameInternal(aFrameNum, requestedSize,
                               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");
 
     // 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);
+    WantDecodedFrames(requestedSize, 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();
-    }
+  if (!ref && requestedSize != mSize) {
+    // There's one more last-ditch option - we can see if a version of this
+    // frame is available at our intrinsic size.
+    ref = LookupFrameInternal(aFrameNum, mSize, aFlags);
+  }
+
+  if (!ref) {
+    // We still weren't able to get a frame. Give up.
+    return DrawableFrameRef();
   }
 
   if (ref->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
 
   MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame");
 
@@ -1518,16 +1527,21 @@ RasterImage::CanScale(GraphicsFilter aFi
   // we usually have no way of updating what we've drawn, so HQ scaling is
   // useless.
   if (!gfxPrefs::ImageHQDownscalingEnabled() || !mHasSize || !mHasSourceData ||
       !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) ||
       aFilter != GraphicsFilter::FILTER_GOOD) {
     return false;
   }
 
+  // We don't HQ scale images that we can downscale during decode.
+  if (mDownscaleDuringDecode) {
+    return false;
+  }
+
   // We don't use the scaler for animated or transient images to avoid doing a
   // bunch of work on an image that just gets thrown away.
   if (mAnim || mTransient) {
     return false;
   }
 
   // If target size is 1:1 with original, don't scale.
   if (aSize == mSize) {
@@ -1553,16 +1567,50 @@ RasterImage::CanScale(GraphicsFilter aFi
   // If that's all it's getting us, I'd rather we just forbid that explicitly.
   gfx::Size scale(double(aSize.width) / mSize.width,
                   double(aSize.height) / mSize.height);
   gfxFloat minFactor = gfxPrefs::ImageHQDownscalingMinFactor() / 1000.0;
   return (scale.width < minFactor || scale.height < minFactor);
 #endif
 }
 
+bool
+RasterImage::CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags)
+{
+  // Check basic requirements: downscale-during-decode is enabled for this
+  // image, we have all the source data and know our size, the flags allow us to
+  // do it, and a 'good' filter is being used.
+  if (!mDownscaleDuringDecode || !mHasSize ||
+      !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
+    return false;
+  }
+
+  // We don't downscale animated images during decode.
+  if (mAnim) {
+    return false;
+  }
+
+  // Never upscale.
+  if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
+    return false;
+  }
+
+  // Zero or negative width or height is unacceptable.
+  if (aSize.width < 1 || aSize.height < 1) {
+    return false;
+  }
+
+  // There's no point in scaling if we can't store the result.
+  if (!SurfaceCache::CanHold(aSize.ToIntSize())) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 RasterImage::NotifyNewScaledFrame()
 {
   // Send an invalidation so observers will repaint and can take advantage of
   // the new scaled frame if possible.
   NotifyProgress(NoProgress, nsIntRect(0, 0, mSize.width, mSize.height));
 }
 
@@ -1677,36 +1725,37 @@ RasterImage::Draw(gfxContext* aContext,
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(aContext);
 
   if (IsUnlocked() && mProgressTracker) {
     mProgressTracker->OnUnlockedDraw();
   }
 
-  // XXX(seth): For now, we deliberately don't look up a frame of size aSize
-  // (though DrawWithPreDownscaleIfNeeded will do so later). It doesn't make
-  // sense to do so until we support downscale-during-decode. Right now we need
-  // to make sure that we always touch an mSize-sized frame so that we have
-  // something to HQ scale.
+  // If we're not using GraphicsFilter::FILTER_GOOD, we shouldn't high-quality
+  // scale or downscale during decode.
+  uint32_t flags = aFilter == GraphicsFilter::FILTER_GOOD
+                 ? aFlags
+                 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
+
   DrawableFrameRef ref =
-    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
+    LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, flags);
   if (!ref) {
     // Getting the frame (above) touches the image and kicks off decoding.
     if (mDrawStartTime.IsNull()) {
       mDrawStartTime = TimeStamp::Now();
     }
     return NS_OK;
   }
 
   bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
                                ref->IsImageComplete();
 
   DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize,
-                               aRegion, aFilter, aFlags);
+                               aRegion, aFilter, flags);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
       mDrawStartTime = TimeStamp();
   }
 
@@ -1985,17 +2034,20 @@ RasterImage::OptimalImageSizeForDest(con
              "Unexpected destination size");
 
   if (mSize.IsEmpty() || aDest.IsEmpty()) {
     return nsIntSize(0, 0);
   }
 
   nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
 
-  if (CanScale(aFilter, destSize, aFlags)) {
+  if (aFilter == GraphicsFilter::FILTER_GOOD &&
+      CanDownscaleDuringDecode(destSize, aFlags)) {
+    return destSize;
+  } else if (CanScale(aFilter, destSize, aFlags)) {
     DrawableFrameRef frameRef =
       SurfaceCache::Lookup(ImageKey(this),
                            RasterSurfaceKey(destSize.ToIntSize(),
                                             DecodeFlags(aFlags),
                                             0));
 
     if (frameRef && frameRef->IsImageComplete()) {
         return destSize;  // We have an existing HQ scale for this size.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -426,16 +426,19 @@ private: // data
   //////////////////////////////////////////////////////////////////////////////
 
   // Initiates an HQ scale for the given frame, if possible.
   void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
 
   // Determines whether we can perform an HQ scale with the given parameters.
   bool CanScale(GraphicsFilter aFilter, const nsIntSize& aSize, uint32_t aFlags);
 
+  // Determines whether we can downscale during decode with the given parameters.
+  bool CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags);
+
   // Called by the HQ scaler when a new scaled frame is ready.
   void NotifyNewScaledFrame();
 
   friend class ScaleRunner;
 
 
   // Error handling.
   void DoError();