Bug 1213744 (Part 2) - Clamp the GIF frame rect to the visible rect for DDD and don't decode outside it. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sun, 25 Oct 2015 13:14:14 -0700
changeset 304598 61d1f8675fc39e0fd1abb9828bbd30e324eb3234
parent 304597 1059ad52078ec19fdf29bfc416dbeb3ebe2c68c4
child 304599 c68de89ab7070aa6916b41568546d46fff2f5219
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1213744
milestone44.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 1213744 (Part 2) - Clamp the GIF frame rect to the visible rect for DDD and don't decode outside it. r=tn
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -202,17 +202,17 @@ nsGIFDecoder2::BeginGIF()
   }
 
   mGIFOpen = true;
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
 bool
-nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
+nsGIFDecoder2::CheckForTransparency(const IntRect& aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
   if (mGIFStruct.is_transparent) {
     PostHasTransparency();
     return true;
   }
 
   if (mGIFStruct.images_decoded > 0) {
@@ -226,16 +226,32 @@ nsGIFDecoder2::CheckForTransparency(IntR
     PostHasTransparency();
     mSawTransparency = true;  // Make sure we don't optimize it away.
     return true;
   }
 
   return false;
 }
 
+IntRect
+nsGIFDecoder2::ClampToImageRect(const IntRect& aRect)
+{
+  IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
+  IntRect visibleFrameRect = aRect.Intersect(imageRect);
+
+  // If there's no intersection, |visibleFrameRect| will be an empty rect
+  // positioned at the maximum of |imageRect|'s and |aRect|'s coordinates, which
+  // is not what we want. Force it to (0, 0) in that case.
+  if (visibleFrameRect.IsEmpty()) {
+    visibleFrameRect.MoveTo(0, 0);
+  }
+
+  return visibleFrameRect;
+}
+
 //******************************************************************************
 nsresult
 nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
   MOZ_ASSERT(HasSize());
 
   IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
                     mGIFStruct.width, mGIFStruct.height);
@@ -270,18 +286,18 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mDownscaler) {
-    rv = mDownscaler->BeginFrame(GetSize(), Some(frameRect), mImageData,
-                                 hasTransparency);
+    rv = mDownscaler->BeginFrame(GetSize(), Some(ClampToImageRect(frameRect)),
+                                 mImageData, hasTransparency);
   }
 
   return rv;
 }
 
 
 //******************************************************************************
 void
@@ -482,16 +498,19 @@ nsGIFDecoder2::OutputRow()
 //******************************************************************************
 // Perform Lempel-Ziv-Welch decoding
 bool
 nsGIFDecoder2::DoLzw(const uint8_t* q)
 {
   if (!mGIFStruct.rows_remaining) {
     return true;
   }
+  if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
+    return true;
+  }
 
   // Copy all the decoder state variables into locals so the compiler
   // won't worry about them being aliased.  The locals will be homed
   // back into the GIF decoder structure when we exit.
   int avail       = mGIFStruct.avail;
   int bits        = mGIFStruct.bits;
   int codesize    = mGIFStruct.codesize;
   int codemask    = mGIFStruct.codemask;
@@ -538,16 +557,20 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
       }
 
       // Check for explicit end-of-stream code
       if (code == (clear_code + 1)) {
         // end-of-stream should only appear after all image data
         return (mGIFStruct.rows_remaining == 0);
       }
 
+      if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
+        goto END;
+      }
+
       if (oldcode == -1) {
         if (code >= MAX_BITS) {
           return false;
         }
         *rowp++ = suffix[code] & mColorMask; // ensure index is within colormap
         if (rowp == rowend) {
           OUTPUT_ROW();
         }
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -45,17 +45,18 @@ 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);
-  bool      CheckForTransparency(gfx::IntRect aFrameRect);
+  bool      CheckForTransparency(const gfx::IntRect& aFrameRect);
+  gfx::IntRect ClampToImageRect(const 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