Bug 1295506 - Don't yield hidden frames in the PNG decoder. r=edwin
authorSeth Fowler <mark.seth.fowler@gmail.com>
Tue, 16 Aug 2016 01:08:39 -0700
changeset 309922 9c25eb4d65d3e80886b8ac95883fe9ae5f7875f6
parent 309921 3a89d0ddb218ec4a41061417589790a19ab04de9
child 309923 f9c695f0f3687ba794c3fe08159a16e6398086e5
push id80709
push userseth.bugzilla@blackhail.net
push dateThu, 18 Aug 2016 05:19:49 +0000
treeherdermozilla-inbound@9c25eb4d65d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1295506
milestone51.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 1295506 - Don't yield hidden frames in the PNG decoder. r=edwin
image/decoders/nsPNGDecoder.cpp
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -951,48 +951,60 @@ void
 nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
 {
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // old frame is done
   decoder->EndImageFrame();
 
-  if (!decoder->mFrameIsHidden && decoder->IsFirstFrameDecode()) {
+  const bool previousFrameWasHidden = decoder->mFrameIsHidden;
+
+  if (!previousFrameWasHidden && decoder->IsFirstFrameDecode()) {
     // We're about to get a second non-hidden frame, but we only want the first.
     // Stop decoding now. (And avoid allocating the unnecessary buffers below.)
     decoder->PostDecodeDone();
     return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
   }
 
   // Only the first frame can be hidden, so unhide unconditionally here.
   decoder->mFrameIsHidden = false;
 
   // Save the information necessary to create the frame; we'll actually create
   // it when we return from the yield.
   const IntRect frameRect(png_get_next_frame_x_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_y_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_width(png_ptr, decoder->mInfo),
                           png_get_next_frame_height(png_ptr, decoder->mInfo));
+  const bool isInterlaced = bool(decoder->interlacebuf);
 
 #ifndef PNGLCONF_H
   // if using system library, check frame_width and height against 0
   if (frameRect.width == 0)
     png_error(png_ptr, "Frame width must not be 0");
   if (frameRect.height == 0)
     png_error(png_ptr, "Frame height must not be 0");
 #endif
 
-  const bool isInterlaced = bool(decoder->interlacebuf);
+  const FrameInfo info { decoder->format, frameRect, isInterlaced };
 
-  decoder->mNextFrameInfo = Some(FrameInfo{ decoder->format,
-                                            frameRect,
-                                            isInterlaced });
+  // If the previous frame was hidden, skip the yield (which will mislead the
+  // caller, who will think the previous frame was real) and just allocate the
+  // new frame here.
+  if (previousFrameWasHidden) {
+    if (NS_FAILED(decoder->CreateFrame(info))) {
+      return decoder->DoTerminate(png_ptr, TerminalState::FAILURE);
+    }
+
+    MOZ_ASSERT(decoder->mImageData, "Should have a buffer now");
+    return;  // No yield, so we'll just keep decoding.
+  }
 
   // Yield to the caller to notify them that the previous frame is now complete.
+  decoder->mNextFrameInfo = Some(info);
   return decoder->DoYield(png_ptr);
 }
 #endif
 
 void
 nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr)
 {
   /* libpng comments: