Cherry-pick: cc: Add invalidations when shrinking piles

Cherry-pick of chromium
crrev.com/2c8b61a4db617427448947ff740ba002da1a9644

Conflicts:
	cc/resources/picture_pile_unittest.cc

BUG: 17965814

Original description:

Previously, there was a hack/optimization where when shrinking a pile,
there would be no invalidations.  Unfortunately, this hack's time has
come due.  When zooming out, recorded pixels outside of the pile's newly
shrunken bounds can get used, causing pixels from old frames to get
incorrectly sampled, causing bogus hairlines.

To fix this, issue invalidations for previously exposed content in the
same way that invalidations are currently issues for newly exposed
content.

Change-Id: I3287e7e2261328a37781bf58da97ac4b7e60cb73
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index ae2f8af..71a421d 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -189,21 +189,30 @@
   gfx::Rect interest_rect_over_tiles =
       tiling_.ExpandRectToTileBounds(interest_rect);
 
+  gfx::Size min_tiling_size(
+      std::min(tiling_size().width(), old_tiling_size.width()),
+      std::min(tiling_size().height(), old_tiling_size.height()));
+  gfx::Size max_tiling_size(
+      std::max(tiling_size().width(), old_tiling_size.width()),
+      std::max(tiling_size().height(), old_tiling_size.height()));
+
   if (old_tiling_size != layer_size) {
     has_any_recordings_ = false;
 
-    // Drop recordings that are outside the new layer bounds or that changed
-    // size.
+    // Drop recordings that are outside the new or old layer bounds or that
+    // changed size.  Newly exposed areas are considered invalidated.
+    // Previously exposed areas that are now outside of bounds also need to
+    // be invalidated, as they may become part of raster when scale < 1.
     std::vector<PictureMapKey> to_erase;
     int min_toss_x = tiling_.num_tiles_x();
-    if (tiling_size().width() > old_tiling_size.width()) {
+    if (max_tiling_size.width() > min_tiling_size.width()) {
       min_toss_x =
-          tiling_.FirstBorderTileXIndexFromSrcCoord(old_tiling_size.width());
+          tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width());
     }
     int min_toss_y = tiling_.num_tiles_y();
-    if (tiling_size().height() > old_tiling_size.height()) {
+    if (max_tiling_size.height() > min_tiling_size.height()) {
       min_toss_y =
-          tiling_.FirstBorderTileYIndexFromSrcCoord(old_tiling_size.height());
+          tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height());
     }
     for (PictureMap::const_iterator it = picture_map_.begin();
          it != picture_map_.end();
@@ -222,20 +231,22 @@
     // If a recording is dropped and not re-recorded below, invalidate that
     // full recording to cause any raster tiles that would use it to be
     // dropped.
-    // If the recording will be replaced below, just invalidate newly exposed
-    // areas to force raster tiles that include the old recording to know
-    // there is new recording to display.
-    gfx::Rect old_tiling_rect_over_tiles =
-        tiling_.ExpandRectToTileBounds(gfx::Rect(old_tiling_size));
+    // If the recording will be replaced below, invalidate newly exposed
+    // areas and previously exposed areas to force raster tiles that include the
+    // old recording to know there is new recording to display.
+    gfx::Rect min_tiling_rect_over_tiles =
+        tiling_.ExpandRectToTileBounds(gfx::Rect(min_tiling_size));
     if (min_toss_x < tiling_.num_tiles_x()) {
       // The bounds which we want to invalidate are the tiles along the old
-      // edge of the pile. We'll call this bounding box the OLD EDGE RECT.
+      // edge of the pile when expanding, or the new edge of the pile when
+      // shrinking. In either case, it's the difference of the two, so we'll
+      // call this bounding box the DELTA EDGE RECT.
       //
-      // In the picture below, the old edge rect would be the bounding box
-      // of tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index
-      // of the same tiles.
+      // In the picture below, the delta edge rect would be the bounding box of
+      // tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index of
+      // the same tiles.
       //
-      //  old pile edge-v  new pile edge-v
+      //  min pile edge-v  max pile edge-v
       // ---------------+ - - - - - - - -+
       // mmppssvvyybbeeh|h               .
       // mmppssvvyybbeeh|h               .
@@ -243,33 +254,33 @@
       // nnqqttwwzzccffi|i               .
       // oorruuxxaaddggj|j               .
       // oorruuxxaaddggj|j               .
-      // ---------------+ - - - - - - - -+ <- old pile edge
+      // ---------------+ - - - - - - - -+ <- min pile edge
       //                                 .
-      //  - - - - - - - - - - - - - - - -+ <- new pile edge
+      //  - - - - - - - - - - - - - - - -+ <- max pile edge
       //
       // If you were to slide a vertical beam from the left edge of the
-      // old edge rect toward the right, it would either hit the right edge
-      // of the old edge rect, or the interest rect (expanded to the bounds
+      // delta edge rect toward the right, it would either hit the right edge
+      // of the delta edge rect, or the interest rect (expanded to the bounds
       // of the tiles it touches). The same is true for a beam parallel to
-      // any of the four edges, sliding accross the old edge rect. We use
+      // any of the four edges, sliding across the delta edge rect. We use
       // the union of these four rectangles generated by these beams to
-      // determine which part of the old edge rect is outside of the expanded
+      // determine which part of the delta edge rect is outside of the expanded
       // interest rect.
       //
-      // Case 1: Intersect rect is outside the old edge rect. It can be
+      // Case 1: Intersect rect is outside the delta edge rect. It can be
       // either on the left or the right. The |left_rect| and |right_rect|,
       // cover this case, one will be empty and one will cover the full
-      // old edge rect. In the picture below, |left_rect| would cover the
-      // old edge rect, and |right_rect| would be empty.
+      // delta edge rect. In the picture below, |left_rect| would cover the
+      // delta edge rect, and |right_rect| would be empty.
       // +----------------------+ |^^^^^^^^^^^^^^^|
-      // |===>   OLD EDGE RECT  | |               |
+      // |===> DELTA EDGE RECT  | |               |
       // |===>                  | | INTEREST RECT |
       // |===>                  | |               |
       // |===>                  | |               |
       // +----------------------+ |vvvvvvvvvvvvvvv|
       //
-      // Case 2: Interest rect is inside the old edge rect. It will always
-      // fill the entire old edge rect horizontally since the old edge rect
+      // Case 2: Interest rect is inside the delta edge rect. It will always
+      // fill the entire delta edge rect horizontally since the old edge rect
       // is a single tile wide, and the interest rect has been expanded to the
       // bounds of the tiles it touches. In this case the |left_rect| and
       // |right_rect| will be empty, but the case is handled by the |top_rect|
@@ -286,19 +297,19 @@
       // |                 |
       // +-----------------+
       // |                 |
-      // | OLD EDGE RECT   |
+      // | DELTA EDGE RECT |
       // +-----------------+
       //
       // Lastly, we need to consider tiles inside the expanded interest rect.
       // For those tiles, we want to invalidate exactly the newly exposed
-      // pixels. In the picture below the tiles in the old edge rect have been
-      // resized and the area covered by periods must be invalidated. The
+      // pixels. In the picture below the tiles in the delta edge rect have
+      // been resized and the area covered by periods must be invalidated. The
       // |exposed_rect| will cover exactly that area.
-      //           v-old pile edge
+      //           v-min pile edge
       // +---------+-------+
       // |         ........|
       // |         ........|
-      // |  OLD EDGE.RECT..|
+      // | DELTA EDGE.RECT.|
       // |         ........|
       // |         ........|
       // |         ........|
@@ -309,18 +320,18 @@
 
       int left = tiling_.TilePositionX(min_toss_x);
       int right = left + tiling_.TileSizeX(min_toss_x);
-      int top = old_tiling_rect_over_tiles.y();
-      int bottom = old_tiling_rect_over_tiles.bottom();
+      int top = min_tiling_rect_over_tiles.y();
+      int bottom = min_tiling_rect_over_tiles.bottom();
 
       int left_until = std::min(interest_rect_over_tiles.x(), right);
       int right_until = std::max(interest_rect_over_tiles.right(), left);
       int top_until = std::min(interest_rect_over_tiles.y(), bottom);
       int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
 
-      int exposed_left = old_tiling_size.width();
-      int exposed_left_until = tiling_size().width();
+      int exposed_left = min_tiling_size.width();
+      int exposed_left_until = max_tiling_size.width();
       int exposed_top = top;
-      int exposed_bottom = tiling_size().height();
+      int exposed_bottom = max_tiling_size.height();
       DCHECK_GE(exposed_left, left);
 
       gfx::Rect left_rect(left, top, left_until - left, bottom - top);
@@ -340,23 +351,23 @@
     }
     if (min_toss_y < tiling_.num_tiles_y()) {
       // The same thing occurs here as in the case above, but the invalidation
-      // rect is the bounding box around the bottom row of tiles in the old
+      // rect is the bounding box around the bottom row of tiles in the min
       // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture.
 
       int top = tiling_.TilePositionY(min_toss_y);
       int bottom = top + tiling_.TileSizeY(min_toss_y);
-      int left = old_tiling_rect_over_tiles.x();
-      int right = old_tiling_rect_over_tiles.right();
+      int left = min_tiling_rect_over_tiles.x();
+      int right = min_tiling_rect_over_tiles.right();
 
       int top_until = std::min(interest_rect_over_tiles.y(), bottom);
       int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
       int left_until = std::min(interest_rect_over_tiles.x(), right);
       int right_until = std::max(interest_rect_over_tiles.right(), left);
 
-      int exposed_top = old_tiling_size.height();
-      int exposed_top_until = tiling_size().height();
+      int exposed_top = min_tiling_size.height();
+      int exposed_top_until = max_tiling_size.height();
       int exposed_left = left;
-      int exposed_right = tiling_size().width();
+      int exposed_right = max_tiling_size.width();
       DCHECK_GE(exposed_top, top);
 
       gfx::Rect left_rect(left, top, left_until - left, bottom - top);
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index 5143b44..b2f0aae 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -655,12 +655,49 @@
       TestPicturePile::PictureMapKey key(i, j);
       TestPicturePile::PictureMap& map = pile_->picture_map();
       TestPicturePile::PictureMap::iterator it = map.find(key);
-      EXPECT_EQ(j < 6, it != map.end() && it->second.GetPicture());
+      bool expect_tile;
+      switch (corner) {
+        case TOP_LEFT:
+        case TOP_RIGHT:
+          expect_tile = j < 5;
+          break;
+        case BOTTOM_LEFT:
+          // The interest rect in the bottom left tile means we'll record it.
+          expect_tile = j < 5 || (j == 5 && i == 0);
+          break;
+        case BOTTOM_RIGHT:
+          // The interest rect in the bottom right tile means we'll record it.
+          expect_tile = j < 5 || (j == 5 && i == 5);
+          break;
+      }
+      EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
     }
   }
 
-  // No invalidation when shrinking.
-  expected_invalidation.Clear();
+  // When shrinking, the previously exposed region is invalidated.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  // The whole bottom row of tiles (except any with the interest rect) are
+  // dropped.
+  gfx::Rect bottom_row_minus_existing_corner = gfx::UnionRects(
+      pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5));
+  switch (corner) {
+    case TOP_LEFT:
+    case TOP_RIGHT:
+      // No tiles are kept in the changed region because it doesn't
+      // intersect with the interest rect.
+      break;
+    case BOTTOM_LEFT:
+      bottom_row_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(0, 5));
+      break;
+    case BOTTOM_RIGHT:
+      bottom_row_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(5, 5));
+      break;
+  }
+
+  expected_invalidation.Union(bottom_row_minus_existing_corner);
   EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
