Bug 1213744 - (Part 2) - Clamp the GIF frame rect to the visible rect for DDD and don't decode outside it. r=tn, a=al
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sun, 25 Oct 2015 13:14:14 -0700
changeset 298276 577862d704279c3e005d61a064030edd8dd65c2b
parent 298275 0e2d662bbd0bddda142bfdecadf01ef2d1e7ac40
child 298277 78f1c3caa41494821a560ee02ee938b3183b67c2
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, al
bugs1213744
milestone43.0a2
Bug 1213744 - (Part 2) - Clamp the GIF frame rect to the visible rect for DDD and don't decode outside it. r=tn, a=al
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
@@ -46,17 +46,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