Bug 530686. Don't redraw box-shadows over the bounding rect of the dirty region; just redraw them over a (somewhat simplified) dirty region. r=dbaron
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 30 Nov 2009 18:14:04 -0500
changeset 35288 1a0f2d658a4991769863b70a343bddb43124d0fc
parent 35287 45f2a2271c733302bbd8ec9d9e5fe2dcffbe3e61
child 35289 3b6e79fd27bfec9215be587ebe3d182ce281bd37
push id10549
push userbzbarsky@mozilla.com
push dateMon, 30 Nov 2009 23:15:13 +0000
treeherdermozilla-central@3b6e79fd27bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs530686
milestone1.9.3a1pre
Bug 530686. Don't redraw box-shadows over the bounding rect of the dirty region; just redraw them over a (somewhat simplified) dirty region. r=dbaron
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/reftests/bugs/530686-1-ref.html
layout/reftests/bugs/530686-1.html
layout/reftests/bugs/reftest.list
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -810,23 +810,63 @@ nsDisplayBorder::Paint(nsDisplayListBuil
   nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
                               mVisibleRect,
                               nsRect(offset, mFrame->GetSize()),
                               *mFrame->GetStyleBorder(),
                               mFrame->GetStyleContext(),
                               mFrame->GetSkipSides());
 }
 
+// Given a region, compute a conservative approximation to it as a list
+// of rectangles that aren't vertically adjacent (i.e., vertically
+// adjacent or overlapping rectangles are combined).
+// Right now this is only approximate, some vertically overlapping rectangles
+// aren't guaranteed to be combined.
+static void
+ComputeDisjointRectangles(const nsRegion& aRegion,
+                          nsTArray<nsRect>* aRects) {
+  nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25);
+  nsRect accumulated;
+  nsRegionRectIterator iter(aRegion);
+  while (PR_TRUE) {
+    const nsRect* r = iter.Next();
+    if (r && !accumulated.IsEmpty() &&
+        accumulated.YMost() >= r->y - accumulationMargin) {
+      accumulated.UnionRect(accumulated, *r);
+      continue;
+    }
+
+    if (!accumulated.IsEmpty()) {
+      aRects->AppendElement(accumulated);
+      accumulated.Empty();
+    }
+
+    if (!r)
+      break;
+
+    accumulated = *r;
+  }
+}
+
 void
 nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
                                nsIRenderingContext* aCtx) {
   nsPoint offset = aBuilder->ToReferenceFrame(mFrame);
-  nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
-                                      nsRect(offset, mFrame->GetSize()),
-                                      mVisibleRect);
+  nsRect borderRect = nsRect(offset, mFrame->GetSize());
+  nsPresContext* presContext = mFrame->PresContext();
+  nsAutoTArray<nsRect,10> rects;
+  ComputeDisjointRectangles(mVisibleRegion, &rects);
+
+  for (PRUint32 i = 0; i < rects.Length(); ++i) {
+    aCtx->PushState();
+    aCtx->SetClipRect(rects[i], nsClipCombine_kIntersect);
+    nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame,
+                                        borderRect, rects[i]);
+    aCtx->PopState();
+  }
 }
 
 nsRect
 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
 }
 
 PRBool
@@ -835,16 +875,19 @@ nsDisplayBoxShadowOuter::ComputeVisibili
                                            nsRegion* aVisibleRegionBeforeMove) {
   NS_ASSERTION((aVisibleRegionBeforeMove != nsnull) == aBuilder->HasMovingFrames(),
                "Should have aVisibleRegionBeforeMove when there are moving frames");
 
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
                                         aVisibleRegionBeforeMove))
     return PR_FALSE;
 
+  // Store the actual visible region
+  mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
+
   nsPoint origin = aBuilder->ToReferenceFrame(mFrame);
   nsRect visibleBounds = aVisibleRegion->GetBounds();
   if (aVisibleRegionBeforeMove) {
     visibleBounds.UnionRect(visibleBounds, aVisibleRegionBeforeMove->GetBounds());
   }
   nsRect frameRect(origin, mFrame->GetSize());
   if (!frameRect.Contains(visibleBounds))
     return PR_TRUE;