@@ -707,12 +744,48 @@
       TestPicturePile::PictureMapKey key(i, j);
       TestPicturePile::PictureMap& map = pile_->picture_map();
       TestPicturePile::PictureMap::iterator it = map.find(key);
-      EXPECT_EQ(i < 6, it != map.end() && it->second.GetPicture());
+      bool expect_tile;
+      switch (corner) {
+        case TOP_LEFT:
+        case BOTTOM_LEFT:
+          // No tiles are kept in the changed region because it doesn't
+          // intersect with the interest rect.
+          expect_tile = i < 5;
+          break;
+        case TOP_RIGHT:
+          // The interest rect in the top right tile means we'll record it.
+          expect_tile = i < 5 || (j == 0 && i == 5);
+          break;
+        case BOTTOM_RIGHT:
+          // The interest rect in the bottom right tile means we'll record it.
+          expect_tile = i < 5 || (j == 5 && i == 5);
+          break;
+      }
+      EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
     }
   }
 
-  // No invalidation when shrinking.
-  expected_invalidation.Clear();
+  // When shrinking, the previously exposed region is invalidated.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  // The whole right column of tiles (except for ones with the interest rect)
+  // are dropped.
+  gfx::Rect right_column_minus_existing_corner = gfx::UnionRects(
+      pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+  switch (corner) {
+    case TOP_LEFT:
+    case BOTTOM_LEFT:
+      break;
+    case TOP_RIGHT:
+      right_column_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(5, 0));
+      break;
+    case BOTTOM_RIGHT:
+      right_column_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(5, 5));
+      break;
+  }
+  expected_invalidation.Union(right_column_minus_existing_corner);
   EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
