2010-04-13 Nikolas Zimmermann <nzimmermann@rim.com>
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Apr 2010 10:35:19 +0000 (10:35 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 Apr 2010 10:35:19 +0000 (10:35 +0000)
        Reviewed by Dirk Schulze.

        SVG renderers should track transform/path changes, instead of pulling every time from SVG DOM
        https://bugs.webkit.org/show_bug.cgi?id=15389

        RenderPath caches repaint rectangles (fill/stroke bbox etc.) though this caching
        was effectively useless because every layout() call caused them to be reset to empty rects.
        Furthermore RenderPath::layout() queried the SVG DOM upon every invocation to retrieve
        the Path object, instead of only doing it when necessary. Even the TransformationMatrix
        was polled everytime from the SVG DOM.

        Move the knownledge wheter we need to update path/transform into the render tree and
        only update when necessary. This should result in a huge performance increase, with
        the drawback of adding slightly more memory, because we need to add booleans indicating
        the status of path/transform (is dirty?).

        Doesn't affect any tests, only performance.

        * rendering/RenderForeignObject.cpp:
        (WebCore::RenderForeignObject::RenderForeignObject):
        (WebCore::RenderForeignObject::layout):
        * rendering/RenderForeignObject.h:
        (WebCore::RenderForeignObject::setNeedsTransformUpdate):
        * rendering/RenderObject.h:
        (WebCore::RenderObject::setNeedsTransformUpdate):
        (WebCore::RenderObject::setNeedsBoundariesUpdate):
        * rendering/RenderPath.cpp:
        (WebCore::RenderPath::RenderPath):
        (WebCore::RenderPath::layout):
        (WebCore::RenderPath::invalidateCachedBoundaries):
        (WebCore::RenderPath::styleWillChange):
        * rendering/RenderPath.h:
        (WebCore::RenderPath::setNeedsBoundariesUpdate):
        (WebCore::RenderPath::setNeedsPathUpdate):
        (WebCore::RenderPath::setNeedsTransformUpdate):
        (WebCore::RenderPath::localToParentTransform):
        (WebCore::RenderPath::localTransform):
        * rendering/RenderSVGImage.cpp:
        (WebCore::RenderSVGImage::RenderSVGImage):
        (WebCore::RenderSVGImage::layout):
        * rendering/RenderSVGImage.h:
        (WebCore::RenderSVGImage::setNeedsTransformUpdate):
        * rendering/RenderSVGResourceClipper.cpp:
        (WebCore::RenderSVGResourceClipper::invalidateClients):
        * rendering/RenderSVGResourceMarker.cpp:
        (WebCore::RenderSVGResourceMarker::invalidateClients):
        * rendering/RenderSVGResourceMasker.cpp:
        (WebCore::RenderSVGResourceMasker::invalidateClients):
        * rendering/RenderSVGText.cpp:
        (WebCore::RenderSVGText::RenderSVGText):
        (WebCore::RenderSVGText::layout):
        * rendering/RenderSVGText.h:
        (WebCore::RenderSVGText::setNeedsTransformUpdate):
        * rendering/RenderSVGTransformableContainer.cpp:
        (WebCore::RenderSVGTransformableContainer::RenderSVGTransformableContainer):
        (WebCore::RenderSVGTransformableContainer::calculateLocalTransform):
        * rendering/RenderSVGTransformableContainer.h:
        (WebCore::RenderSVGTransformableContainer::localToParentTransform):
        (WebCore::RenderSVGTransformableContainer::setNeedsTransformUpdate):
        (WebCore::RenderSVGTransformableContainer::localTransform):
        * svg/SVGAnimateMotionElement.cpp:
        (WebCore::SVGAnimateMotionElement::calculateAnimatedValue):
        (WebCore::SVGAnimateMotionElement::applyResultsToTarget):
        * svg/SVGAnimateTransformElement.cpp:
        (WebCore::SVGAnimateTransformElement::applyResultsToTarget):
        * svg/SVGCircleElement.cpp:
        (WebCore::SVGCircleElement::svgAttributeChanged):
        * svg/SVGEllipseElement.cpp:
        (WebCore::SVGEllipseElement::svgAttributeChanged):
        * svg/SVGForeignObjectElement.cpp:
        (WebCore::SVGForeignObjectElement::svgAttributeChanged):
        * svg/SVGGElement.cpp:
        (WebCore::SVGGElement::svgAttributeChanged):
        * svg/SVGImageElement.cpp:
        (WebCore::SVGImageElement::svgAttributeChanged):
        * svg/SVGLineElement.cpp:
        (WebCore::SVGLineElement::svgAttributeChanged):
        * svg/SVGPathElement.cpp:
        (WebCore::SVGPathElement::svgAttributeChanged):
        * svg/SVGPolyElement.cpp:
        (WebCore::SVGPolyElement::svgAttributeChanged):
        * svg/SVGRectElement.cpp:
        (WebCore::SVGRectElement::svgAttributeChanged):
        * svg/SVGTextElement.cpp:
        (WebCore::SVGTextElement::svgAttributeChanged):
        * svg/SVGUseElement.cpp:
        (WebCore::SVGUseElement::svgAttributeChanged):

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

28 files changed:
WebCore/ChangeLog
WebCore/rendering/RenderForeignObject.cpp
WebCore/rendering/RenderForeignObject.h
WebCore/rendering/RenderObject.h
WebCore/rendering/RenderPath.cpp
WebCore/rendering/RenderPath.h
WebCore/rendering/RenderSVGImage.cpp
WebCore/rendering/RenderSVGImage.h
WebCore/rendering/RenderSVGResourceClipper.cpp
WebCore/rendering/RenderSVGResourceMarker.cpp
WebCore/rendering/RenderSVGResourceMasker.cpp
WebCore/rendering/RenderSVGText.cpp
WebCore/rendering/RenderSVGText.h
WebCore/rendering/RenderSVGTransformableContainer.cpp
WebCore/rendering/RenderSVGTransformableContainer.h
WebCore/svg/SVGAnimateMotionElement.cpp
WebCore/svg/SVGAnimateTransformElement.cpp
WebCore/svg/SVGCircleElement.cpp
WebCore/svg/SVGEllipseElement.cpp
WebCore/svg/SVGForeignObjectElement.cpp
WebCore/svg/SVGGElement.cpp
WebCore/svg/SVGImageElement.cpp
WebCore/svg/SVGLineElement.cpp
WebCore/svg/SVGPathElement.cpp
WebCore/svg/SVGPolyElement.cpp
WebCore/svg/SVGRectElement.cpp
WebCore/svg/SVGTextElement.cpp
WebCore/svg/SVGUseElement.cpp

index 19ea9c9..880d70a 100644 (file)
@@ -1,3 +1,93 @@
+2010-04-13  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Reviewed by Dirk Schulze.
+
+        SVG renderers should track transform/path changes, instead of pulling every time from SVG DOM
+        https://bugs.webkit.org/show_bug.cgi?id=15389
+
+        RenderPath caches repaint rectangles (fill/stroke bbox etc.) though this caching
+        was effectively useless because every layout() call caused them to be reset to empty rects.
+        Furthermore RenderPath::layout() queried the SVG DOM upon every invocation to retrieve
+        the Path object, instead of only doing it when necessary. Even the TransformationMatrix
+        was polled everytime from the SVG DOM.
+
+        Move the knownledge wheter we need to update path/transform into the render tree and
+        only update when necessary. This should result in a huge performance increase, with
+        the drawback of adding slightly more memory, because we need to add booleans indicating
+        the status of path/transform (is dirty?).
+
+        Doesn't affect any tests, only performance.
+
+        * rendering/RenderForeignObject.cpp:
+        (WebCore::RenderForeignObject::RenderForeignObject):
+        (WebCore::RenderForeignObject::layout):
+        * rendering/RenderForeignObject.h:
+        (WebCore::RenderForeignObject::setNeedsTransformUpdate):
+        * rendering/RenderObject.h:
+        (WebCore::RenderObject::setNeedsTransformUpdate):
+        (WebCore::RenderObject::setNeedsBoundariesUpdate):
+        * rendering/RenderPath.cpp:
+        (WebCore::RenderPath::RenderPath):
+        (WebCore::RenderPath::layout):
+        (WebCore::RenderPath::invalidateCachedBoundaries):
+        (WebCore::RenderPath::styleWillChange):
+        * rendering/RenderPath.h:
+        (WebCore::RenderPath::setNeedsBoundariesUpdate):
+        (WebCore::RenderPath::setNeedsPathUpdate):
+        (WebCore::RenderPath::setNeedsTransformUpdate):
+        (WebCore::RenderPath::localToParentTransform):
+        (WebCore::RenderPath::localTransform):
+        * rendering/RenderSVGImage.cpp:
+        (WebCore::RenderSVGImage::RenderSVGImage):
+        (WebCore::RenderSVGImage::layout):
+        * rendering/RenderSVGImage.h:
+        (WebCore::RenderSVGImage::setNeedsTransformUpdate):
+        * rendering/RenderSVGResourceClipper.cpp:
+        (WebCore::RenderSVGResourceClipper::invalidateClients):
+        * rendering/RenderSVGResourceMarker.cpp:
+        (WebCore::RenderSVGResourceMarker::invalidateClients):
+        * rendering/RenderSVGResourceMasker.cpp:
+        (WebCore::RenderSVGResourceMasker::invalidateClients):
+        * rendering/RenderSVGText.cpp:
+        (WebCore::RenderSVGText::RenderSVGText):
+        (WebCore::RenderSVGText::layout):
+        * rendering/RenderSVGText.h:
+        (WebCore::RenderSVGText::setNeedsTransformUpdate):
+        * rendering/RenderSVGTransformableContainer.cpp:
+        (WebCore::RenderSVGTransformableContainer::RenderSVGTransformableContainer):
+        (WebCore::RenderSVGTransformableContainer::calculateLocalTransform):
+        * rendering/RenderSVGTransformableContainer.h:
+        (WebCore::RenderSVGTransformableContainer::localToParentTransform):
+        (WebCore::RenderSVGTransformableContainer::setNeedsTransformUpdate):
+        (WebCore::RenderSVGTransformableContainer::localTransform):
+        * svg/SVGAnimateMotionElement.cpp:
+        (WebCore::SVGAnimateMotionElement::calculateAnimatedValue):
+        (WebCore::SVGAnimateMotionElement::applyResultsToTarget):
+        * svg/SVGAnimateTransformElement.cpp:
+        (WebCore::SVGAnimateTransformElement::applyResultsToTarget):
+        * svg/SVGCircleElement.cpp:
+        (WebCore::SVGCircleElement::svgAttributeChanged):
+        * svg/SVGEllipseElement.cpp:
+        (WebCore::SVGEllipseElement::svgAttributeChanged):
+        * svg/SVGForeignObjectElement.cpp:
+        (WebCore::SVGForeignObjectElement::svgAttributeChanged):
+        * svg/SVGGElement.cpp:
+        (WebCore::SVGGElement::svgAttributeChanged):
+        * svg/SVGImageElement.cpp:
+        (WebCore::SVGImageElement::svgAttributeChanged):
+        * svg/SVGLineElement.cpp:
+        (WebCore::SVGLineElement::svgAttributeChanged):
+        * svg/SVGPathElement.cpp:
+        (WebCore::SVGPathElement::svgAttributeChanged):
+        * svg/SVGPolyElement.cpp:
+        (WebCore::SVGPolyElement::svgAttributeChanged):
+        * svg/SVGRectElement.cpp:
+        (WebCore::SVGRectElement::svgAttributeChanged):
+        * svg/SVGTextElement.cpp:
+        (WebCore::SVGTextElement::svgAttributeChanged):
+        * svg/SVGUseElement.cpp:
+        (WebCore::SVGUseElement::svgAttributeChanged):
+
 2010-04-13  Mikhail Naganov  <mnaganov@chromium.org>
 
         Unreviewed Qt build fix: add new .idl files to WebCore.pri
index aa28ff0..9f0889b 100644 (file)
@@ -36,6 +36,7 @@ namespace WebCore {
 
 RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) 
     : RenderSVGBlock(node)
+    , m_needsTransformUpdate(true)
 {
 }
 
@@ -99,9 +100,12 @@ void RenderForeignObject::layout()
     ASSERT(!view()->layoutStateEnabled()); // RenderSVGRoot disables layoutState for the SVG rendering tree.
 
     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
-
     SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(node());
-    m_localTransform = foreign->animatedLocalTransform();
+
+    if (m_needsTransformUpdate) {
+        m_localTransform = foreign->animatedLocalTransform();
+        m_needsTransformUpdate = false;
+    }
 
     // Cache viewport boundaries
     FloatPoint viewportLocation(foreign->x().value(foreign), foreign->y().value(foreign));
index bb6b555..d8c1f68 100644 (file)
@@ -54,6 +54,7 @@ public:
     virtual bool isSVGForeignObject() const { return true; }
 
     virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const;
+    virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
 
  private:
     virtual void calcWidth();
@@ -62,6 +63,7 @@ public:
     virtual const AffineTransform& localToParentTransform() const;
     virtual AffineTransform localTransform() const { return m_localTransform; }
 
+    bool m_needsTransformUpdate : 1;
     FloatRect m_viewport;
     AffineTransform m_localTransform;
     mutable AffineTransform m_localToParentTransform;
index 11363ca..78645b5 100644 (file)
@@ -341,6 +341,12 @@ public:
     virtual const SVGRenderBase* toSVGRenderBase() const;
     virtual RenderSVGResource* toRenderSVGResource();
 
+    // FIXME: Those belong into a SVG specific base-class for all renderers (see above)
+    // 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() { }
+
     // 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().
     // NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we ignore them
@@ -353,7 +359,6 @@ public:
     // respecting clipping, masking, filters, opacity, stroke-width and markers
     virtual FloatRect repaintRectInLocalCoordinates() const;
 
-    // FIXME: This accessor is deprecated and mostly around for SVGRenderTreeAsText.
     // This only returns the transform="" value from the element
     // most callsites want localToParentTransform() instead.
     virtual AffineTransform localTransform() const;
index dee7d33..11bc9fb 100644 (file)
@@ -64,19 +64,12 @@ private:
 
 RenderPath::RenderPath(SVGStyledTransformableElement* node)
     : RenderSVGModelObject(node)
+    , m_needsBoundariesUpdate(false) // default is false, as this is only used when a RenderSVGResource tells us that the boundaries need to be recached
+    , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement
+    , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement
 {
 }
 
-const AffineTransform& RenderPath::localToParentTransform() const
-{
-    return m_localTransform;
-}
-
-AffineTransform RenderPath::localTransform() const
-{
-    return m_localTransform;
-}
-
 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
 {
     if (m_path.isEmpty())
@@ -171,22 +164,34 @@ FloatRect RenderPath::repaintRectInLocalCoordinates() const
     return m_cachedLocalRepaintRect;
 }
 
-void RenderPath::setPath(const Path& newPath)
-{
-    m_path = newPath;
-    m_cachedLocalRepaintRect = FloatRect();
-    m_cachedLocalStrokeBBox = FloatRect();
-    m_cachedLocalFillBBox = FloatRect();
-    m_cachedLocalMarkerBBox = FloatRect();
-}
-
 void RenderPath::layout()
 {
     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
-
     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
-    m_localTransform = element->animatedLocalTransform();
-    setPath(element->toPathData());
+
+    // We need to update the Path object whenever the underlying SVGStyledTransformableElement uses relative values
+    // as the viewport size may have changed. It would be nice to optimize this to detect these changes, and only
+    // update when needed, even when using relative values.
+    if (!m_needsPathUpdate && element->hasRelativeValues())
+        m_needsPathUpdate = true;
+
+    bool needsUpdate = m_needsPathUpdate || m_needsTransformUpdate || m_needsBoundariesUpdate;
+
+    if (m_needsBoundariesUpdate)
+        m_needsBoundariesUpdate = false;
+
+    if (m_needsPathUpdate) {
+        m_path = element->toPathData();
+        m_needsPathUpdate = false;
+    }
+
+    if (m_needsTransformUpdate) {
+        m_localTransform = element->animatedLocalTransform();
+        m_needsTransformUpdate = false;
+    }
+
+    if (needsUpdate)
+        invalidateCachedBoundaries();
 
     repainter.repaintAfterLayout();
     setNeedsLayout(false);
@@ -326,6 +331,20 @@ void RenderPath::calculateMarkerBoundsIfNeeded() const
     m_cachedLocalMarkerBBox = m_markerLayoutInfo.calculateBoundaries(startMarker, midMarker, endMarker, strokeWidth, m_path);
 }
 
+void RenderPath::invalidateCachedBoundaries()
+{
+    m_cachedLocalRepaintRect = FloatRect();
+    m_cachedLocalStrokeBBox = FloatRect();
+    m_cachedLocalFillBBox = FloatRect();
+    m_cachedLocalMarkerBBox = FloatRect();
+}
+
+void RenderPath::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+    invalidateCachedBoundaries();
+    RenderSVGModelObject::styleWillChange(diff, newStyle);
+}
+
 }
 
 #endif // ENABLE(SVG)