@@ -861,19 +904,44 @@ nsDisplayBoxShadowOuter::ComputeVisibili
 
   return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds);
 }
 
 void
 nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
                                nsIRenderingContext* aCtx) {
   nsPoint offset = aBuilder->ToReferenceFrame(mFrame);
-  nsCSSRendering::PaintBoxShadowInner(mFrame->PresContext(), *aCtx, mFrame,
-                                      nsRect(offset, mFrame->GetSize()),
-                                      mVisibleRect);
+  nsRect borderRect = nsRect(offset, mFrame->GetSize());
+  nsPresContext* presContext = mFrame->PresContext();
+  nsAutoTArray<nsRect,10> rects;
+  ComputeDisjointRectangles(mVisibleRegion, &rects);
+
+  for (PRUint32 i = 0; i < rects.Length(); ++i) {
+    aCtx->PushState();
+    aCtx->SetClipRect(rects[i], nsClipCombine_kIntersect);
+    nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame,
+                                        borderRect, rects[i]);
+    aCtx->PopState();
+  }
+}
+
+PRBool
+nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
+                                           nsRegion* aVisibleRegion,
+                                           nsRegion* aVisibleRegionBeforeMove) {
+  NS_ASSERTION((aVisibleRegionBeforeMove != nsnull) == aBuilder->HasMovingFrames(),
+               "Should have aVisibleRegionBeforeMove when there are moving frames");
+
+  if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aVisibleRegionBeforeMove))
+    return PR_FALSE;
+
+  // Store the actual visible region
+  mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
+  return PR_TRUE;
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsIFrame* aFrame, nsDisplayList* aList)
   : nsDisplayItem(aFrame) {
   mList.AppendToTop(aList);
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsIFrame* aFrame, nsDisplayItem* aItem)
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1207,16 +1207,19 @@ public:
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
                                    nsRegion* aVisibleRegionBeforeMove);
   NS_DISPLAY_DECL_NAME("BoxShadowOuter")
+
+private:
+  nsRegion mVisibleRegion;
 };
 
 /**
  * The standard display item to paint the inner CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowInner : public nsDisplayItem {
 public:
   nsDisplayBoxShadowInner(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
@@ -1224,17 +1227,23 @@ public:
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBoxShadowInner() {
     MOZ_COUNT_DTOR(nsDisplayBoxShadowInner);
   }
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
+  virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
+                                   nsRegion* aVisibleRegion,
+                                   nsRegion* aVisibleRegionBeforeMove);
   NS_DISPLAY_DECL_NAME("BoxShadowInner")
+
+private:
+  nsRegion mVisibleRegion;
 };
 
 /**
  * The standard display item to paint the CSS outline of a frame.
  */
 class nsDisplayOutline : public nsDisplayItem {
 public:
   nsDisplayOutline(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/530686-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<style type="text/css"> 
+#rear {
+	width: 500px;
+	height: 1500px;
+	-moz-box-shadow: 0 0 100px #667;
+	display: block;
+}
+</style>
+</head>
+<body>
+<div id="rear"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/530686-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<style type="text/css"> 
+#rear {
+	width: 500px;
+	height: 1500px;
+	-moz-box-shadow: 0 0 100px #667;
+	display: block;
+}
+.cover {
+    position: absolute;
+    width: 520px;
+    height: 100px;
+    background: yellow;
+}
+</style>
+<script>
+function doTest() {
+  var es = document.getElementsByClassName("cover");
+  for (var i = 0; i < es.length; ++i) {
+    es[i].style.display = 'none';
+  }
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+</head>
+<body>
+<div id="rear"></div>
+<div class="cover" style="top:100px"></div>
+<div class="cover" style="top:300px"></div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1342,8 +1342,9 @@ fails-if(MOZ_WIDGET_TOOLKIT!="cocoa") ==
 == 527464-1.html 527464-ref.html
 == 528038-1a.html 528038-1-ref.html
 == 528038-1b.html 528038-1-ref.html
 == 528038-1c.html 528038-1-ref.html
 == 528038-1d.html 528038-1-ref.html
 == 528038-1e.html 528038-1-ref.html
 == 528038-1f.html 528038-1-ref.html
 == 528038-2.html 528038-2-ref.html
+== 530686-1.html 503686-1-ref.html