Bug 1249576 - If the first frame of an APNG doesn't cover the whole image mark the image as transparent. r=edwin
authorTimothy Nikkel <tnikkel@gmail.com>
Tue, 23 Feb 2016 20:22:00 +0100
changeset 285549 02a820dcf5ee106a44c71f6f33594075e2a200c9
parent 285548 b38ae7ec0fe23a94f1df75c34f56a38ff07b3789
child 285550 4c4f342da9f554cdc27aee300aa5cb1681c9dd3d
push id30031
push userkwierso@gmail.com
push dateThu, 25 Feb 2016 22:25:25 +0000
treeherdermozilla-central@97cf677ee668 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1249576
milestone47.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 1249576 - If the first frame of an APNG doesn't cover the whole image mark the image as transparent. r=edwin In (non-animated) PNGs the image data is contained in IDAT chunks. In APNGs there are IDAT chunks, which contain the default image, and fDAT chunks, which contain frames of the animation. The default image is sometimes part of the animation (as the first frame), and sometimes not (displayed only by non-APNG aware viewers). The default image must have the same size as in the PNG header chunk. But the fDAT images can be any (smaller) size. So the first frame of a PNG is allowed to be smaller than the whole image size so long as we are in an APNG and the first frame is from an fDAT chunk, not an IDAT chunk. We post transparency if we encounter this case because we don't draw into those pixels on at least the first frame.
image/decoders/nsPNGDecoder.cpp
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -128,19 +128,22 @@ void
 nsPNGDecoder::CheckForTransparency(SurfaceFormat aFormat,
                                    const IntRect& aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
   if (aFormat == SurfaceFormat::B8G8R8A8) {
     PostHasTransparency();
   }
 
-  // PNGs shouldn't have first-frame padding.
-  MOZ_ASSERT_IF(mNumFrames == 0,
-                IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect));
+  // If the first frame of animated image doesn't draw into the whole image,
+  // then record that it is transparent.
+  if (mNumFrames == 0 && !IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect)) {
+    MOZ_ASSERT(HasAnimation());
+    PostHasTransparency();
+  }
 }
 
 // CreateFrame() is used for both simple and animated images
 nsresult
 nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset,
                           int32_t aWidth, int32_t aHeight,
                           gfx::SurfaceFormat aFormat)
 {
@@ -592,16 +595,22 @@ nsPNGDecoder::info_callback(png_structp 
       MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
                              "for an animated image?");
       decoder->mDownscaler.reset();
     }
   }
 #endif
 
   if (decoder->IsMetadataDecode()) {
+    // If we are animated then the first frame rect is either: 1) the whole image
+    // if the IDAT chunk is part of the animation 2) the frame rect of the first
+    // fDAT chunk otherwise. If we are not animated then we want to make sure to
+    // call PostHasTransparency in the metadata decode if we need to. So it's okay
+    // to pass IntRect(0, 0, width, height) here for animated images; they will
+    // call with the proper first frame rect in the full decode.
     decoder->CheckForTransparency(decoder->format,
                                   IntRect(0, 0, width, height));
 
     // We have the metadata we're looking for, so we don't need to decode any
     // further.
     decoder->mSuccessfulEarlyFinish = true;
     png_longjmp(decoder->mPNG, 1);
   }