Bug 1207378 (Part 1) - Add support for a frame rect to Downscaler. r=tn,a=lizzard
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sat, 26 Sep 2015 01:36:19 -0700
changeset 296220 d6cad20ab005798b1c6af52e488508561b1d93d8
parent 296219 19a60a8e02c5d6e5284b5dac528994e40b8282d5
child 296221 9afad3de5ea3f59970d33196db59ce66ca177f16
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, lizzard
bugs1207378
milestone43.0a2
Bug 1207378 (Part 1) - Add support for a frame rect to Downscaler. r=tn,a=lizzard
image/Downscaler.cpp
image/Downscaler.h
image/decoders/nsBMPDecoder.cpp
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsIconDecoder.cpp
image/decoders/nsJPEGDecoder.cpp
image/decoders/nsPNGDecoder.cpp
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -52,38 +52,49 @@ Downscaler::ReleaseWindow()
   }
 
   mWindow = nullptr;
   mWindowCapacity = 0;
 }
 
 nsresult
 Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
+                       const Maybe<nsIntRect>& aFrameRect,
                        uint8_t* aOutputBuffer,
                        bool aHasAlpha,
                        bool aFlipVertically /* = false */)
 {
   MOZ_ASSERT(aOutputBuffer);
   MOZ_ASSERT(mTargetSize != aOriginalSize,
              "Created a downscaler, but not downscaling?");
   MOZ_ASSERT(mTargetSize.width <= aOriginalSize.width,
              "Created a downscaler, but width is larger");
   MOZ_ASSERT(mTargetSize.height <= aOriginalSize.height,
              "Created a downscaler, but height is larger");
   MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0,
              "Invalid original size");
 
+  mFrameRect = aFrameRect.valueOr(nsIntRect(nsIntPoint(), aOriginalSize));
+  MOZ_ASSERT(mFrameRect.x >= 0 && mFrameRect.y >= 0 &&
+             mFrameRect.width > 0 && mFrameRect.height > 0,
+             "Frame rect must have positive components");
+  MOZ_ASSERT(nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
+               .Contains(mFrameRect),
+             "Frame rect must fit inside image");
+  MOZ_ASSERT_IF(!nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
+                  .IsEqualEdges(mFrameRect),
+                aHasAlpha);
+
   mOriginalSize = aOriginalSize;
   mScale = gfxSize(double(mOriginalSize.width) / mTargetSize.width,
                    double(mOriginalSize.height) / mTargetSize.height);
   mOutputBuffer = aOutputBuffer;
   mHasAlpha = aHasAlpha;
   mFlipVertically = aFlipVertically;
 
-  ResetForNextProgressivePass();
   ReleaseWindow();
 
   auto resizeMethod = skia::ImageOperations::RESIZE_LANCZOS3;
 
   skia::resize::ComputeFilters(resizeMethod,
                                mOriginalSize.width, mTargetSize.width,
                                0, mTargetSize.width,
                                mXFilter.get());
@@ -119,26 +130,42 @@ Downscaler::BeginFrame(const nsIntSize& 
 
   if (MOZ_UNLIKELY(anyAllocationFailed)) {
     // We intentionally iterate through the entire array even if an allocation
     // fails, to ensure that all the pointers in it are either valid or nullptr.
     // That in turn ensures that ReleaseWindow() can clean up correctly.
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  ResetForNextProgressivePass();
+
   return NS_OK;
 }
 
 void
+Downscaler::SkipToRow(int32_t aRow)
+{
+  if (mCurrentInLine < aRow) {
+    ClearRow();
+    do {
+      CommitRow();
+    } while (mCurrentInLine < aRow);
+  }
+}
+
+void
 Downscaler::ResetForNextProgressivePass()
 {
   mPrevInvalidatedLine = 0;
   mCurrentOutLine = 0;
   mCurrentInLine = 0;
   mLinesInBuffer = 0;
+
+  // If we have a vertical offset, commit rows to shift us past it.
+  SkipToRow(mFrameRect.y);
 }
 
 static void
 GetFilterOffsetAndLength(UniquePtr<skia::ConvolutionFilter1D>& aFilter,
                          int32_t aOutputImagePosition,
                          int32_t* aFilterOffsetOut,
                          int32_t* aFilterLengthOut)
 {
@@ -188,16 +215,22 @@ Downscaler::CommitRow()
       break;  // We're done.
     }
 
     GetFilterOffsetAndLength(mYFilter, mCurrentOutLine,
                              &filterOffset, &filterLength);
   }
 
   mCurrentInLine += 1;
+
+  // If we're at the end of the part of the original image that has data, commit
+  // rows to shift us to the end.
+  if (mCurrentInLine == (mFrameRect.y + mFrameRect.height)) {
+    SkipToRow(mOriginalSize.height - 1);
+  }
 }
 
 bool
 Downscaler::HasInvalidation() const
 {
   return mCurrentOutLine > mPrevInvalidatedLine;
 }
 
--- a/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -7,20 +7,20 @@
 /**
  * Downscaler is a high-quality, streaming image downscaler based upon Skia's
  * scaling implementation.
  */
 
 #ifndef mozilla_image_Downscaler_h
 #define mozilla_image_Downscaler_h
 
+#include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 #include "nsRect.h"
 
