Bug 1207378 (Part 2) - Use Downscaler to remove first-frame padding when downscaling GIFs. r=tn,a=lizzard
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sat, 26 Sep 2015 01:36:23 -0700
changeset 296221 9afad3de5ea3f59970d33196db59ce66ca177f16
parent 296220 d6cad20ab005798b1c6af52e488508561b1d93d8
child 296222 5426b69fd7258c1454ad879ea80d12b28d293808
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, lizzard
bugs1207378
milestone43.0a2
Bug 1207378 (Part 2) - Use Downscaler to remove first-frame padding when downscaling GIFs. r=tn,a=lizzard
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -215,68 +215,64 @@ nsGIFDecoder2::BeginGIF()
     return;
   }
 
   mGIFOpen = true;
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
-void
+bool
 nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
   if (mGIFStruct.is_transparent) {
     PostHasTransparency();
-    return;
+    return true;
   }
 
   if (mGIFStruct.images_decoded > 0) {
-    return;  // We only care about first frame padding below.
+    return false;  // We only care about first frame padding below.
   }
 
   // If we need padding on the first frame, that means we don't draw into part
   // of the image at all. Report that as transparency.
   IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
   if (!imageRect.IsEqualEdges(aFrameRect)) {
     PostHasTransparency();
+    mSawTransparency = true;  // Make sure we don't optimize it away.
+    return true;
   }
+
+  return false;
 }
 
 //******************************************************************************
 nsresult
 nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
   MOZ_ASSERT(HasSize());
 
-  gfx::SurfaceFormat format;
-  if (mGIFStruct.is_transparent) {
-    format = gfx::SurfaceFormat::B8G8R8A8;
-  } else {
-    format = gfx::SurfaceFormat::B8G8R8X8;
-  }
-
   IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
                     mGIFStruct.width, mGIFStruct.height);
 
-  CheckForTransparency(frameRect);
+  bool hasTransparency = CheckForTransparency(frameRect);
+  gfx::SurfaceFormat format = hasTransparency ? SurfaceFormat::B8G8R8A8
+                                              : SurfaceFormat::B8G8R8X8;
 
   // Make sure there's no animation if we're downscaling.
   MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
 
+  // Compute the target size and target frame rect. If we're downscaling,
+  // Downscaler will automatically strip out first-frame padding, so the target
+  // frame rect takes up the entire frame regardless.
   IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
                                    : GetSize();
-
-  // Rescale the frame rect for the target size.
-  IntRect targetFrameRect = frameRect;
-  if (mDownscaler) {
-    IntSize originalSize = GetSize();
-    targetFrameRect.ScaleRoundOut(double(targetSize.width) / originalSize.width,
-                                  double(targetSize.height) / originalSize.height);
-  }
+  IntRect targetFrameRect = mDownscaler ? IntRect(IntPoint(), targetSize)
+                                        : frameRect;
 
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   nsresult rv = NS_OK;
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette.
     rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
                        targetFrameRect, format, aDepth);
@@ -288,18 +284,18 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mDownscaler) {
-    rv = mDownscaler->BeginFrame(frameRect.Size(), Nothing(), mImageData,
-                                 mGIFStruct.is_transparent);
+    rv = mDownscaler->BeginFrame(GetSize(), Some(frameRect), mImageData,
+                                 hasTransparency);
   }
 
   return rv;
 }
 
 
 //******************************************************************************
 void
@@ -327,17 +323,17 @@ nsGIFDecoder2::EndImageFrame()
         PostInvalidation(r);
       }
     }
 
     // The first frame was preallocated with alpha; if it wasn't transparent, we
     // should fix that. We can also mark it opaque unconditionally if we didn't
     // actually see any transparent pixels - this test is only valid for the
     // first frame.
-    if (!mGIFStruct.is_transparent || !mSawTransparency) {
+    if (!mGIFStruct.is_transparent && !mSawTransparency) {
       opacity = Opacity::OPAQUE;
     }
   }
   mCurrentRow = mLastFlushedRow = -1;
   mCurrentPass = mLastFlushedPass = 0;
 
   // Only add frame if we have any rows at all
   if (mGIFStruct.rows_remaining != mGIFStruct.height) {
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -48,17 +48,17 @@ private:
   void      FlushImageData();
   void      FlushImageData(uint32_t fromRow, uint32_t rows);
 
   nsresult  GifWrite(const uint8_t* buf, uint32_t numbytes);
   uint32_t  OutputRow();
   bool      DoLzw(const uint8_t* q);
   bool      SetHold(const uint8_t* buf, uint32_t count,
                     const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
-  void      CheckForTransparency(gfx::IntRect aFrameRect);
+  bool      CheckForTransparency(gfx::IntRect aFrameRect);
 
   inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
 
   int32_t mCurrentRow;
   int32_t mLastFlushedRow;
 
   uint32_t mOldColor;        // The old value of the transparent pixel