@@ -737,7 +810,7 @@
   // We invalidated all new pixels in the recording.
   expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
                                           gfx::Rect(base_tiling_size));
-  // But the new pixels don't cover the whole right_column.
+  // But the new pixels don't cover the whole right column or bottom row.
   Region right_column_and_bottom_row =
       UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
                                    pile_->tiling().TileBounds(5, 5)),
@@ -750,7 +823,8 @@
   invalidation.Clear();
 
   UpdateWholePile();
-  UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect());
+  UpdateAndExpandInvalidation(&invalidation, base_tiling_size,
+                              CornerSinglePixelRect(corner, base_tiling_size));
 
   // We should have lost the recordings that are now outside the tiling only.
   EXPECT_EQ(6, pile_->tiling().num_tiles_x());
@@ -760,12 +834,54 @@
       TestPicturePile::PictureMapKey key(i, j);
       TestPicturePile::PictureMap& map = pile_->picture_map();
       TestPicturePile::PictureMap::iterator it = map.find(key);
-      EXPECT_EQ(i < 6 && j < 6, it != map.end() && it->second.GetPicture());
+      bool expect_tile;
+      switch (corner) {
+        case TOP_LEFT:
+          expect_tile = i < 5 && j < 5;
+          break;
+        case TOP_RIGHT:
+          // The interest rect in the top right tile means we'll record it.
+          expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5);
+          break;
+        case BOTTOM_LEFT:
+          // The interest rect in the bottom left tile means we'll record it.
+          expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0);
+          break;
+        case BOTTOM_RIGHT:
+          // The interest rect in the bottom right tile means we'll record it.
+          expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5);
+          break;
+      }
+      EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture())
+          << i << "," << j;
     }
   }
 
