2010-09-01 Nikolas Zimmermann <nzimmermann@rim.com>
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Sep 2010 12:32:29 +0000 (12:32 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Sep 2010 12:32:29 +0000 (12:32 +0000)
        Reviewed by Dirk Schulze.

        Add culling to RenderSVGContainer
        https://bugs.webkit.org/show_bug.cgi?id=44896

        Cache object/strokeBoundingBox and repaintRectInLocalCoordinates() in RenderSVGContainer & RenderSVGRoot, just like it's done in RenderPath.
        Propagate boundary changes up the render tree, in layout phase, in order to notify parents that they should update their cached boundaries.

        This is a huge speed benefit when interacting with an already layouted document, as there's no need anymore to traverse all children
        of any container or <svg> element, just to retrieve the repaint rect.

        * rendering/RenderForeignObject.cpp:
        (WebCore::RenderForeignObject::layout): Propagate boundary changes to parent, if neeeded.
        * rendering/RenderObject.cpp:
        (WebCore::RenderObject::setNeedsBoundariesUpdate): Ditto.
        * rendering/RenderObject.h:
        * rendering/RenderObjectChildList.cpp:
        (WebCore::RenderObjectChildList::removeChildNode): Ditto.
        * rendering/RenderPath.cpp:
        (WebCore::RenderPath::layout): Ditto.
        (WebCore::RenderPath::paint): Use new helper function to intersect the paintInfo.rect with the repaintRectInLocalCoordinates().
        * rendering/RenderPath.h: Remove styleWillChange(), it's in RenderSVGModelObject now.
        (WebCore::RenderPath::setNeedsBoundariesUpdate): Mark as virtual, just like in RenderObject.
        * rendering/RenderSVGContainer.cpp:
        (WebCore::RenderSVGContainer::RenderSVGContainer): Initialize m_needsBoundariesUpdate to true.
        (WebCore::RenderSVGContainer::layout): Only update cached boundaries, if needed, once by traversing the child tree.
        (WebCore::RenderSVGContainer::paint): Use new helper function to intersect the paintInfo.rect with the repaintRectInLocalCoordinates().
        (WebCore::RenderSVGContainer::updateCachedBoundaries): Ask children for their boundaries, and cache them.
        * rendering/RenderSVGContainer.h:
        (WebCore::RenderSVGContainer::setNeedsBoundariesUpdate): Set m_needsBoundariesUpdate when called.
        (WebCore::RenderSVGContainer::objectBoundingBox): Inlined, just returns a member variable now.
        (WebCore::RenderSVGContainer::strokeBoundingBox): Ditto.
        (WebCore::RenderSVGContainer::repaintRectInLocalCoordinates): Ditto.
        (WebCore::RenderSVGContainer::calculateLocalTransform): Changed to return a boolean, wheter the transform was modified.
        * rendering/RenderSVGGradientStop.h:
        (WebCore::RenderSVGGradientStop::strokeBoundingBox): Add missing default implementation, return just FloatRect().
        * rendering/RenderSVGHiddenContainer.cpp: Remove repaintRectInLocalCoordinates() override, it's now needed for painting markers.
        * rendering/RenderSVGHiddenContainer.h:
        (WebCore::RenderSVGHiddenContainer::clippedOverflowRectForRepaint): Inlined, returning IntRect().
        * rendering/RenderSVGImage.cpp:
        (WebCore::RenderSVGImage::layout): Propagate boundary changes to parent, if neeeded.
        * rendering/RenderSVGRoot.cpp: Cache boundaries here as well, just like in RenderSVGContainer.
        (WebCore::RenderSVGRoot::RenderSVGRoot):
        (WebCore::RenderSVGRoot::layout):
        (WebCore::RenderSVGRoot::updateCachedBoundaries):
        * rendering/RenderSVGRoot.h:
        (WebCore::RenderSVGRoot::setNeedsBoundariesUpdate):
        (WebCore::RenderSVGRoot::setNeedsTransformUpdate):
        (WebCore::RenderSVGRoot::objectBoundingBox):
        (WebCore::RenderSVGRoot::strokeBoundingBox):
        (WebCore::RenderSVGRoot::repaintRectInLocalCoordinates):
        * rendering/RenderSVGText.cpp:
        (WebCore::RenderSVGText::layout): Propagate boundary changes to parent, if neeeded.
        * rendering/RenderSVGTransformableContainer.cpp:
        (WebCore::RenderSVGTransformableContainer::calculateLocalTransform): Return true, if the localTransform changed.
        * rendering/RenderSVGTransformableContainer.h:
        * rendering/RenderSVGViewportContainer.cpp:
        (WebCore::RenderSVGViewportContainer::calcViewport): Propagate boundary changes to parent, if neeeded.
        * rendering/SVGRenderSupport.cpp:
        (WebCore::SVGRenderSupport::computeContainerBoundingBoxes): Helper function calculating object/strokeBoundingBox and repaintRectInLocalCoordinates in one shot.
        (WebCore::SVGRenderSupport::paintInfoIntersectsRepaintRect): Moved from RenderPath to share the culling functionality.
        * rendering/SVGRenderSupport.h:

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

21 files changed:
WebCore/ChangeLog
WebCore/rendering/RenderForeignObject.cpp
WebCore/rendering/RenderObject.cpp
WebCore/rendering/RenderObject.h
WebCore/rendering/RenderObjectChildList.cpp
WebCore/rendering/RenderPath.cpp
WebCore/rendering/RenderPath.h
WebCore/rendering/RenderSVGContainer.cpp
WebCore/rendering/RenderSVGContainer.h
WebCore/rendering/RenderSVGGradientStop.h
WebCore/rendering/RenderSVGHiddenContainer.cpp
WebCore/rendering/RenderSVGHiddenContainer.h
WebCore/rendering/RenderSVGImage.cpp
WebCore/rendering/RenderSVGRoot.cpp
WebCore/rendering/RenderSVGRoot.h
WebCore/rendering/RenderSVGText.cpp
WebCore/rendering/RenderSVGTransformableContainer.cpp
WebCore/rendering/RenderSVGTransformableContainer.h
WebCore/rendering/RenderSVGViewportContainer.cpp
WebCore/rendering/SVGRenderSupport.cpp
WebCore/rendering/SVGRenderSupport.h

index 6ad9f9a..a42f9da 100644 (file)
@@ -1,3 +1,68 @@
+2010-09-01  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Reviewed by Dirk Schulze.
+
+        Add culling to RenderSVGContainer
+        https://bugs.webkit.org/show_bug.cgi?id=44896
+
+        Cache object/strokeBoundingBox and repaintRectInLocalCoordinates() in RenderSVGContainer & RenderSVGRoot, just like it's done in RenderPath.
+        Propagate boundary changes up the render tree, in layout phase, in order to notify parents that they should update their cached boundaries.
+
+        This is a huge speed benefit when interacting with an already layouted document, as there's no need anymore to traverse all children
+        of any container or <svg> element, just to retrieve the repaint rect.
+
+        * rendering/RenderForeignObject.cpp:
+        (WebCore::RenderForeignObject::layout): Propagate boundary changes to parent, if neeeded.
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::setNeedsBoundariesUpdate): Ditto.
+        * rendering/RenderObject.h:
+        * rendering/RenderObjectChildList.cpp:
+        (WebCore::RenderObjectChildList::removeChildNode): Ditto.
+        * rendering/RenderPath.cpp:
+        (WebCore::RenderPath::layout): Ditto.
+        (WebCore::RenderPath::paint): Use new helper function to intersect the paintInfo.rect with the repaintRectInLocalCoordinates().
+        * rendering/RenderPath.h: Remove styleWillChange(), it's in RenderSVGModelObject now.
+        (WebCore::RenderPath::setNeedsBoundariesUpdate): Mark as virtual, just like in RenderObject.
+        * rendering/RenderSVGContainer.cpp:
+        (WebCore::RenderSVGContainer::RenderSVGContainer): Initialize m_needsBoundariesUpdate to true.
+        (WebCore::RenderSVGContainer::layout): Only update cached boundaries, if needed, once by traversing the child tree.
+        (WebCore::RenderSVGContainer::paint): Use new helper function to intersect the paintInfo.rect with the repaintRectInLocalCoordinates().
+        (WebCore::RenderSVGContainer::updateCachedBoundaries): Ask children for their boundaries, and cache them.
+        * rendering/RenderSVGContainer.h:
+        (WebCore::RenderSVGContainer::setNeedsBoundariesUpdate): Set m_needsBoundariesUpdate when called.
+        (WebCore::RenderSVGContainer::objectBoundingBox): Inlined, just returns a member variable now.
+        (WebCore::RenderSVGContainer::strokeBoundingBox): Ditto.
+        (WebCore::RenderSVGContainer::repaintRectInLocalCoordinates): Ditto.
+        (WebCore::RenderSVGContainer::calculateLocalTransform): Changed to return a boolean, wheter the transform was modified.
+        * rendering/RenderSVGGradientStop.h:
+        (WebCore::RenderSVGGradientStop::strokeBoundingBox): Add missing default implementation, return just FloatRect().
+        * rendering/RenderSVGHiddenContainer.cpp: Remove repaintRectInLocalCoordinates() override, it's now needed for painting markers.
+        * rendering/RenderSVGHiddenContainer.h:
+        (WebCore::RenderSVGHiddenContainer::clippedOverflowRectForRepaint): Inlined, returning IntRect().
+        * rendering/RenderSVGImage.cpp:
+        (WebCore::RenderSVGImage::layout): Propagate boundary changes to parent, if neeeded.
+        * rendering/RenderSVGRoot.cpp: Cache boundaries here as well, just like in RenderSVGContainer.
+        (WebCore::RenderSVGRoot::RenderSVGRoot):
+        (WebCore::RenderSVGRoot::layout):
+        (WebCore::RenderSVGRoot::updateCachedBoundaries):
+        * rendering/RenderSVGRoot.h:
+        (WebCore::RenderSVGRoot::setNeedsBoundariesUpdate):
+        (WebCore::RenderSVGRoot::setNeedsTransformUpdate):
+        (WebCore::RenderSVGRoot::objectBoundingBox):
+        (WebCore::RenderSVGRoot::strokeBoundingBox):
+        (WebCore::RenderSVGRoot::repaintRectInLocalCoordinates):
+        * rendering/RenderSVGText.cpp:
+        (WebCore::RenderSVGText::layout): Propagate boundary changes to parent, if neeeded.
+        * rendering/RenderSVGTransformableContainer.cpp:
+        (WebCore::RenderSVGTransformableContainer::calculateLocalTransform): Return true, if the localTransform changed.
+        * rendering/RenderSVGTransformableContainer.h:
+        * rendering/RenderSVGViewportContainer.cpp:
+        (WebCore::RenderSVGViewportContainer::calcViewport): Propagate boundary changes to parent, if neeeded.
+        * rendering/SVGRenderSupport.cpp:
+        (WebCore::SVGRenderSupport::computeContainerBoundingBoxes): Helper function calculating object/strokeBoundingBox and repaintRectInLocalCoordinates in one shot.
+        (WebCore::SVGRenderSupport::paintInfoIntersectsRepaintRect): Moved from RenderPath to share the culling functionality.
+        * rendering/SVGRenderSupport.h:
+
 2010-09-01  Mahesh Kulkarni  <mahesh.kulkarni@nokia.com>
 
         Reviewed by Laszlo Gombos.
index 50c1a42..bfcb940 100644 (file)
@@ -102,14 +102,20 @@ void RenderForeignObject::layout()
     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
     SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(node());
 
+    bool updateCachedBoundariesInParents = false;
     if (m_needsTransformUpdate) {
         m_localTransform = foreign->animatedLocalTransform();
         m_needsTransformUpdate = false;
+        updateCachedBoundariesInParents = true;
     }
 
+    FloatRect oldViewport = m_viewport;
+
     // Cache viewport boundaries
     FloatPoint viewportLocation(foreign->x().value(foreign), foreign->y().value(foreign));
     m_viewport = FloatRect(viewportLocation, FloatSize(foreign->width().value(foreign), foreign->height().value(foreign)));
+    if (!updateCachedBoundariesInParents)
+        updateCachedBoundariesInParents = oldViewport != m_viewport;
 
     // Set box origin to the foreignObject x/y translation, so positioned objects in XHTML content get correct
     // positions. A regular RenderBoxModelObject would pull this information from RenderStyle - in SVG those
@@ -122,6 +128,10 @@ void RenderForeignObject::layout()
     RenderBlock::layout();
     ASSERT(!needsLayout());
 
+    // If our bounds changed, notify the parents.
+    if (updateCachedBoundariesInParents)
+        RenderSVGBlock::setNeedsBoundariesUpdate();
+
     // Invalidate all resources of this client if our layout changed.
     if (layoutChanged)
         SVGResourcesCache::clientLayoutChanged(this);