-
 namespace skia {
   class ConvolutionFilter1D;
 } // namespace skia
 
 namespace mozilla {
 namespace image {
 
 /**
@@ -59,33 +59,40 @@ public:
   const nsIntSize& OriginalSize() const { return mOriginalSize; }
   const nsIntSize& TargetSize() const { return mTargetSize; }
   const gfxSize& Scale() const { return mScale; }
 
   /**
    * Begins a new frame and reinitializes the Downscaler.
    *
    * @param aOriginalSize The original size of this frame, before scaling.
+   * @param aFrameRect The region of  the original image which has data.
+   *                   Every pixel outside @aFrameRect is considered blank and
+   *                   has zero alpha.
    * @param aOutputBuffer The buffer to which the Downscaler should write its
    *                      output; this is the same buffer where the Decoder
    *                      would write its output when not downscaling during
    *                      decode.
    * @param aHasAlpha Whether or not this frame has an alpha channel.
    *                  Performance is a little better if it doesn't have one.
    * @param aFlipVertically If true, output rows will be written to the output
    *                        buffer in reverse order vertically, which matches
    *                        the way they are stored in some image formats.
    */
   nsresult BeginFrame(const nsIntSize& aOriginalSize,
+                      const Maybe<nsIntRect>& aFrameRect,
                       uint8_t* aOutputBuffer,
                       bool aHasAlpha,
                       bool aFlipVertically = false);
 
   /// Retrieves the buffer into which the Decoder should write each row.
-  uint8_t* RowBuffer() { return mRowBuffer.get(); }
+  uint8_t* RowBuffer()
+  {
+    return mRowBuffer.get() + mFrameRect.x * sizeof(uint32_t);
+  }
 
   /// Clears the current row buffer (optionally starting at @aStartingAtCol).
   void ClearRow(uint32_t aStartingAtCol = 0);
 
   /// Signals that the decoder has finished writing a row into the row buffer.
   void CommitRow();
 
   /// Returns true if there is a non-empty invalid rect available.
@@ -99,19 +106,21 @@ public:
    * over the same frame. Because the same data structures can be reused, this
    * is more efficient than calling BeginFrame.
    */
   void ResetForNextProgressivePass();
 
 private:
   void DownscaleInputLine();
   void ReleaseWindow();
+  void SkipToRow(int32_t aRow);
 
   nsIntSize mOriginalSize;
   nsIntSize mTargetSize;
+  nsIntRect mFrameRect;
   gfxSize mScale;
 
   uint8_t* mOutputBuffer;
 
   UniquePtr<uint8_t[]> mRowBuffer;
   UniquePtr<uint8_t*[]> mWindow;
 
   UniquePtr<skia::ConvolutionFilter1D> mXFilter;
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -470,17 +470,18 @@ nsBMPDecoder::WriteInternal(const char* 
           return;
       }
 
       MOZ_ASSERT(mImageData, "Should have a buffer now");
 
       if (mDownscaler) {
         // BMPs store their rows in reverse order, so the downscaler needs to
         // reverse them again when writing its output.
-        rv = mDownscaler->BeginFrame(GetSize(), mImageData, hasTransparency,
+        rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
+                                     mImageData, hasTransparency,
                                      /* aFlipVertically = */ true);
         if (NS_FAILED(rv)) {
           return;
         }
       }
   }
 
   if (mColors && mPos >= mLOH) {
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -288,17 +288,17 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mDownscaler) {
-    rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData,
+    rv = mDownscaler->BeginFrame(frameRect.Size(), Nothing(), mImageData,
                                  mGIFStruct.is_transparent);
   }
 
   return rv;
 }
 
 
 //******************************************************************************
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -102,17 +102,17 @@ nsIconDecoder::WriteInternal(const char*
             mState = iconStateFinished;
             return;
           }
         }
 
         MOZ_ASSERT(mImageData, "Should have a buffer now");
 
         if (mDownscaler) {
-          nsresult rv = mDownscaler->BeginFrame(GetSize(),
+          nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
                                                 mImageData,
                                                 /* aHasAlpha = */ true);
           if (NS_FAILED(rv)) {
             mState = iconStateFinished;
             return;
           }
         }
 
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -407,17 +407,17 @@ nsJPEGDecoder::WriteInternal(const char*
       MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
              ("} (could not initialize image frame)"));
       return;
     }
 
     MOZ_ASSERT(mImageData, "Should have a buffer now");
 
     if (mDownscaler) {
-      nsresult rv = mDownscaler->BeginFrame(GetSize(),
+      nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
                                             mImageData,
                                             /* aHasAlpha = */ false);
       if (NS_FAILED(rv)) {
         mState = JPEG_ERROR;
         return;
       }
     }
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -218,17 +218,18 @@ nsPNGDecoder::CreateFrame(png_uint_32 aX
       // animation playback, so we regard it as transparent.
       PostHasTransparency();
     }
   }
 #endif
 
   if (mDownscaler) {
     bool hasAlpha = aFormat != SurfaceFormat::B8G8R8X8;
-    rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData, hasAlpha);
+    rv = mDownscaler->BeginFrame(frameRect.Size(), Nothing(),
+                                 mImageData, hasAlpha);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   return NS_OK;
 }