Index: Source/core/rendering/svg/SVGRenderSupport.h |
diff --git a/Source/core/rendering/svg/SVGRenderSupport.h b/Source/core/rendering/svg/SVGRenderSupport.h |
index fc80a7ca6813593ecf026f337d92796aee22b424..641e08cb675fe573c9207ce9cc48c824a2f54c4b 100644 |
--- a/Source/core/rendering/svg/SVGRenderSupport.h |
+++ b/Source/core/rendering/svg/SVGRenderSupport.h |
@@ -25,6 +25,12 @@ |
#define SVGRenderSupport_h |
#include "core/rendering/PaintInfo.h" |
+#include "core/rendering/svg/RenderSVGRoot.h" |
+#include "core/rendering/svg/RenderSVGShape.h" |
+#include "core/rendering/svg/RenderSVGText.h" |
+#include "core/rendering/svg/RenderSVGViewportContainer.h" |
+#include "core/rendering/svg/SVGResources.h" |
+#include "core/rendering/svg/SVGResourcesCache.h" |
namespace WebCore { |
@@ -40,50 +46,189 @@ class RenderStyle; |
class RenderSVGRoot; |
class TransformState; |
-namespace SVGRenderSupport { |
-// Shares child layouting code between RenderSVGRoot/RenderSVG(Hidden)Container |
-void layoutChildren(RenderObject*, bool selfNeedsLayout); |
- |
-// Layout resources used by this node. |
-void layoutResourcesIfNeeded(const RenderObject*); |
- |
-// Helper function determining wheter overflow is hidden |
-bool isOverflowHidden(const RenderObject*); |
- |
-// Calculates the repaintRect in combination with filter, clipper and masker in local coordinates. |
-void intersectRepaintRectWithResources(const RenderObject*, FloatRect&); |
- |
-// Determines whether a container needs to be laid out because it's filtered and a child is being laid out. |
-bool filtersForceContainerLayout(RenderObject*); |
- |
-// Determines whether the passed point lies in a clipping area |
-bool pointInClippingArea(RenderObject*, const FloatPoint&); |
- |
-void computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox); |
-bool paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo&); |
- |
-// Important functions used by nearly all SVG renderers centralizing coordinate transformations / repaint rect calculations |
-LayoutRect clippedOverflowRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer); |
-void computeFloatRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer, FloatRect&, bool fixed); |
-void mapLocalToContainer(const RenderObject*, const RenderLayerModelObject* repaintContainer, TransformState&, bool* wasFixed = 0); |
-const RenderObject* pushMappingToContainer(const RenderObject*, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&); |
-bool checkForSVGRepaintDuringLayout(RenderObject*); |
- |
-// Shared between SVG renderers and resources. |
-void applyStrokeStyleToContext(GraphicsContext*, const RenderStyle*, const RenderObject*); |
-void applyStrokeStyleToStrokeData(StrokeData*, const RenderStyle*, const RenderObject*); |
- |
-// Determines if any ancestor's transform has changed. |
-bool transformToRootChanged(RenderObject*); |
- |
-// FIXME: These methods do not belong here. |
-const RenderSVGRoot* findTreeRootObject(const RenderObject*); |
- |
-// Helper method for determining if a RenderObject marked as text (isText()== true) |
-// can/will be rendered as part of a <text>. |
-bool isRenderableTextNode(const RenderObject*); |
- |
-} // namespace SVGRenderSupport |
+class SVGRenderSupport { |
+public: |
+ // Shares child layouting code between RenderSVGRoot/RenderSVG(Hidden)Container |
+ template <typename RenderObjectType> |
+ static void layoutChildren(RenderObjectType*, bool selfNeedsLayout); |
+ |
+ // Layout resources used by this node. |
+ static void layoutResourcesIfNeeded(const RenderObject*); |
+ |
+ // Helper function determining wheter overflow is hidden |
+ static bool isOverflowHidden(const RenderObject*); |
+ |
+ // Calculates the repaintRect in combination with filter, clipper and masker in local coordinates. |
+ static void intersectRepaintRectWithResources(const RenderObject*, FloatRect&); |
+ |
+ // Determines whether a container needs to be laid out because it's filtered and a child is being laid out. |
+ static bool filtersForceContainerLayout(RenderObject*); |
+ |
+ // Determines whether the passed point lies in a clipping area |
+ static bool pointInClippingArea(RenderObject*, const FloatPoint&); |
+ |
+ template <typename RenderObjectType> |
+ static void computeContainerBoundingBoxes(const RenderObjectType* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox); |
+ |
+ static bool paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo&); |
+ |
+ // Important functions used by nearly all SVG renderers centralizing coordinate transformations / repaint rect calculations |
+ static LayoutRect clippedOverflowRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer); |
+ static void computeFloatRectForRepaint(const RenderObject*, const RenderLayerModelObject* repaintContainer, FloatRect&, bool fixed); |
+ static void mapLocalToContainer(const RenderObject*, const RenderLayerModelObject* repaintContainer, TransformState&, bool* wasFixed = 0); |
+ static const RenderObject* pushMappingToContainer(const RenderObject*, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap&); |
+ static bool checkForSVGRepaintDuringLayout(RenderObject*); |
+ |
+ // Shared between SVG renderers and resources. |
+ static void applyStrokeStyleToContext(GraphicsContext*, const RenderStyle*, const RenderObject*); |
+ static void applyStrokeStyleToStrokeData(StrokeData*, const RenderStyle*, const RenderObject*); |
+ |
+ // Determines if any ancestor's transform has changed. |
+ static bool transformToRootChanged(RenderObject*); |
+ |
+ // FIXME: These methods do not belong here. |
+ static const RenderSVGRoot* findTreeRootObject(const RenderObject*); |
+ |
+ // Helper method for determining if a RenderObject marked as text (isText()== true) |
+ // can/will be rendered as part of a <text>. |
+ static bool isRenderableTextNode(const RenderObject*); |
+ |
+private: |
+ static void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox); |
+ static void invalidateResourcesOfChildren(RenderObject* start); |
+ static bool layoutSizeOfNearestViewportChanged(const RenderObject* start); |
+}; |
+ |
+template <typename RenderObjectType> |
+void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObjectType* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) |
+{ |
+ objectBoundingBox = FloatRect(); |
+ objectBoundingBoxValid = false; |
+ strokeBoundingBox = FloatRect(); |
+ |
+ // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes |
+ // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound |
+ // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. |
+ for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) { |
+ if (current->isSVGHiddenContainer()) |
+ continue; |
+ |
+ const AffineTransform& transform = current->localToParentTransform(); |
+ updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, |
+ transform.mapRect(current->objectBoundingBox())); |
+ strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); |
+ } |
+ |
+ repaintBoundingBox = strokeBoundingBox; |
+} |
+ |
+template <typename RenderObjectType> |
+void SVGRenderSupport::layoutChildren(RenderObjectType* start, bool selfNeedsLayout) |
+{ |
+ bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); |
+ bool transformChanged = transformToRootChanged(start); |
+ HashSet<RenderObject*> notlayoutedObjects; |
+ |
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { |
+ bool needsLayout = selfNeedsLayout; |
+ bool childEverHadLayout = child->everHadLayout(); |
+ |
+ if (transformChanged) { |
+ // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). |
+ if (child->isSVGText()) |
+ toRenderSVGText(child)->setNeedsTextMetricsUpdate(); |
+ needsLayout = true; |
+ } |
+ |
+ if (layoutSizeChanged) { |
+ // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths |
+ if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { |
+ if (element->hasRelativeLengths()) { |
+ // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object |
+ if (child->isSVGShape()) { |
+ toRenderSVGShape(child)->setNeedsShapeUpdate(); |
+ } else if (child->isSVGText()) { |
+ toRenderSVGText(child)->setNeedsTextMetricsUpdate(); |
+ toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); |
+ } |
+ |
+ needsLayout = true; |
+ } |
+ } |
+ } |
+ |
+ SubtreeLayoutScope layoutScope(*child); |
+ // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope. |
+ // Since they only care about viewport size changes (to resolve their relative lengths), we trigger |
+ // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher |
+ // SubtreeLayoutScope (in RenderView::layout()). |
+ if (needsLayout && !child->isSVGResourceContainer()) |
+ layoutScope.setNeedsLayout(child); |
+ |
+ layoutResourcesIfNeeded(child); |
+ |
+ if (child->needsLayout()) { |
+ child->layout(); |
+ // Renderers are responsible for repainting themselves when changing, except |
+ // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. |
+ // We could handle this in the individual objects, but for now it's easier to have |
+ // parent containers call repaint(). (RenderBlock::layout* has similar logic.) |
+ if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) |
+ child->repaint(); |
+ } else if (layoutSizeChanged) { |
+ notlayoutedObjects.add(child); |
+ } |
+ } |
+ |
+ if (!layoutSizeChanged) { |
+ ASSERT(notlayoutedObjects.isEmpty()); |
+ return; |
+ } |
+ |
+ // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. |
+ HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); |
+ for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) |
+ invalidateResourcesOfChildren(*it); |
+} |
+ |
+// Update a bounding box taking into account the validity of the other bounding box. |
+inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) |
+{ |
+ bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; |
+ if (!otherValid) |
+ return; |
+ |
+ if (!objectBoundingBoxValid) { |
+ objectBoundingBox = otherBoundingBox; |
+ objectBoundingBoxValid = true; |
+ return; |
+ } |
+ |
+ objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); |
+} |
+ |
+inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start) |
+{ |
+ ASSERT(!start->needsLayout()); |
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) |
+ resources->removeClientFromCache(start, false); |
+ |
+ for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) |
+ invalidateResourcesOfChildren(child); |
+} |
+ |
+inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start) |
+{ |
+ while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) |
+ start = start->parent(); |
+ |
+ ASSERT(start); |
+ ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); |
+ if (start->isSVGViewportContainer()) |
+ return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); |
+ |
+ return toRenderSVGRoot(start)->isLayoutSizeChanged(); |
+} |
} // namespace WebCore |