Bug 1207378 (Part 1) - Add support for a frame rect to Downscaler. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sat, 26 Sep 2015 01:36:19 -0700
changeset 264548 3d603de6ef4b69d0f20032f4f860951c1f8af797
parent 264547 bd4a6ddd01a049ebf846a0e4b5a795596e65508e
child 264549 649f934c48e639a9d52e8da665b06a733f2e590d
push id65663
push usermfowler@mozilla.com
push dateSat, 26 Sep 2015 08:36:57 +0000
treeherdermozilla-inbound@649f934c48e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1207378
milestone44.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 1207378 (Part 1) - Add support for a frame rect to Downscaler. r=tn
image/Downscaler.cpp
image/Downscaler.h
image/decoders/nsBMPDecoder.cpp
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsICODecoder.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
@@ -456,17 +456,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
@@ -274,17 +274,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/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -548,17 +548,17 @@ nsICODecoder::PrepareForMask()
   // produced, so we need to downscale the mask into a temporary buffer and then
   // combine the mask's alpha values with the color values from the image.
   if (mDownscaler) {
     MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
                  mDownscaler->TargetSize().width *
                  mDownscaler->TargetSize().height *
                  sizeof(uint32_t));
     mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
-    nsresult rv = mDownscaler->BeginFrame(GetRealSize(),
+    nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
                                           mMaskBuffer.get(),
                                           /* aHasAlpha = */ true,
                                           /* aFlipVertically = */ true);
     if (NS_FAILED(rv)) {
       return Transition::Terminate(ICOState::FAILURE);
     }
   }
 
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -88,17 +88,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
@@ -393,17 +393,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
@@ -204,17 +204,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;
 }