index 576aad0..d01b92d 100644 (file)
@@ -2692,6 +2692,12 @@ RenderSVGResourceContainer* RenderObject::toRenderSVGResourceContainer()
     return 0;
 }
 
+void RenderObject::setNeedsBoundariesUpdate()
+{
+    if (RenderObject* renderer = parent())
+        renderer->setNeedsBoundariesUpdate();
+}
+
 FloatRect RenderObject::objectBoundingBox() const
 {
     ASSERT_NOT_REACHED();
index 34eba6d..9fbaf91 100644 (file)
@@ -327,7 +327,7 @@ public:
     // Unfortunately we don't have such a class yet, because it's not possible for all renderers
     // to inherit from RenderSVGObject -> RenderObject (some need RenderBlock inheritance for instance)
     virtual void setNeedsTransformUpdate() { }
-    virtual void setNeedsBoundariesUpdate() { }
+    virtual void setNeedsBoundariesUpdate();
 
     // Per SVG 1.1 objectBoundingBox ignores clipping, masking, filter effects, opacity and stroke-width.
     // This is used for all computation of objectBoundingBox relative units and by SVGLocateable::getBBox().
index d8c7955..96ec800 100644 (file)
@@ -98,6 +98,11 @@ RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, Render
 
         if (oldChild->isPositioned() && owner->childrenInline())
             owner->dirtyLinesFromChangedChild(oldChild);
+
+#if ENABLE(SVG)
+        // Update cached boundaries in SVG renderers, if a child is removed.
+        owner->setNeedsBoundariesUpdate();
+#endif
     }
     
     // If oldChild is the start or end of the selection, then clear the selection to