index d530f3c..ea4de40 100644 (file)
@@ -41,6 +41,9 @@ public:
     RenderPath(SVGStyledTransformableElement*);
 
     const Path& path() const { return m_path; }
+    void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
+    void setNeedsPathUpdate() { m_needsPathUpdate = true; }
+    virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
 
 private:
     // Hit-detection seperated for the fill and the stroke
@@ -52,9 +55,7 @@ private:
     virtual FloatRect markerBoundingBox() const;
     virtual FloatRect repaintRectInLocalCoordinates() const;
 
-    virtual const AffineTransform& localToParentTransform() const;
-
-    void setPath(const Path&);
+    virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
 
     virtual bool isRenderPath() const { return true; }
     virtual const char* renderName() const { return "RenderPath"; }
@@ -64,11 +65,17 @@ 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*);
 
     void calculateMarkerBoundsIfNeeded() const;
+    void invalidateCachedBoundaries();
 
 private:
-    virtual AffineTransform localTransform() const;
+    virtual AffineTransform localTransform() const { return m_localTransform; }
+
+    bool m_needsBoundariesUpdate : 1;
+    bool m_needsPathUpdate : 1;
+    bool m_needsTransformUpdate : 1;
 
     mutable Path m_path;
     mutable FloatRect m_cachedLocalFillBBox;