-  // No invalidation when shrinking.
-  expected_invalidation.Clear();
+  // We invalidated all previous pixels in the recording.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  // The whole right column and bottom row of tiles (except for ones with the
+  // interest rect) are dropped.
+  Region right_column_and_bottom_row_minus_existing_corner =
+      right_column_and_bottom_row;
+  switch (corner) {
+    case TOP_LEFT:
+      break;
+    case BOTTOM_LEFT:
+      right_column_and_bottom_row_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(0, 5));
+      break;
+    case TOP_RIGHT:
+      right_column_and_bottom_row_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(5, 0));
+      break;
+    case BOTTOM_RIGHT:
+      right_column_and_bottom_row_minus_existing_corner.Subtract(
+          pile_.tiling().TileBounds(5, 5));
+      break;
+  }
+  expected_invalidation.Union(
+      right_column_and_bottom_row_minus_existing_corner);
   EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 }
@@ -805,10 +921,10 @@
     }
   }
 
-  UpdateAndExpandInvalidation(
-      &invalidation,
-      grow_down_tiling_size,
-      CornerSinglePixelRect(corner, grow_down_tiling_size));
+  // In this test (unlike the large resize test), as all growing and shrinking
+  // happens within tiles, the resulting invalidation is symmetrical, so use
+  // this enum to repeat the test both ways.
+  enum ChangeDirection { GROW, SHRINK, LAST_DIRECTION = SHRINK };
 
   // We should have lost the recordings in the bottom row that do not intersect
   // the interest rect.
@@ -823,19 +939,25 @@
       switch (corner) {
         case TOP_LEFT:
         case TOP_RIGHT:
-          expect_tile = j < 5;
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5));
           break;
         case BOTTOM_LEFT:
-          // The interest rect in the bottom left tile means we'll record it.
-          expect_tile = j < 5 || (j == 5 && i == 0);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
           break;
         case BOTTOM_RIGHT:
-          // The interest rect in the bottom right tile means we'll record it.
-          expect_tile = j < 5 || (j == 5 && i == 5);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
           break;
       }