index 86a12c8..0f31df1 100644 (file)
@@ -101,28 +101,39 @@ void RenderPath::layout()
     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
 
+    bool updateCachedBoundariesInParents = false;
+
     bool needsPathUpdate = m_needsPathUpdate;
     if (needsPathUpdate) {
         m_path = element->toPathData();
         m_needsPathUpdate = false;
+        updateCachedBoundariesInParents = true;
     }
 
     if (m_needsTransformUpdate) {
         m_localTransform = element->animatedLocalTransform();
         m_needsTransformUpdate = false;
+        updateCachedBoundariesInParents = true;
     }
 
+    if (m_needsBoundariesUpdate)
+        updateCachedBoundariesInParents = true;
+
     // Invalidate all resources of this client if our layout changed.
     if (m_everHadLayout && selfNeedsLayout())
         SVGResourcesCache::clientLayoutChanged(this);
 
     // At this point LayoutRepainter already grabbed the old bounds,
-    // recalculate them now so repaintAfterLayout() uses the new bounds
+    // recalculate them now so repaintAfterLayout() uses the new bounds.
     if (needsPathUpdate || m_needsBoundariesUpdate) {
         updateCachedBoundaries();
         m_needsBoundariesUpdate = false;
     }
 
+    // If our bounds changed, notify the parents.
+    if (updateCachedBoundariesInParents)
+        RenderSVGModelObject::setNeedsBoundariesUpdate();
+
     repainter.repaintAfterLayout();
     setNeedsLayout(false);
 }
@@ -172,8 +183,7 @@ void RenderPath::paint(PaintInfo& paintInfo, int, int)
         return;
 
     FloatRect boundingBox = repaintRectInLocalCoordinates();
-    FloatRect nonLocalBoundingBox = m_localTransform.mapRect(boundingBox);
-    if (!nonLocalBoundingBox.intersects(paintInfo.rect))
+    if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
         return;
 
     PaintInfo childPaintInfo(paintInfo);
@@ -270,13 +280,6 @@ FloatRect RenderPath::calculateMarkerBoundsIfNeeded()
     return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path);
 }
 
