Fix occlusion culling logic to handle css background layer clipping
authorjunov@google.com <junov@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 2 Dec 2012 10:55:24 +0000 (10:55 +0000)
committerjunov@google.com <junov@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 2 Dec 2012 10:55:24 +0000 (10:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=103276

Reviewed by Simon Fraser.

Source/WebCore:

Bug was introduced by r135629
This patch fixes it by verifying layer clip.  A fill layer must
not be treated as if it occludes the layers under it unless it has
a larger or equal clip setting than the layers under it.

Test: fast/backgrounds/background-opaque-clipped-gradients.html

* rendering/RenderBox.cpp:
(WebCore::RenderBox::paintFillLayers):
* rendering/style/FillLayer.cpp:
(WebCore::clipMax):
(WebCore::FillLayer::computeClipMax):
(WebCore::FillLayer::clipOccludesNextLayers):
* rendering/style/FillLayer.h:
(FillLayer):

LayoutTests:

New ref test that verifies the superposition of background layers with
the layers having smaller clip regions than the layers beneath them.
This test ensures the bottom layers are not removed by culling
optimizations.

* fast/backgrounds/background-opaque-clipped-gradients-expected.html: Added.
* fast/backgrounds/background-opaque-clipped-gradients.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@136326 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/backgrounds/background-opaque-clipped-gradients-expected.html [new file with mode: 0644]
LayoutTests/fast/backgrounds/background-opaque-clipped-gradients.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/style/FillLayer.cpp
Source/WebCore/rendering/style/FillLayer.h

index 2bda494..0ec4bad 100644 (file)
@@ -1,3 +1,18 @@
+2012-12-02  Justin Novosad  <junov@google.com>
+
+        Fix occlusion culling logic to handle css background layer clipping
+        https://bugs.webkit.org/show_bug.cgi?id=103276
+
+        Reviewed by Simon Fraser.
+
+        New ref test that verifies the superposition of background layers with
+        the layers having smaller clip regions than the layers beneath them.
+        This test ensures the bottom layers are not removed by culling
+        optimizations.
+
+        * fast/backgrounds/background-opaque-clipped-gradients-expected.html: Added.
+        * fast/backgrounds/background-opaque-clipped-gradients.html: Added.
+
 2012-12-02  Tony Chang  <tony@chromium.org>
 
         Avoid a second layout of flex items in layoutAndPlaceChildren()
diff --git a/LayoutTests/fast/backgrounds/background-opaque-clipped-gradients-expected.html b/LayoutTests/fast/backgrounds/background-opaque-clipped-gradients-expected.html
new file mode 100644 (file)
index 0000000..4b69b37
--- /dev/null
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+<head>
+<style type="text/css" media="screen">
+#div1
+{
+width: 150px;
+height: 150px;
+border:25px dotted;
+background-color: green;
+}
+#div2
+{
+width: 100px;
+height: 100px;
+padding: 25px;
+background-color: blue;
+}
+#div3
+{
+width: 100px;
+height: 100px;
+background-color: yellow;
+}
+</style>
+</head>
+<body>
+Test passes if the image below shows nested green, blue and yellow squares with a dotted black border.
+<div id="div1"><div id="div2"><div id="div3"></div></div></div>
+</body>
+</html>
diff --git a/LayoutTests/fast/backgrounds/background-opaque-clipped-gradients.html b/LayoutTests/fast/backgrounds/background-opaque-clipped-gradients.html
new file mode 100644 (file)
index 0000000..0568479
--- /dev/null
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head>
+<style type="text/css" media="screen">
+    #div1 {
+        width: 100px;
+        height: 100px;
+        padding: 25px;
+        border: 25px dotted;
+        background-image: -webkit-linear-gradient(top, yellow, yellow), -webkit-linear-gradient(top, blue, blue), -webkit-linear-gradient(top, green, green);
+        background-clip: content-box, padding-box, border-box;
+}
+</style>
+</head>
+<body>
+Test passes if the image below shows nested green, blue and yellow squares with a dotted black border.
+<div id="div1"></div>
+</body>
+</html>
index af212df..a68fcf3 100644 (file)
@@ -1,3 +1,26 @@
+2012-12-02  Justin Novosad  <junov@google.com>
+
+        Fix occlusion culling logic to handle css background layer clipping
+        https://bugs.webkit.org/show_bug.cgi?id=103276
+
+        Reviewed by Simon Fraser.
+
+        Bug was introduced by r135629
+        This patch fixes it by verifying layer clip.  A fill layer must
+        not be treated as if it occludes the layers under it unless it has
+        a larger or equal clip setting than the layers under it.
+
+        Test: fast/backgrounds/background-opaque-clipped-gradients.html
+
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::paintFillLayers):
+        * rendering/style/FillLayer.cpp:
+        (WebCore::clipMax):
+        (WebCore::FillLayer::computeClipMax):
+        (WebCore::FillLayer::clipOccludesNextLayers):
+        * rendering/style/FillLayer.h:
+        (FillLayer):
+
 2012-12-02  Tony Chang  <tony@chromium.org>
 
         Avoid a second layout of flex items in layoutAndPlaceChildren()