-      EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
     }
+    EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+    invalidation.Clear();
   }
 
   // We invalidated the bottom row outside the new interest rect. The tile that
@@ -877,18 +999,6 @@
       TestPicturePile::PictureMap::iterator it = map.find(key);
       EXPECT_TRUE(it != map.end() && it->second.GetPicture());
     }
-  }
-
-  // We invalidated nothing.
-  expected_invalidation.Clear();
-  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
-  invalidation.Clear();
-
-  UpdateWholePile();
-  UpdateAndExpandInvalidation(
-      &invalidation,
-      grow_right_tiling_size,
-      CornerSinglePixelRect(corner, grow_right_tiling_size));
 
   // We should have lost the recordings in the right column.
   EXPECT_EQ(6, pile_->tiling().num_tiles_x());
@@ -902,19 +1012,25 @@
       switch (corner) {
         case TOP_LEFT:
         case BOTTOM_LEFT:
-          expect_tile = i < 5;
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
           break;
         case TOP_RIGHT:
-          // The interest rect in the top right tile means we'll record it.
-          expect_tile = i < 5 || (j == 0 && i == 5);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
           break;
         case BOTTOM_RIGHT:
-          // The interest rect in the bottom right tile means we'll record it.
-          expect_tile = i < 5 || (j == 5 && i == 5);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
           break;
       }
-      EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
     }
+    EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+    invalidation.Clear();
   }
 
   // We invalidated the right column outside the new interest rect. The tile
@@ -956,7 +1072,6 @@
       TestPicturePile::PictureMap::iterator it = map.find(key);
       EXPECT_TRUE(it != map.end() && it->second.GetPicture());
     }
-  }
 
   // We invalidated nothing.
   expected_invalidation.Clear();
@@ -981,19 +1096,38 @@
       bool expect_tile;
       switch (corner) {
         case TOP_LEFT:
-          expect_tile = i < 5 && j < 5;
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+          expected_invalidation.Union(
+              gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+                              pile_.tiling().TileBounds(5, 5)));
           break;
         case TOP_RIGHT:
-          // The interest rect in the top right tile means we'll record it.
-          expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5));
+          expected_invalidation.Union(
+              gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+                              pile_.tiling().TileBounds(5, 5)));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
           break;
         case BOTTOM_LEFT:
-          // The interest rect in the bottom left tile means we'll record it.
-          expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+          expected_invalidation.Union(
+              gfx::UnionRects(pile_.tiling().TileBounds(1, 5),
+                              pile_.tiling().TileBounds(5, 5)));
+          expected_invalidation.Union(SubtractRects(
+              pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
           break;
         case BOTTOM_RIGHT:
-          // The interest rect in the bottom right tile means we'll record it.
-          expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5);
+          expected_invalidation = gfx::UnionRects(
+              pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4));
+          expected_invalidation.Union(
+              gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+                              pile_.tiling().TileBounds(4, 5)));
+          expected_invalidation.Union(SubtractRegions(
+              pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
           break;
       }
       EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture())
@@ -1054,12 +1188,9 @@
       TestPicturePile::PictureMap::iterator it = map.find(key);
       EXPECT_TRUE(it != map.end() && it->second.GetPicture());
     }
+    EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+    invalidation.Clear();
   }
-
-  // We invalidated nothing.
-  expected_invalidation.Clear();
-  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
-  invalidation.Clear();
 }
 
 INSTANTIATE_TEST_CASE_P(
@@ -1136,8 +1267,11 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels on the bottom row of tiles.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
   UpdateWholePile();
@@ -1182,8 +1316,11 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels on the right column of tiles.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
   UpdateWholePile();
@@ -1232,8 +1369,13 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels on the bottom row and right
+  // column of tiles.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_TRUE(
+      expected_invalidation.Contains(bottom_row_and_right_column_new_pixels));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 }
 
@@ -1301,8 +1443,10 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
   UpdateWholePile();
@@ -1342,8 +1486,10 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 
   UpdateWholePile();
@@ -1383,8 +1529,10 @@
     }
   }
 
-  // No invalidation when shrinking.
-  EXPECT_EQ(Region().ToString(), invalidation.ToString());
+  // We invalidated the previously exposed pixels.
+  expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+                                          gfx::Rect(base_tiling_size));
+  EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
   invalidation.Clear();
 }