-void RenderPath::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
-{
-    if (diff == StyleDifferenceLayout)
-        setNeedsBoundariesUpdate();
-    RenderSVGModelObject::styleWillChange(diff, newStyle);
-}
-
 void RenderPath::updateCachedBoundaries()
 {
     if (m_path.isEmpty()) {
index 1bdac07..1d35a01 100644 (file)
@@ -41,8 +41,8 @@ public:
     RenderPath(SVGStyledTransformableElement*);
 
     const Path& path() const { return m_path; }
-    void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
     void setNeedsPathUpdate() { m_needsPathUpdate = true; }
+    virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
     virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
 
 private:
@@ -63,7 +63,6 @@ private:
     virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
 
     virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
-    virtual void styleWillChange(StyleDifference, const RenderStyle*);
 
     FloatRect calculateMarkerBoundsIfNeeded();
     void updateCachedBoundaries();
index bab07a4..8c99270 100644 (file)
@@ -39,6 +39,7 @@ namespace WebCore {
 RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node)
     : RenderSVGModelObject(node)
     , m_drawsContents(true)
+    , m_needsBoundariesUpdate(true)
 {
 }
 
@@ -55,7 +56,7 @@ void RenderSVGContainer::layout()
     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
 
     // Allow RenderSVGTransformableContainer to update its transform.
-    calculateLocalTransform();
+    bool updatedTransform = calculateLocalTransform();
 
     SVGRenderSupport::layoutChildren(this, selfNeedsLayout());
 
@@ -63,6 +64,16 @@ void RenderSVGContainer::layout()
     if (m_everHadLayout && selfNeedsLayout())
         SVGResourcesCache::clientLayoutChanged(this);
 
+    // At this point LayoutRepainter already grabbed the old bounds,
+    // recalculate them now so repaintAfterLayout() uses the new bounds.
+    if (m_needsBoundariesUpdate || updatedTransform) {
+        updateCachedBoundaries();
+        m_needsBoundariesUpdate = false;
+    
+        // If our bounds changed, notify the parents.
+        RenderSVGModelObject::setNeedsBoundariesUpdate();
+    }
+
     repainter.repaintAfterLayout();
     setNeedsLayout(false);
 }
@@ -86,6 +97,10 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
     if (!firstChild() && !selfWillPaint())
         return;
 
+    FloatRect repaintRect = repaintRectInLocalCoordinates();
+    if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(repaintRect, localToParentTransform(), paintInfo))
+        return;
+
     PaintInfo childPaintInfo(paintInfo);
     childPaintInfo.context->save();
 
@@ -115,7 +130,7 @@ void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
     // FIXME: This means our focus ring won't share our rotation like it should.
     // We should instead disable our clip during PaintPhaseOutline
     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) {
-        IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates()));
+        IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect));
         paintOutline(paintInfo.context, paintRectInParent.x(), paintRectInParent.y(), paintRectInParent.width(), paintRectInParent.height());
     }
 }
@@ -128,21 +143,14 @@ void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int)
         rects.append(paintRectInParent);
 }
 
-FloatRect RenderSVGContainer::objectBoundingBox() const
+void RenderSVGContainer::updateCachedBoundaries()
 {
-    return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::ObjectBoundingBox);
-}
+    m_objectBoundingBox = FloatRect();
+    m_strokeBoundingBox = FloatRect();
+    m_repaintBoundingBox = FloatRect();
 
-FloatRect RenderSVGContainer::strokeBoundingBox() const
-{
-    return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::StrokeBoundingBox);
-}
-
-FloatRect RenderSVGContainer::repaintRectInLocalCoordinates() const
-{
-    FloatRect repaintRect = SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::RepaintBoundingBox);
-    SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
-    return repaintRect;
+    SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_strokeBoundingBox, m_repaintBoundingBox);
+    SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
 }
 
 bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
index 0a122cd..b0c952f 100644 (file)
@@ -43,6 +43,7 @@ public:
     bool drawsContents() const { return m_drawsContents; }
 
     virtual void paint(PaintInfo&, int parentX, int parentY);
+    virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
 
 protected:
     virtual RenderObjectChildList* virtualChildren() { return children(); }
@@ -55,14 +56,14 @@ protected:
 
     virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
 
-    virtual FloatRect objectBoundingBox() const;
-    virtual FloatRect strokeBoundingBox() const;
-    virtual FloatRect repaintRectInLocalCoordinates() const;
+    virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; }
+    virtual FloatRect strokeBoundingBox() const { return m_strokeBoundingBox; }
+    virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
 
     virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
 
     // Allow RenderSVGTransformableContainer to hook in at the right time in layout()
-    virtual void calculateLocalTransform() { }
+    virtual bool calculateLocalTransform() { return false; }
 
     // Allow RenderSVGViewportContainer to hook in at the right times in layout(), paint() and nodeAtFloatPoint()
     virtual void calcViewport() { }
@@ -70,10 +71,15 @@ protected:
     virtual bool pointIsInsideViewportClip(const FloatPoint& /*pointInParent*/) { return true; }
 
     bool selfWillPaint();
+    void updateCachedBoundaries();
 
 private:
     RenderObjectChildList m_children;
+    FloatRect m_objectBoundingBox;
+    FloatRect m_strokeBoundingBox;
+    FloatRect m_repaintBoundingBox;
     bool m_drawsContents : 1;