index 6c341dd..79c4b10 100644 (file)
@@ -1048,18 +1048,25 @@ LayoutRect RenderBox::maskClipRect()
 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
     BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
 {
-    if (!fillLayer)
-        return;
+    Vector<const FillLayer*, 8> layers;
+    const FillLayer* curLayer = fillLayer;
+    while (curLayer) {
+        layers.append(curLayer);
+        // Stop traversal when an opaque layer is encountered.
+        // FIXME : It would be possible for the following occlusion culling test to be more aggressive 
+        // on layers with no repeat by testing whether the image covers the layout rect.
+        // Testing that here would imply duplicating a lot of calculations that are currently done in
+        // RenderBoxModelOBject::paintFillLayerExtended. A more efficient solution might be to move
+        // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
+        // and pass it down.
+        if (curLayer->hasOpaqueImage(this) && curLayer->clipOccludesNextLayers(curLayer == fillLayer) && curLayer->image()->canRender(this, style()->effectiveZoom()) && curLayer->hasRepeatXY())
+            break;
+        curLayer = curLayer->next();
+    }
 
-    // FIXME : It would be possible for the following occlusion culling test to be more aggressive 
-    // on layers with no repeat by testing whether the image covers the layout rect.
-    // Testing that here would imply duplicating a lot of calculations that are currently done in
-    // RenderBoxModelOBject::paintFillLayerExtended. A more efficient solution might be to move
-    // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
-    // and pass it down.
-    if (fillLayer->next() && (!fillLayer->hasOpaqueImage(this) || !fillLayer->image()->canRender(this, style()->effectiveZoom()) || !fillLayer->hasRepeatXY()))
-        paintFillLayers(paintInfo, c, fillLayer->next(), rect, bleedAvoidance, op, backgroundObject);
-    paintFillLayer(paintInfo, c, fillLayer, rect, bleedAvoidance, op, backgroundObject);
+    Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
+    for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
+        paintFillLayer(paintInfo, c, *it, rect, bleedAvoidance, op, backgroundObject);
 }
 
 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
index edd3d9a..38db240 100644 (file)
@@ -258,6 +258,33 @@ void FillLayer::cullEmptyLayers()
     }
 }
 
+static EFillBox clipMax(EFillBox clipA, EFillBox clipB)
+{
+    if (clipA == BorderFillBox || clipB == BorderFillBox)
+        return BorderFillBox;
+    if (clipA == PaddingFillBox || clipB == PaddingFillBox)
+        return PaddingFillBox;
+    if (clipA == ContentFillBox || clipB == ContentFillBox)
+        return ContentFillBox;
+    return TextFillBox;
+}
+
+void FillLayer::computeClipMax() const
+{
+    if (m_next) {
+        m_next->computeClipMax();
+        m_clipMax = clipMax(clip(), m_next->clip());
+    } else
+        m_clipMax = m_clip;
+}
+
+bool FillLayer::clipOccludesNextLayers(bool firstLayer) const
+{
+    if (firstLayer)
+        computeClipMax();
+    return m_clip == m_clipMax;
+}
+
 bool FillLayer::containsImage(StyleImage* s) const
 {
     if (!s)
index 525f533..7e1061c 100644 (file)
@@ -146,6 +146,7 @@ public:
 
     bool hasOpaqueImage(const RenderObject*) const;
     bool hasRepeatXY() const;
+    bool clipOccludesNextLayers(bool firstLayer) const;
 
     EFillLayerType type() const { return static_cast<EFillLayerType>(m_type); }
 
@@ -168,6 +169,8 @@ public:
 private:
     friend class RenderStyle;
 
+    void computeClipMax() const;
+
     FillLayer() { }
 
     FillLayer* m_next;
@@ -198,6 +201,8 @@ private:
     unsigned m_compositeSet : 1;
     
     unsigned m_type : 1; // EFillLayerType
+
+    mutable unsigned m_clipMax : 2; // EFillBox, maximum m_clip value from this to bottom layer
 };
 
 } // namespace WebCore