index 5a740d7..fd2e0b7 100644 (file)
@@ -42,6 +42,7 @@ namespace WebCore {
 
 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
     : RenderImage(impl)
+    , m_needsTransformUpdate(true)
 {
 }
 
@@ -50,10 +51,13 @@ void RenderSVGImage::layout()
     ASSERT(needsLayout());
 
     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
-
     SVGImageElement* image = static_cast<SVGImageElement*>(node());
-    m_localTransform = image->animatedLocalTransform();
-    
+
+    if (m_needsTransformUpdate) {
+        m_localTransform = image->animatedLocalTransform();
+        m_needsTransformUpdate = false;
+    }
+
     // minimum height
     setHeight(errorOccurred() ? intrinsicSize().height() : 0);
 
index f48b9dd..120ac72 100644 (file)
@@ -38,6 +38,8 @@ class RenderSVGImage : public RenderImage, protected SVGRenderBase {
 public:
     RenderSVGImage(SVGImageElement*);
 
+    virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+
 private:
     virtual const SVGRenderBase* toSVGRenderBase() const { return this; }
     virtual const char* renderName() const { return "RenderSVGImage"; }
@@ -72,6 +74,7 @@ private:
 
     virtual AffineTransform localTransform() const { return m_localTransform; }
 
+    bool m_needsTransformUpdate : 1;
     AffineTransform m_localTransform;
     FloatRect m_localBounds;
     mutable FloatRect m_cachedLocalRepaintRect;
index a15442f..61a01e5 100644 (file)
@@ -51,8 +51,12 @@ RenderSVGResourceClipper::~RenderSVGResourceClipper()
 void RenderSVGResourceClipper::invalidateClients()
 {
     HashSet<RenderObject*>::const_iterator end = m_clipper.end();
-    for (HashSet<RenderObject*>::const_iterator it = m_clipper.begin(); it != end; ++it)
-        (*it)->setNeedsLayout(true);
+    for (HashSet<RenderObject*>::const_iterator it = m_clipper.begin(); it != end; ++it) {
+        RenderObject* renderer = *it;
+        renderer->setNeedsBoundariesUpdate();
+        renderer->setNeedsLayout(true);
+    }
+
     m_clipper.clear();
 }
 
index 9272a1a..c526962 100644 (file)
@@ -60,9 +60,13 @@ void RenderSVGResourceMarker::addClient(const RenderObject* object)
 
 void RenderSVGResourceMarker::invalidateClients()
 {
-    HashSet<const RenderObject*>::const_iterator end = m_marker.end();
-    for (HashSet<const RenderObject*>::const_iterator it = m_marker.begin(); it != end; ++it)
-        const_cast<RenderObject*>(*it)->setNeedsLayout(true);
+    const HashSet<const RenderObject*>::const_iterator end = m_marker.end();
+    for (HashSet<const RenderObject*>::const_iterator it = m_marker.begin(); it != end; ++it) {
+        RenderObject* renderer = const_cast<RenderObject*>(*it);
+        renderer->setNeedsBoundariesUpdate();
+        renderer->setNeedsLayout(true);
+    }
+
     m_marker.clear();
 }
 
index de87d5a..020b05f 100644 (file)
@@ -56,8 +56,12 @@ RenderSVGResourceMasker::~RenderSVGResourceMasker()
 void RenderSVGResourceMasker::invalidateClients()
 {
     HashMap<RenderObject*, MaskerData*>::const_iterator end = m_masker.end();
-    for (HashMap<RenderObject*, MaskerData*>::const_iterator it = m_masker.begin(); it != end; ++it)
-        it->first->setNeedsLayout(true);
+    for (HashMap<RenderObject*, MaskerData*>::const_iterator it = m_masker.begin(); it != end; ++it) {
+        RenderObject* renderer = it->first;
+        renderer->setNeedsBoundariesUpdate();
+        renderer->setNeedsLayout(true);
+    }
+
     deleteAllValues(m_masker);
     m_masker.clear();
 }
index 76b8b86..e945a6c 100644 (file)
@@ -49,6 +49,7 @@ namespace WebCore {
 
 RenderSVGText::RenderSVGText(SVGTextElement* node) 
     : RenderSVGBlock(node)
+    , m_needsTransformUpdate(true)
 {
 }
 
@@ -78,7 +79,10 @@ void RenderSVGText::layout()
     int yOffset = (int)(text->y()->getFirst().value(text));
     setLocation(xOffset, yOffset);
 
-    m_localTransform = text->animatedLocalTransform();
+    if (m_needsTransformUpdate) {
+        m_localTransform = text->animatedLocalTransform();
+        m_needsTransformUpdate = false;
+    }
 
     RenderBlock::layout();
 
index ab4b09b..fe53086 100644 (file)
@@ -37,6 +37,8 @@ class RenderSVGText : public RenderSVGBlock {
 public:
     RenderSVGText(SVGTextElement* node);
 
+    virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+
 private:
     virtual const char* renderName() const { return "RenderSVGText"; }
 
@@ -67,11 +69,11 @@ private:
     virtual FloatRect strokeBoundingBox() const;
     virtual FloatRect repaintRectInLocalCoordinates() const;
 
-    // FIXME: This can be removed when localTransform() is removed from RenderObject
     virtual AffineTransform localTransform() const { return m_localTransform; }
 
     virtual RootInlineBox* createRootInlineBox();
 
+    bool m_needsTransformUpdate : 1;
     AffineTransform m_localTransform;
 };
 
index 4bec7a7..94b9eea 100644 (file)
@@ -31,29 +31,31 @@ namespace WebCore {
     
 RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransformableElement* node)
     : RenderSVGContainer(node)
+    , m_needsTransformUpdate(true)
 {
 }
 
-const AffineTransform& RenderSVGTransformableContainer::localToParentTransform() const
+void RenderSVGTransformableContainer::calculateLocalTransform()
 {
-    return m_localTransform;
-}
+    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
 
-AffineTransform RenderSVGTransformableContainer::localTransform() const
-{
-    return m_localTransform;
-}
+    bool needsUpdate = m_needsTransformUpdate;
+    if (needsUpdate) {
+        m_localTransform = element->animatedLocalTransform();
+        m_needsTransformUpdate = false;
+    }
 
-void RenderSVGTransformableContainer::calculateLocalTransform()
-{
-    m_localTransform = static_cast<SVGStyledTransformableElement*>(node())->animatedLocalTransform();
-    if (!node()->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(node())->isShadowTreeContainerElement())
+    if (!element->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(element)->isShadowTreeContainerElement())
         return;
 
-    FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(node())->containerTranslation();
+    FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(element)->containerTranslation();
     if (translation.width() == 0 && translation.height() == 0)
         return;
 
+    // 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());
 }
 
index 1de0b19..e6de054 100644 (file)
@@ -31,13 +31,14 @@ namespace WebCore {
     public:
         RenderSVGTransformableContainer(SVGStyledTransformableElement*);
 
-        virtual const AffineTransform& localToParentTransform() const;
+        virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
+        virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
 
     private:
         virtual void calculateLocalTransform();
-        // FIXME: This can be made non-virtual once SVGRenderTreeAsText stops using localTransform()
-        virtual AffineTransform localTransform() const;
+        virtual AffineTransform localTransform() const { return m_localTransform; }
 
+        bool m_needsTransformUpdate : 1;
         AffineTransform m_localTransform;
     };
 }
index 9355436..868a48d 100644 (file)
@@ -179,7 +179,10 @@ void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned,
     AffineTransform* transform = target->supplementalTransform();
     if (!transform)
         return;
-    
+
+    if (target->renderer())
+        target->renderer()->setNeedsTransformUpdate();
+
     if (!isAdditive())
         transform->makeIdentity();
     
@@ -223,8 +226,10 @@ void SVGAnimateMotionElement::applyResultsToTarget()
         AffineTransform* transform = shadowTreeElement->supplementalTransform();
         AffineTransform* t = targetElement->supplementalTransform();
         transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
-        if (shadowTreeElement->renderer())
-            shadowTreeElement->renderer()->setNeedsLayout(true);
+        if (RenderObject* renderer = shadowTreeElement->renderer()) {
+            renderer->setNeedsTransformUpdate();
+            renderer->setNeedsLayout(true);
+        }
     }
 }
 
index 8e077a4..96485a6 100644 (file)
@@ -159,9 +159,11 @@ void SVGAnimateTransformElement::applyResultsToTarget()
         return;
     // We accumulate to the target element transform list so there is not much to do here.
     SVGElement* targetElement = this->targetElement();
-    if (targetElement->renderer())
-        targetElement->renderer()->setNeedsLayout(true);
-    
+    if (RenderObject* renderer = targetElement->renderer()) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+    }
+
     // ...except in case where we have additional instances in <use> trees.
     const HashSet<SVGElementInstance*>& instances = targetElement->instancesForElement();
     RefPtr<SVGTransformList> transformList = transformListFor(targetElement);
@@ -173,8 +175,10 @@ void SVGAnimateTransformElement::applyResultsToTarget()
             static_cast<SVGStyledTransformableElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
         else if (shadowTreeElement->hasTagName(SVGNames::textTag))
             static_cast<SVGTextElement*>(shadowTreeElement)->setTransformBaseValue(transformList.get());
-        if (shadowTreeElement->renderer())
-            shadowTreeElement->renderer()->setNeedsLayout(true);
+        if (RenderObject* renderer = shadowTreeElement->renderer()) {
+            renderer->setNeedsTransformUpdate();
+            renderer->setNeedsLayout(true);
+        }
     }
 }
     
index 10da742..12ccf69 100644 (file)
@@ -71,16 +71,28 @@ void SVGCircleElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::cxAttr || attrName == SVGNames::cyAttr ||
-        attrName == SVGNames::rAttr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::cxAttr
+        || attrName == SVGNames::cyAttr
+        || attrName == SVGNames::rAttr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGCircleElement::synchronizeProperty(const QualifiedName& attrName)
index a7400fa..76f374f 100644 (file)
@@ -76,16 +76,29 @@ void SVGEllipseElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::cxAttr || attrName == SVGNames::cyAttr ||
-        attrName == SVGNames::rxAttr || attrName == SVGNames::ryAttr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::cxAttr
+        || attrName == SVGNames::cyAttr
+        || attrName == SVGNames::rxAttr
+        || attrName == SVGNames::ryAttr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGEllipseElement::synchronizeProperty(const QualifiedName& attrName)
index d28e2a4..f9ab057 100644 (file)
@@ -76,18 +76,24 @@ void SVGForeignObjectElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderObject* renderer = this->renderer();
+    if (!renderer)
         return;
 
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
     if (attrName == SVGNames::xAttr
         || attrName == SVGNames::yAttr
         || attrName == SVGNames::widthAttr
         || attrName == SVGNames::heightAttr
         || SVGTests::isKnownAttribute(attrName)
         || SVGLangSpace::isKnownAttribute(attrName)
-        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
-        || SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGForeignObjectElement::synchronizeProperty(const QualifiedName& attrName)
index 0fd329f..6e39bf5 100644 (file)
@@ -55,14 +55,20 @@ void SVGGElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderObject* renderer = this->renderer();
+    if (!renderer)
         return;
 
-    if (SVGTests::isKnownAttribute(attrName) || 
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGGElement::synchronizeProperty(const QualifiedName& attrName)
index 697d90e..82bf3bd 100644 (file)
@@ -91,18 +91,25 @@ void SVGImageElement::svgAttributeChanged(const QualifiedName& attrName)
     if (SVGURIReference::isKnownAttribute(attrName))
         m_imageLoader.updateFromElementIgnoringPreviousError();
 
-    if (!renderer())
+    RenderObject* renderer = this->renderer();
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
-        attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
-        attrName == SVGNames::preserveAspectRatioAttr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName)) {
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
     }
+
+    if (attrName == SVGNames::xAttr
+        || attrName == SVGNames::yAttr
+        || attrName == SVGNames::widthAttr
+        || attrName == SVGNames::heightAttr
+        || attrName == SVGNames::preserveAspectRatioAttr
+        || SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGImageElement::synchronizeProperty(const QualifiedName& attrName)
index 6c8a16b..68b8259 100644 (file)
@@ -72,16 +72,29 @@ void SVGLineElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::x1Attr || attrName == SVGNames::y1Attr ||
-        attrName == SVGNames::x2Attr || attrName == SVGNames::y2Attr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::x1Attr
+        || attrName == SVGNames::y1Attr
+        || attrName == SVGNames::x2Attr
+        || attrName == SVGNames::y2Attr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGLineElement::synchronizeProperty(const QualifiedName& attrName)
index f6e7867..d28f2e5 100644 (file)
@@ -193,15 +193,27 @@ void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::dAttr || attrName == SVGNames::pathLengthAttr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (attrName == SVGNames::dAttr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::pathLengthAttr
+        || SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGPathElement::synchronizeProperty(const QualifiedName& attrName)
index 800bdfa..371c679 100644 (file)
@@ -90,15 +90,26 @@ void SVGPolyElement::svgAttributeChanged(const QualifiedName& attrName)
     if (attrName == SVGNames::pointsAttr)
         setSynchronizedSVGAttributes(false);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::pointsAttr
-        || SVGTests::isKnownAttribute(attrName)
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::pointsAttr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
         || SVGLangSpace::isKnownAttribute(attrName)
-        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
-        || SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGPolyElement::synchronizeProperty(const QualifiedName& attrName)
index 014c42f..30d278a 100644 (file)
@@ -85,17 +85,31 @@ void SVGRectElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGStyledTransformableElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderPath* renderer = static_cast<RenderPath*>(this->renderer());
+    if (!renderer)
         return;
 
-    if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
-        attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
-        attrName == SVGNames::rxAttr || attrName == SVGNames::ryAttr ||
-        SVGTests::isKnownAttribute(attrName) ||
-        SVGLangSpace::isKnownAttribute(attrName) ||
-        SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
-        SVGStyledTransformableElement::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (attrName == SVGNames::xAttr
+        || attrName == SVGNames::yAttr
+        || attrName == SVGNames::widthAttr
+        || attrName == SVGNames::heightAttr
+        || attrName == SVGNames::rxAttr
+        || attrName == SVGNames::ryAttr) {
+        renderer->setNeedsPathUpdate();
+        renderer->setNeedsLayout(true);
+        return;
+    }
+
+    if (SVGTests::isKnownAttribute(attrName)
+        || SVGLangSpace::isKnownAttribute(attrName)
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
+        renderer->setNeedsLayout(true);
 }
 
 void SVGRectElement::synchronizeProperty(const QualifiedName& attrName)
index 8fec67c..25729cf 100644 (file)
@@ -114,11 +114,14 @@ void SVGTextElement::svgAttributeChanged(const QualifiedName& attrName)
 {
     SVGTextPositioningElement::svgAttributeChanged(attrName);
 
-    if (!renderer())
+    RenderObject* renderer = this->renderer();
+    if (!renderer)
         return;
 
-    if (SVGTransformable::isKnownAttribute(attrName))
-        renderer()->setNeedsLayout(true);
+    if (SVGTransformable::isKnownAttribute(attrName)) {
+        renderer->setNeedsTransformUpdate();
+        renderer->setNeedsLayout(true);
+    }
 }
 
 void SVGTextElement::synchronizeProperty(const QualifiedName& attrName)
index 32fc8cb..4510a6c 100644 (file)
@@ -165,12 +165,16 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
         return;
     }
 
+    if (SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        renderer()->setNeedsTransformUpdate();
+        renderer()->setNeedsLayout(true);
+        return;
+    }
+
     if (SVGTests::isKnownAttribute(attrName)
         || SVGLangSpace::isKnownAttribute(attrName)
-        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
-        || SVGStyledTransformableElement::isKnownAttribute(attrName)) {
+        || SVGExternalResourcesRequired::isKnownAttribute(attrName))
         invalidateShadowTree();
-    }
 }
 
 void SVGUseElement::synchronizeProperty(const QualifiedName& attrName)