Bug 1201763 - Add downscale-during-decode support for the ICON decoder. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Tue, 08 Sep 2015 22:07:18 -0700
changeset 294086 c9a6d9c234181121b667aea4087680fdbd254ba8
parent 294085 aeb2c0a8eda05c36b54d3632126f1c8667db1344
child 294087 022245b323cf56ef396e11fc2db0c6e8b3c13012
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
bugs1201763
milestone43.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 1201763 - Add downscale-during-decode support for the ICON decoder. r=tn
image/ImageFactory.cpp
image/decoders/nsIconDecoder.cpp
image/decoders/nsIconDecoder.h
--- a/image/ImageFactory.cpp
+++ b/image/ImageFactory.cpp
@@ -32,16 +32,17 @@ namespace image {
 ImageFactory::Initialize()
 { }
 
 static bool
 ShouldDownscaleDuringDecode(const nsCString& aMimeType)
 {
   DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
   return type == DecoderType::JPEG ||
+         type == DecoderType::ICON ||
          type == DecoderType::PNG ||
          type == DecoderType::BMP;
 }
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -7,41 +7,56 @@
 #include "nsIconDecoder.h"
 #include "nsIInputStream.h"
 #include "nspr.h"
 #include "nsRect.h"
 #include "nsError.h"
 #include "RasterImage.h"
 #include <algorithm>
 
+using namespace mozilla::gfx;
+
+using std::min;
+
 namespace mozilla {
 namespace image {
 
 nsIconDecoder::nsIconDecoder(RasterImage* aImage)
- : Decoder(aImage),
-   mWidth(-1),
-   mHeight(-1),
-   mPixBytesRead(0),
-   mState(iconStateStart)
+ : Decoder(aImage)
+ , mExpectedDataLength(0)
+ , mPixBytesRead(0)
+ , mState(iconStateStart)
+ , mWidth(-1)
+ , mHeight(-1)
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
+nsresult
+nsIconDecoder::SetTargetSize(const nsIntSize& aSize)
+{
+  // Make sure the size is reasonable.
+  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a downscaler that we'll filter our output through.
+  mDownscaler.emplace(aSize);
+
+  return NS_OK;
+}
+
 void
 nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
 
-  // We put this here to avoid errors about crossing initialization with case
-  // jumps on linux.
-  uint32_t bytesToRead = 0;
-
   // Loop until the input data is gone
   while (aCount > 0) {
     switch (mState) {
       case iconStateStart:
 
         // Grab the width
         mWidth = (uint8_t)*aBuffer;
 
@@ -68,55 +83,107 @@ nsIconDecoder::WriteInternal(const char*
         }
 
         // If we're doing a metadata decode, we're done.
         if (IsMetadataDecode()) {
           mState = iconStateFinished;
           break;
         }
 
+        // The input is 32bpp, so we expect 4 bytes of data per pixel.
+        mExpectedDataLength = mWidth * mHeight * 4;
+
         {
           MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
-          nsresult rv = AllocateBasicFrame();
+          IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
+                                           : GetSize();
+          nsresult rv = AllocateFrame(0, targetSize,
+                                      IntRect(IntPoint(), targetSize),
+                                      gfx::SurfaceFormat::B8G8R8A8);
           if (NS_FAILED(rv)) {
             mState = iconStateFinished;
             return;
           }
         }
 
         MOZ_ASSERT(mImageData, "Should have a buffer now");
 
+        if (mDownscaler) {
+          nsresult rv = mDownscaler->BeginFrame(GetSize(),
+                                                mImageData,
+                                                /* aHasAlpha = */ true);
+          if (NS_FAILED(rv)) {
+            mState = iconStateFinished;
+            return;
+          }
+        }
+
         // Book Keeping
         aBuffer++;
         aCount--;
         mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels: {
 
         // How many bytes are we reading?
-        bytesToRead = std::min(aCount, mImageDataLength - mPixBytesRead);
+        uint32_t bytesToRead = min(aCount, mExpectedDataLength - mPixBytesRead);
+
+        if (mDownscaler) {
+          uint8_t* row = mDownscaler->RowBuffer();
+          const uint32_t bytesPerRow = mWidth * 4;
+          const uint32_t rowOffset = mPixBytesRead % bytesPerRow;
 
-        // Copy the bytes
-        memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
+          // Update global state; we're about to read |bytesToRead| bytes.
+          aCount -= bytesToRead;
+          mPixBytesRead += bytesToRead;
+
+          if (rowOffset > 0) {
+            // Finish the current row.
+            const uint32_t remaining = bytesPerRow - rowOffset;
+            memcpy(row + rowOffset, aBuffer, remaining);
+            aBuffer += remaining;
+            bytesToRead -= remaining;
+            mDownscaler->CommitRow();
+          }
 
-        // Performance isn't critical here, so our update rectangle is
-        // always the full icon
-        nsIntRect r(0, 0, mWidth, mHeight);
+          // Copy the bytes a row at a time.
+          while (bytesToRead > bytesPerRow) {
+            memcpy(row, aBuffer, bytesPerRow);
+            aBuffer += bytesPerRow;
+            bytesToRead -= bytesPerRow;
+            mDownscaler->CommitRow();
+          }
+
+          // Copy any leftover bytes. (Leaving the current row incomplete.)
+          if (bytesToRead > 0) {
+            memcpy(row, aBuffer, bytesToRead);
+            aBuffer += bytesPerRow;
+            bytesToRead -= bytesPerRow;
+          }
 
-        // Invalidate
-        PostInvalidation(r);
+          if (mDownscaler->HasInvalidation()) {
+            DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
+            PostInvalidation(invalidRect.mOriginalSizeRect,
+                             Some(invalidRect.mTargetSizeRect));
+          }
+        } else {
+          // Copy all the bytes at once.
+          memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
+          aBuffer += bytesToRead;
+          aCount -= bytesToRead;
+          mPixBytesRead += bytesToRead;
 
-        // Book Keeping
-        aBuffer += bytesToRead;
-        aCount -= bytesToRead;
-        mPixBytesRead += bytesToRead;
+          // Invalidate. Performance isn't critical here, so our update
+          // rectangle is always the full icon.
+          PostInvalidation(IntRect(0, 0, mWidth, mHeight));
+        }
 
         // If we've got all the pixel bytes, we're finished
-        if (mPixBytesRead == mImageDataLength) {
+        if (mPixBytesRead == mExpectedDataLength) {
           PostFrameStop();
           PostDecodeDone();
           mState = iconStateFinished;
         }
         break;
       }
 
       case iconStateFinished:
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -34,29 +34,33 @@ class RasterImage;
 //
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsIconDecoder : public Decoder
 {
 public:
   virtual ~nsIconDecoder();
 
+  virtual nsresult SetTargetSize(const nsIntSize& aSize) override;
+
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsIconDecoder(RasterImage* aImage);
 
-public:
+  Maybe<Downscaler> mDownscaler;
+
+  uint32_t mExpectedDataLength;
+  uint32_t mPixBytesRead;
+  uint32_t mState;
   uint8_t mWidth;
   uint8_t mHeight;
-  uint32_t mPixBytesRead;
-  uint32_t mState;
 };
 
 enum {
   iconStateStart      = 0,
   iconStateHaveHeight = 1,
   iconStateReadPixels = 2,
   iconStateFinished   = 3
 };