Bug 716140 - Preallocate frames before going into a decoder. r=seth
authorJoe Drew <joe@drew.ca>
Fri, 01 Feb 2013 20:06:30 -0500
changeset 125625 9c280517b6ea59bf8d25567ba7037dffcf5c731c
parent 125624 2b96c5907a1b2e4bc8614e294c8a0808eace35dd
child 125626 3cf71e7e1efc2f7f5fabd870095ad74c5b955200
push id25021
push userjdrew@mozilla.com
push dateWed, 20 Mar 2013 14:11:16 +0000
treeherdermozilla-inbound@d1f978369c50 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs716140
milestone22.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 716140 - Preallocate frames before going into a decoder. r=seth
image/decoders/nsBMPDecoder.cpp
image/decoders/nsICODecoder.cpp
image/decoders/nsIconDecoder.cpp
image/decoders/nsJPEGDecoder.cpp
image/decoders/nsWBMPDecoder.cpp
image/src/Decoder.cpp
image/src/Decoder.h
image/src/RasterImage.cpp
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -134,17 +134,21 @@ nsBMPDecoder::FinishInternal()
 
     // Send notifications if appropriate
     if (!IsSizeDecode() && (GetFrameCount() == 1)) {
 
         // Invalidate
         nsIntRect r(0, 0, mBIH.width, GetHeight());
         PostInvalidation(r);
 
-        PostFrameStop();
+        if (mUseAlphaData) {
+          PostFrameStop(RasterImage::kFrameHasAlpha);
+        } else {
+          PostFrameStop(RasterImage::kFrameOpaque);
+        }
         PostDecodeDone();
     }
 }
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
 
@@ -188,17 +192,16 @@ void
 nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
     NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
 
     // aCount=0 means EOF, mCurLine=0 means we're past end of image
     if (!aCount || !mCurLine)
         return;
 
-    nsresult rv;
     if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
         uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos;
         if (toCopy > aCount)
             toCopy = aCount;
         memcpy(mRawBuf + mPos, aBuffer, toCopy);
         mPos += toCopy;
         aCount -= toCopy;
         aBuffer += toCopy;
@@ -303,56 +306,38 @@ nsBMPDecoder::WriteInternal(const char* 
         if (mBIH.compression == BI_ALPHABITFIELDS && 
             mBIH.bpp != 16 && mBIH.bpp != 32) {
           PR_LOG(GetBMPLog(), PR_LOG_DEBUG, 
                  ("BMP ALPHABITFIELDS only supports 16 or 32 bits per pixel\n"));
           PostDataError();
           return;
         }
 
-        uint32_t imageLength;
-        if (mBIH.compression == BI_RLE8 || mBIH.compression == BI_RLE4 || 
-            mBIH.compression == BI_ALPHABITFIELDS) {
-            rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height, 
-                                    gfxASurface::ImageFormatARGB32,
-                                    (uint8_t**)&mImageData, &imageLength);
-        } else {
+        if (mBIH.compression != BI_RLE8 && mBIH.compression != BI_RLE4 &&
+            mBIH.compression != BI_ALPHABITFIELDS) {
             // mRow is not used for RLE encoded images
             mRow = (uint8_t*)moz_malloc((mBIH.width * mBIH.bpp) / 8 + 4);
             // + 4 because the line is padded to a 4 bit boundary, but I don't want
             // to make exact calculations here, that's unnecessary.
             // Also, it compensates rounding error.
             if (!mRow) {
-                PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
-                return;
-            }
-
-            if (mUseAlphaData) {
-              rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height, 
-                                      gfxASurface::ImageFormatARGB32,
-                                      (uint8_t**)&mImageData, &imageLength);
-            } else {
-              rv = mImage.EnsureFrame(0, 0, 0, mBIH.width, real_height, 
-                                      gfxASurface::ImageFormatRGB24,
-                                      (uint8_t**)&mImageData, &imageLength);
+              PostDataError();
+              return;
             }
         }
-        if (NS_FAILED(rv) || !mImageData) {
+        if (!mImageData) {
             PostDecoderError(NS_ERROR_FAILURE);
             return;
         }
 
         // Prepare for transparency
         if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
             // Clear the image, as the RLE may jump over areas
-            memset(mImageData, 0, imageLength);
+            memset(mImageData, 0, mImageDataLength);
         }