+    bool m_needsBoundariesUpdate : 1;
 };
   
 inline RenderSVGContainer* toRenderSVGContainer(RenderObject* object)
index 45da7b4..f06a9a5 100644 (file)
@@ -46,6 +46,7 @@ public:
     // https://bugs.webkit.org/show_bug.cgi?id=20400
     virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject*) { return IntRect(); }
     virtual FloatRect objectBoundingBox() const { return FloatRect(); }
+    virtual FloatRect strokeBoundingBox() const { return FloatRect(); }
     virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); }
 
 protected:
index 0af4236..ee1d214 100644 (file)
@@ -47,11 +47,6 @@ void RenderSVGHiddenContainer::paint(PaintInfo&, int, int)
     // This subtree does not paint.
 }
 
-IntRect RenderSVGHiddenContainer::clippedOverflowRectForRepaint(RenderBoxModelObject* /*repaintContainer*/)
-{
-    return IntRect();
-}
-
 void RenderSVGHiddenContainer::absoluteQuads(Vector<FloatQuad>&)
 {
     // This subtree does not take up space or paint
@@ -62,16 +57,6 @@ bool RenderSVGHiddenContainer::nodeAtFloatPoint(const HitTestRequest&, HitTestRe
     return false;
 }
 
-FloatRect RenderSVGHiddenContainer::objectBoundingBox() const
-{
-    return FloatRect();
-}
-
-FloatRect RenderSVGHiddenContainer::repaintRectInLocalCoordinates() const
-{
-    return FloatRect();
-}
-
 }
 
 #endif // ENABLE(SVG)
index e0daac1..97800d4 100644 (file)
@@ -47,12 +47,9 @@ namespace WebCore {
 
         virtual void paint(PaintInfo&, int parentX, int parentY);
         
-        virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+        virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject*) { return IntRect(); }
         virtual void absoluteQuads(Vector<FloatQuad>&);
 
-        virtual FloatRect objectBoundingBox() const;
-        virtual FloatRect repaintRectInLocalCoordinates() const;
-
         virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
     };
 }
index 31ff8cb..5e30465 100644 (file)
@@ -57,9 +57,11 @@ void RenderSVGImage::layout()
     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
     SVGImageElement* image = static_cast<SVGImageElement*>(node());
 
+    bool updateCachedBoundariesInParents = false;
     if (m_needsTransformUpdate) {
         m_localTransform = image->animatedLocalTransform();
         m_needsTransformUpdate = false;
+        updateCachedBoundariesInParents = true;
     }
 
     // minimum height
@@ -69,13 +71,21 @@ void RenderSVGImage::layout()
     calcHeight();
 
     // FIXME: Optimize caching the repaint rects.
+    FloatRect oldBoundaries = m_localBounds;
     m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
     m_cachedLocalRepaintRect = FloatRect();
 
+    if (!updateCachedBoundariesInParents)
+        updateCachedBoundariesInParents = oldBoundaries != m_localBounds;
+
     // Invalidate all resources of this client if our layout changed.
     if (m_everHadLayout && selfNeedsLayout())
         SVGResourcesCache::clientLayoutChanged(this);
 
+    // If our bounds changed, notify the parents.
+    if (updateCachedBoundariesInParents)
+        RenderImage::setNeedsBoundariesUpdate();
+
     repainter.repaintAfterLayout();
     setNeedsLayout(false);
 }
index cb2dfe1..3fd439c 100644 (file)
@@ -48,6 +48,7 @@ namespace WebCore {
 RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node)
     : RenderBox(node)
     , m_isLayoutSizeChanged(false)
+    , m_needsBoundariesOrTransformUpdate(true)
 {
     setReplaced(true);
 }
@@ -124,6 +125,13 @@ void RenderSVGRoot::layout()
     SVGRenderSupport::layoutChildren(this, needsLayout);
     m_isLayoutSizeChanged = false;
 
+    // At this point LayoutRepainter already grabbed the old bounds,
+    // recalculate them now so repaintAfterLayout() uses the new bounds.
+    if (m_needsBoundariesOrTransformUpdate) {
+        updateCachedBoundaries();
+        m_needsBoundariesOrTransformUpdate = false;
+    }
+
     repainter.repaintAfterLayout();
 
     view()->enableLayoutState();
@@ -274,28 +282,6 @@ const AffineTransform& RenderSVGRoot::localToParentTransform() const
     return m_localToParentTransform;
 }
 
-FloatRect RenderSVGRoot::objectBoundingBox() const
-{
-    return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::ObjectBoundingBox);
-}
-
-FloatRect RenderSVGRoot::strokeBoundingBox() const
-{
-    return SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::StrokeBoundingBox);
-}
-
-FloatRect RenderSVGRoot::repaintRectInLocalCoordinates() const
-{
-    FloatRect repaintRect = SVGRenderSupport::computeContainerBoundingBox(this, SVGRenderSupport::RepaintBoundingBox);
-
-    const SVGRenderStyle* svgStyle = style()->svgStyle();
-    if (const ShadowData* shadow = svgStyle->shadow())
-        shadow->adjustRectForShadow(repaintRect);
-
-    repaintRect.inflate(borderAndPaddingWidth());
-    return repaintRect;
-}
-
 IntRect RenderSVGRoot::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
 {
     return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
@@ -327,6 +313,17 @@ void RenderSVGRoot::mapLocalToContainer(RenderBoxModelObject* repaintContainer,
     RenderBox::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
 }
 
+void RenderSVGRoot::updateCachedBoundaries()
+{
+    m_objectBoundingBox = FloatRect();
+    m_strokeBoundingBox = FloatRect();
+    m_repaintBoundingBox = FloatRect();
+
+    SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_strokeBoundingBox, m_repaintBoundingBox);
+    SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
+    m_repaintBoundingBox.inflate(borderAndPaddingWidth());
+}
+
 bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
 {
     IntPoint pointInContainer(_x, _y);
index 459e43a..63a7b3f 100644 (file)
@@ -41,6 +41,8 @@ public:
     RenderObjectChildList* children() { return &m_children; }
 
     bool isLayoutSizeChanged() const { return m_isLayoutSizeChanged; }
+    virtual void setNeedsBoundariesUpdate() { m_needsBoundariesOrTransformUpdate = true; }
+    virtual void setNeedsTransformUpdate() { m_needsBoundariesOrTransformUpdate = true; }
 
 private:
     virtual RenderObjectChildList* virtualChildren() { return children(); }
@@ -67,9 +69,9 @@ private:
     bool fillContains(const FloatPoint&) const;
     bool strokeContains(const FloatPoint&) const;
 
-    virtual FloatRect objectBoundingBox() const;
-    virtual FloatRect strokeBoundingBox() const;
-    virtual FloatRect repaintRectInLocalCoordinates() const;
+    virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; }
+    virtual FloatRect strokeBoundingBox() const { return m_strokeBoundingBox; }
+    virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
 
     virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
 
@@ -81,6 +83,7 @@ private:
     void calcViewport();
 
     bool selfWillPaint();
+    void updateCachedBoundaries();
 
     IntSize parentOriginToBorderBox() const;
     IntSize borderOriginToContentBox() const;
@@ -89,8 +92,12 @@ private:
 
     RenderObjectChildList m_children;
     FloatSize m_viewportSize;
+    FloatRect m_objectBoundingBox;
+    FloatRect m_strokeBoundingBox;
+    FloatRect m_repaintBoundingBox;
     mutable AffineTransform m_localToParentTransform;
     bool m_isLayoutSizeChanged : 1;
+    bool m_needsBoundariesOrTransformUpdate : 1;
 };
 
 inline RenderSVGRoot* toRenderSVGRoot(RenderObject* object)
index 4b70643..80b8a91 100644 (file)
@@ -75,10 +75,12 @@ void RenderSVGText::layout()
     ASSERT(needsLayout());
     LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
 
+    bool updateCachedBoundariesInParents = false;
     if (m_needsTransformUpdate) {
         SVGTextElement* text = static_cast<SVGTextElement*>(node());
         m_localTransform = text->animatedLocalTransform();
         m_needsTransformUpdate = false;
+        updateCachedBoundariesInParents = true;
     }
 
     // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
@@ -95,13 +97,22 @@ void RenderSVGText::layout()
     if (!firstChild())
         setChildrenInline(true);
 
+    // FIXME: We need to find a way to only layout the child boxes, if needed.
+    FloatRect oldBoundaries = objectBoundingBox();
     ASSERT(childrenInline());
     forceLayoutInlineChildren();
 
+    if (!updateCachedBoundariesInParents)
+        updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
+
     // Invalidate all resources of this client if our layout changed.
     if (m_everHadLayout && selfNeedsLayout())
         SVGResourcesCache::clientLayoutChanged(this);
 