-
-        // Tell the superclass we're starting a frame
-        PostFrameStart();
     }
 
     if (mColors && mPos >= mLOH) {
       // OS/2 Bitmaps have no padding byte
       uint8_t bytesPerColor = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4;
       if (mPos < (mLOH + mNumColors * bytesPerColor)) {
         // Number of bytes already received
         uint32_t colorBytes = mPos - mLOH; 
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -310,17 +310,18 @@ nsICODecoder::WriteInternal(const char* 
     aBuffer += toCopy;
 
     mIsPNG = !memcmp(mSignature, nsPNGDecoder::pngSignatureBytes, 
                      PNGSIGNATURESIZE);
     if (mIsPNG) {
       mContainedDecoder = new nsPNGDecoder(mImage);
       mContainedDecoder->SetObserver(mObserver);
       mContainedDecoder->SetSizeDecode(IsSizeDecode());
-      mContainedDecoder->InitSharedDecoder();
+      mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
+                                           mColormap, mColormapSize);
       if (!WriteToContainedDecoder(mSignature, PNGSIGNATURESIZE)) {
         return;
       }
     }
   }
 
   // If we have a PNG, let the PNG decoder do all of the rest of the work
   if (mIsPNG && mContainedDecoder && mPos >= mImageOffset + PNGSIGNATURESIZE) {
@@ -379,17 +380,18 @@ nsICODecoder::WriteInternal(const char* 
     // Init the bitmap decoder which will do most of the work for us
     // It will do everything except the AND mask which isn't present in bitmaps
     // bmpDecoder is for local scope ease, it will be freed by mContainedDecoder
     nsBMPDecoder *bmpDecoder = new nsBMPDecoder(mImage);
     mContainedDecoder = bmpDecoder;
     bmpDecoder->SetUseAlphaData(true);
     mContainedDecoder->SetObserver(mObserver);
     mContainedDecoder->SetSizeDecode(IsSizeDecode());
-    mContainedDecoder->InitSharedDecoder();
+    mContainedDecoder->InitSharedDecoder(mImageData, mImageDataLength,
+                                         mColormap, mColormapSize);
 
     // The ICO format when containing a BMP does not include the 14 byte
     // bitmap file header. To use the code of the BMP decoder we need to 
     // generate this header ourselves and feed it to the BMP decoder.
     int8_t bfhBuffer[BMPFILEHEADERSIZE];
     if (!FillBitmapFileHeaderBuffer(bfhBuffer)) {
       PostDataError();
       return;
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -32,17 +32,16 @@ nsIconDecoder::~nsIconDecoder()
 void
 nsIconDecoder::WriteInternal(const char *aBuffer, uint32_t aCount)
 {
   NS_ABORT_IF_FALSE(!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;
-  nsresult rv;
 
   // Loop until the input data is gone
   while (aCount > 0) {
     switch (mState) {
       case iconStateStart:
 
         // Grab the width
         mWidth = (uint8_t)*aBuffer;
@@ -67,28 +66,21 @@ nsIconDecoder::WriteInternal(const char 
         }
 
         // If We're doing a size decode, we're done
         if (IsSizeDecode()) {
           mState = iconStateFinished;
           break;
         }
 
-        // Add the frame and signal
-        rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
-                                gfxASurface::ImageFormatARGB32,
-                                &mImageData, &mImageDataLength);
-        if (NS_FAILED(rv)) {
-          PostDecoderError(rv);
+        if (!mImageData) {
+          PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
           return;
         }
 
-        // Tell the superclass we're starting a frame
-        PostFrameStart();
-
         // Book Keeping
         aBuffer++;
         aCount--;
         mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels:
       {
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -366,34 +366,28 @@ nsJPEGDecoder::WriteInternal(const char 
      * Don't allocate a giant and superfluous memory buffer
      * when not doing a progressive decode.
      */
     mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo);
 
     /* Used to set up image size so arrays can be allocated */
     jpeg_calc_output_dimensions(&mInfo);
 
-    uint32_t imagelength;
-    if (NS_FAILED(mImage.EnsureFrame(0, 0, 0, mInfo.image_width, mInfo.image_height,
-                                     gfxASurface::ImageFormatRGB24,
-                                     &mImageData, &imagelength))) {
+    if (!mImageData) {
       mState = JPEG_ERROR;
       PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
       PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
              ("} (could not initialize image frame)"));
       return;
     }
 
     PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
            ("        JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels",
             mInfo.image_width, mInfo.image_height));
 
-    // Tell the superclass we're starting a frame
-    PostFrameStart();
-
     mState = JPEG_START_DECOMPRESS;
   }
 
   case JPEG_START_DECOMPRESS:
   {
     LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case");
     /* Step 4: set parameters for decompression */
 
@@ -537,17 +531,17 @@ nsJPEGDecoder::WriteInternal(const char 
   PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
          ("} (end of function)"));
   return;
 }
 
 void
 nsJPEGDecoder::NotifyDone()
 {
-  PostFrameStop();
+  PostFrameStop(RasterImage::kFrameOpaque);
   PostDecodeDone();
 }
 
 void
 nsJPEGDecoder::OutputScanlines(bool* suspend)
 {
   *suspend = false;
 
--- a/image/decoders/nsWBMPDecoder.cpp
+++ b/image/decoders/nsWBMPDecoder.cpp
@@ -169,38 +169,30 @@ nsWBMPDecoder::WriteInternal(const char 
           }
 
           // If We're doing a size decode, we're done
           if (IsSizeDecode()) {
             mState = WbmpStateFinished;
             return;
           }
 
-          // Add the frame and signal
-          nsresult rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
-                                           gfxASurface::ImageFormatRGB24,
-                                           (uint8_t**)&mImageData, &mImageDataLength);
-
-          if (NS_FAILED(rv) || !mImageData) {
+          if (!mImageData) {
             PostDecoderError(NS_ERROR_FAILURE);
             mState = DecodingFailed;
             return;
           }
 
           // Create mRow, the buffer that holds one line of the raw image data
           mRow = (uint8_t*)moz_malloc((mWidth + 7) / 8);
           if (!mRow) {
             PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
             mState = DecodingFailed;
             return;
           }
 
-          // Tell the superclass we're starting a frame
-          PostFrameStart();
-
           mState = DecodingImageData;
 
         } else if (heightReadResult == IntParseFailed) {
           // Encoded height was bigger than a uint32_t.
           PostDataError();
           mState = DecodingFailed;
           return;
         } else {
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -47,26 +47,34 @@ Decoder::Init()
   NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
 
   // Fire OnStartDecode at init time to support bug 512435.
   if (!IsSizeDecode())
       mObserver->OnStartDecode();
 
   // Implementation-specific initialization
   InitInternal();
+
   mInitialized = true;
 }
 
 // Initializes a decoder whose image and observer is already being used by a
 // parent decoder
 void
-Decoder::InitSharedDecoder()
+Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
+                           uint32_t* colormap, uint32_t colormapSize)
 {
   // No re-initializing
   NS_ABORT_IF_FALSE(!mInitialized, "Can't re-initialize a decoder!");
+  NS_ABORT_IF_FALSE(mObserver, "Need an observer!");
+
+  mImageData = imageData;
+  mImageDataLength = imageDataLength;
+  mColormap = colormap;
+  mColormapSize = colormapSize;
 
   // Implementation-specific initialization
   InitInternal();
   mInitialized = true;
 }
 
 void
 Decoder::Write(const char* aBuffer, uint32_t aCount)
@@ -76,45 +84,36 @@ Decoder::Write(const char* aBuffer, uint
   // We're strict about decoder errors
   NS_ABORT_IF_FALSE(!HasDecoderError(),
                     "Not allowed to make more decoder calls after error!");
 
   // If a data error occured, just ignore future data
   if (HasDataError())
     return;
 
+  nsresult rv = NS_OK;
+
+  // Preallocate a frame if we've been asked to.
+  if (mNeedsNewFrame) {
+    rv = AllocateFrame();
+    if (NS_FAILED(rv)) {
+      PostDataError();
+      return;
+    }
+  }
+
   // Pass the data along to the implementation
   WriteInternal(aBuffer, aCount);
 
   // If the decoder told us that it needs a new frame to proceed, let's create
   // one and call it again.
   while (mNeedsNewFrame && !HasDataError()) {
-    nsresult rv;
-    if (mNewFrameData.mPaletteDepth) {
-      rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
-                              mNewFrameData.mOffsetY, mNewFrameData.mWidth,
-                              mNewFrameData.mHeight, mNewFrameData.mFormat,
-                              mNewFrameData.mPaletteDepth,
-                              &mImageData, &mImageDataLength,
-                              &mColormap, &mColormapSize);
-    } else {
-      rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
-                              mNewFrameData.mOffsetY, mNewFrameData.mWidth,
-                              mNewFrameData.mHeight, mNewFrameData.mFormat,
-                              &mImageData, &mImageDataLength);
-    }
-
-    // Release our new frame data before talking to anyone else so they can
-    // tell us if they need yet another.
-    mNeedsNewFrame = false;
+    nsresult rv = AllocateFrame();
 
     if (NS_SUCCEEDED(rv)) {
-      // We've now created our frame, so be sure we keep track of it correctly.
-      PostFrameStart();
-
       // Tell the decoder to use the data it saved when it asked for a new frame.
       WriteInternal(nullptr, 0);
     } else {
       PostDataError();
       break;
     }
   }
 }
@@ -184,16 +183,47 @@ Decoder::Finish(RasterImage::eShutdownIn
 void
 Decoder::FinishSharedDecoder()
 {
   if (!HasError()) {
     FinishInternal();
   }
 }
 
+nsresult
+Decoder::AllocateFrame()
+{
+  MOZ_ASSERT(mNeedsNewFrame);
+
+  nsresult rv;
+  if (mNewFrameData.mPaletteDepth) {
+    rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
+                            mNewFrameData.mOffsetY, mNewFrameData.mWidth,
+                            mNewFrameData.mHeight, mNewFrameData.mFormat,
+                            mNewFrameData.mPaletteDepth,
+                            &mImageData, &mImageDataLength,
+                            &mColormap, &mColormapSize);
+  } else {
+    rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
+                            mNewFrameData.mOffsetY, mNewFrameData.mWidth,
+                            mNewFrameData.mHeight, mNewFrameData.mFormat,
+                            &mImageData, &mImageDataLength);
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    PostFrameStart();
+  }
+
+  // Mark ourselves as not needing another frame before talking to anyone else
+  // so they can tell us if they need yet another.
+  mNeedsNewFrame = false;
+
+  return rv;
+}
+
 void
 Decoder::FlushInvalidations()
 {
   NS_ABORT_IF_FALSE(!HasDecoderError(),
                     "Not allowed to make more decoder calls after error!");
 
   // If we've got an empty invalidation rect, we have nothing to do
   if (mInvalidRect.IsEmpty())
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -29,17 +29,18 @@ public:
   void Init();
 
   /**
    * Initializes a decoder whose image and observer is already being used by a
    * parent decoder. Decoders may not be re-initialized.
    *
    * Notifications Sent: TODO
    */
-  void InitSharedDecoder();
+  void InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
+                         uint32_t* colormap, uint32_t colormapSize);
 
   /**
    * Writes data to the decoder.
    *
    * @param aBuffer buffer containing the data to be written
    * @param aCount the number of bytes to write
    *
    * Any errors are reported by setting the appropriate state on the decoder.
@@ -129,16 +130,28 @@ public:
   void SetDecodeFlags(uint32_t aFlags) { mDecodeFlags = aFlags; }
   uint32_t GetDecodeFlags() { return mDecodeFlags; }
 
   // Use HistogramCount as an invalid Histogram ID
   virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
 
   ImageMetadata& GetImageMetadata() { return mImageMetadata; }
 
+  // Tell the decoder infrastructure to allocate a frame. By default, frame 0
+  // is created as an ARGB frame with no offset and with size width * height.
+  // If decoders need something different, they must ask for it.
+  // This is called by decoders when they need a new frame. These decoders
+  // must then save the data they have been sent but not yet processed and
+  // return from WriteInternal. When the new frame is created, WriteInternal
+  // will be called again with nullptr and 0 as arguments.
+  void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset,
+                    uint32_t width, uint32_t height,
+                    gfxASurface::gfxImageFormat format,
+                    uint8_t palette_depth = 0);
+
 protected:
 
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    */
   virtual void InitInternal();
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
@@ -179,24 +192,19 @@ protected:
   // For animated images, specify the loop count. -1 means loop forever, 0
   // means a single iteration, stopping on the last frame.
   void PostDecodeDone(int32_t aLoopCount = 0);
 
   // Data errors are the fault of the source data, decoder errors are our fault
   void PostDataError();
   void PostDecoderError(nsresult aFailCode);
 
-  // This is called by decoders when they need a new frame. These decoders
-  // must then save the data they have been sent but not yet processed and
-  // return from WriteInternal. When the new frame is created, WriteInternal
-  // will be called again with nullptr and 0 as arguments.
-  void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset,
-                    uint32_t width, uint32_t height,
-                    gfxASurface::gfxImageFormat format,
-                    uint8_t palette_depth = 0);
+  // Try to allocate a frame as described in mNewFrameData and return the
+  // status code from that attempt. Clears mNewFrameData.
+  nsresult AllocateFrame();
 
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
   RefPtr<imgDecoderObserver> mObserver;
   ImageMetadata mImageMetadata;
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -2613,16 +2613,23 @@ RasterImage::InitDecoder(bool aDoSizeDec
 
   // Initialize the decoder
   if (!mDecodeRequest) {
     mDecodeRequest = new DecodeRequest(this);
   }
   mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver());
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
+  if (!aDoSizeDecode) {
+    // 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.
+    mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
+                           gfxASurface::ImageFormatARGB32);
+  }
   mDecoder->Init();
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   if (!aDoSizeDecode) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);