+    // If our bounds changed, notify the parents.
+    if (updateCachedBoundariesInParents)
+        RenderSVGBlock::setNeedsBoundariesUpdate();
+
     repainter.repaintAfterLayout();
     setNeedsLayout(false);
 }
index 94b9eea..c7d2029 100644 (file)
@@ -35,7 +35,7 @@ RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransf
 {
 }
 
-void RenderSVGTransformableContainer::calculateLocalTransform()
+bool RenderSVGTransformableContainer::calculateLocalTransform()
 {
     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
 
@@ -46,17 +46,18 @@ void RenderSVGTransformableContainer::calculateLocalTransform()
     }
 
     if (!element->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(element)->isShadowTreeContainerElement())
-        return;
+        return needsUpdate;
 
     FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(element)->containerTranslation();
     if (translation.width() == 0 && translation.height() == 0)
-        return;
+        return needsUpdate;
 
     // FIXME: Could optimize this case for use to avoid refetching the animatedLocalTransform() here, if only the containerTranslation() changed.
     if (!needsUpdate)
         m_localTransform = element->animatedLocalTransform();
 
     m_localTransform.translate(translation.width(), translation.height());
+    return true;
 }
 
 }
index e6de054..b63b91c 100644 (file)
@@ -35,7 +35,7 @@ namespace WebCore {
         virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
 
     private:
-        virtual void calculateLocalTransform();
+        virtual bool calculateLocalTransform();
         virtual AffineTransform localTransform() const { return m_localTransform; }
 
         bool m_needsTransformUpdate : 1;
index f314bde..ccf67fa 100644 (file)
@@ -48,10 +48,15 @@ void RenderSVGViewportContainer::calcViewport()
      SVGElement* element = static_cast<SVGElement*>(node());
      if (element->hasTagName(SVGNames::svgTag)) {
          SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
+
+         FloatRect oldViewport = m_viewport;
          m_viewport = FloatRect(svg->x().value(svg)
                                 , svg->y().value(svg)
                                 , svg->width().value(svg)
                                 , svg->height().value(svg));
+
+        if (oldViewport != m_viewport)
+            setNeedsBoundariesUpdate();
     }
 }
 
index e6cc032..644ff70 100644 (file)
@@ -161,32 +161,31 @@ void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& p
         paintInfo.context->endTransparencyLayer();
 }
 
-FloatRect SVGRenderSupport::computeContainerBoundingBox(const RenderObject* container, ContainerBoundingBoxMode mode)
+void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
 {
-    FloatRect boundingBox;
-
     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
         if (current->isSVGHiddenContainer())
             continue;
 
-        FloatRect childBoundingBox;
-
-        switch (mode) {
-        case ObjectBoundingBox:
-            childBoundingBox = current->objectBoundingBox();
-            break;
-        case StrokeBoundingBox:
-            childBoundingBox = current->strokeBoundingBox();
-            break;
-        case RepaintBoundingBox:
-            childBoundingBox = current->repaintRectInLocalCoordinates();
-            break;
+        const AffineTransform& transform = current->localToParentTransform();
+        if (transform.isIdentity()) {
+            objectBoundingBox.unite(current->objectBoundingBox());
+            strokeBoundingBox.unite(current->strokeBoundingBox());
+            repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
+        } else {
+            objectBoundingBox.unite(transform.mapRect(current->objectBoundingBox()));
+            strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
+            repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
         }
-
-        boundingBox.unite(current->localToParentTransform().mapRect(childBoundingBox));
     }
+}
+
+bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
+{
+    if (localTransform.isIdentity())
+        return localRepaintRect.intersects(paintInfo.rect);
 
-    return boundingBox;
+    return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
 }
 
 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
index 2de1e99..54622d2 100644 (file)
@@ -58,15 +58,8 @@ public:
     // Determines whether the passed point lies in a clipping area
     static bool pointInClippingArea(RenderObject*, const FloatPoint&);
 
-    enum ContainerBoundingBoxMode {
-        ObjectBoundingBox,
-        StrokeBoundingBox,
-        RepaintBoundingBox
-    };
-
-    // Used to share the "walk all the children" logic between objectBoundingBox
-    // and repaintRectInLocalCoordinates in RenderSVGRoot and RenderSVGContainer
-    static FloatRect computeContainerBoundingBox(const RenderObject* container, ContainerBoundingBoxMode);
+    static void computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox);
+    static bool paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo);
 
     // Important functions used by nearly all SVG renderers centralizing coordinate transformations / repaint rect calculations
     static IntRect clippedOverflowRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer);