2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Google, Inc. All rights reserved.
6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
26 #include "core/rendering/svg/SVGRenderSupport.h"
28 #include "core/rendering/PaintInfo.h"
29 #include "core/rendering/RenderGeometryMap.h"
30 #include "core/rendering/RenderLayer.h"
31 #include "core/rendering/SubtreeLayoutScope.h"
32 #include "core/rendering/svg/RenderSVGInlineText.h"
33 #include "core/rendering/svg/RenderSVGResourceClipper.h"
34 #include "core/rendering/svg/RenderSVGResourceFilter.h"
35 #include "core/rendering/svg/RenderSVGResourceMasker.h"
36 #include "core/rendering/svg/RenderSVGRoot.h"
37 #include "core/rendering/svg/RenderSVGText.h"
38 #include "core/rendering/svg/RenderSVGViewportContainer.h"
39 #include "core/rendering/svg/SVGResources.h"
40 #include "core/rendering/svg/SVGResourcesCache.h"
41 #include "core/svg/SVGElement.h"
42 #include "platform/geometry/TransformState.h"
46 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer)
48 // Return early for any cases where we don't actually paint
49 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
52 // Pass our local paint rect to computeRectForRepaint() which will
53 // map to parent coords and recurse up the parent chain.
54 FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates();
55 object->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect);
56 return enclosingLayoutRect(repaintRect);
59 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
61 repaintRect.inflate(object->style()->outlineWidth());
63 // Translate to coords in our parent renderer, and then call computeFloatRectForPaintInvalidation() on our parent.
64 repaintRect = object->localToParentTransform().mapRect(repaintRect);
65 object->parent()->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect, fixed);
68 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
70 transformState.applyTransform(object->localToParentTransform());
72 RenderObject* parent = object->parent();
74 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
75 // to map an element from SVG viewport coordinates to CSS box coordinates.
76 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
77 if (parent->isSVGRoot())
78 transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
80 MapCoordinatesFlags mode = UseTransforms;
81 parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
84 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
86 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
88 RenderObject* parent = object->parent();
90 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
91 // to map an element from SVG viewport coordinates to CSS box coordinates.
92 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
93 if (parent->isSVGRoot()) {
94 TransformationMatrix matrix(object->localToParentTransform());
95 matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
96 geometryMap.push(object, matrix);
98 geometryMap.push(object, object->localToParentTransform());
103 bool SVGRenderSupport::parentTransformDidChange(RenderObject* object)
105 // When a parent container is transformed in SVG, all children will be painted automatically
106 // so we are able to skip redundant repaint checks.
107 RenderObject* parent = object->parent();
108 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
111 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
113 if (!object->checkForPaintInvalidationDuringLayout())
116 return parentTransformDidChange(object);
119 // Update a bounding box taking into account the validity of the other bounding box.
120 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
122 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
126 if (!objectBoundingBoxValid) {
127 objectBoundingBox = otherBoundingBox;
128 objectBoundingBoxValid = true;
132 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
135 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
137 objectBoundingBox = FloatRect();
138 objectBoundingBoxValid = false;
139 strokeBoundingBox = FloatRect();
141 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
142 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
143 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
144 for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) {
145 if (current->isSVGHiddenContainer())
148 const AffineTransform& transform = current->localToParentTransform();
149 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
150 transform.mapRect(current->objectBoundingBox()));
151 strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates()));
154 repaintBoundingBox = strokeBoundingBox;
157 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
159 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
162 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
164 while (start && !start->isSVGRoot())
165 start = start->parent();
168 ASSERT(start->isSVGRoot());
169 return toRenderSVGRoot(start);
172 inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start)
174 ASSERT(!start->needsLayout());
175 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
176 resources->removeClientFromCache(start, false);
178 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling())
179 invalidateResourcesOfChildren(child);
182 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start)
184 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
185 start = start->parent();
188 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
189 if (start->isSVGViewportContainer())
190 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
192 return toRenderSVGRoot(start)->isLayoutSizeChanged();
195 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
197 while (ancestor && !ancestor->isSVGRoot()) {
198 if (ancestor->isSVGTransformableContainer())
199 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
200 if (ancestor->isSVGViewportContainer())
201 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
202 ancestor = ancestor->parent();
208 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
210 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
211 bool transformChanged = transformToRootChanged(start);
212 HashSet<RenderObject*> notlayoutedObjects;
214 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
215 bool needsLayout = selfNeedsLayout;
216 bool childEverHadLayout = child->everHadLayout();
218 if (transformChanged) {
219 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
220 if (child->isSVGText())
221 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
225 if (layoutSizeChanged) {
226 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
227 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
228 if (element->hasRelativeLengths()) {
229 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
230 if (child->isSVGShape()) {
231 toRenderSVGShape(child)->setNeedsShapeUpdate();
232 } else if (child->isSVGText()) {
233 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
234 toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
242 SubtreeLayoutScope layoutScope(*child);
243 // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
244 // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
245 // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
246 // SubtreeLayoutScope (in RenderView::layout()).
247 if (needsLayout && !child->isSVGResourceContainer())
248 layoutScope.setNeedsLayout(child);
250 layoutResourcesIfNeeded(child);
252 if (child->needsLayout()) {
254 // Renderers are responsible for repainting themselves when changing, except
255 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
256 // We could handle this in the individual objects, but for now it's easier to have
257 // parent containers call repaint(). (RenderBlock::layout* has similar logic.)
258 if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
259 child->paintInvalidationForWholeRenderer();
260 } else if (layoutSizeChanged) {
261 notlayoutedObjects.add(child);
265 if (!layoutSizeChanged) {
266 ASSERT(notlayoutedObjects.isEmpty());
270 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
271 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
272 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
273 invalidateResourcesOfChildren(*it);
276 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
280 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
282 resources->layoutIfNeeded();
285 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
287 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
288 ASSERT(!object->isDocumentElement());
290 return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL;
293 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect)
297 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
301 if (RenderSVGResourceFilter* filter = resources->filter())
302 repaintRect = filter->resourceBoundingBox(renderer);
304 if (RenderSVGResourceClipper* clipper = resources->clipper())
305 repaintRect.intersect(clipper->resourceBoundingBox(renderer));
307 if (RenderSVGResourceMasker* masker = resources->masker())
308 repaintRect.intersect(masker->resourceBoundingBox(renderer));
311 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
313 // If any of this container's children need to be laid out, and a filter is applied
314 // to the container, we need to repaint the entire container.
315 if (!object->normalChildNeedsLayout())
318 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
319 if (!resources || !resources->filter())
325 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
329 // We just take clippers into account to determine if a point is on the node. The Specification may
330 // change later and we also need to check maskers.
331 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
335 if (RenderSVGResourceClipper* clipper = resources->clipper())
336 return clipper->hitTestClipContent(object->objectBoundingBox(), point);
341 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
346 ASSERT(object->node());
347 ASSERT(object->node()->isSVGElement());
349 const SVGRenderStyle* svgStyle = style->svgStyle();
352 SVGLengthContext lengthContext(toSVGElement(object->node()));
353 context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext));
354 context->setLineCap(svgStyle->capStyle());
355 context->setLineJoin(svgStyle->joinStyle());
356 context->setMiterLimit(svgStyle->strokeMiterLimit());
358 RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
359 if (dashes->isEmpty())
363 SVGLengthList::ConstIterator it = dashes->begin();
364 SVGLengthList::ConstIterator itEnd = dashes->end();
365 for (; it != itEnd; ++it)
366 dashArray.append(it->value(lengthContext));
368 context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
371 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
376 ASSERT(object->node());
377 ASSERT(object->node()->isSVGElement());
379 const SVGRenderStyle* svgStyle = style->svgStyle();
382 SVGLengthContext lengthContext(toSVGElement(object->node()));
383 strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext));
384 strokeData->setLineCap(svgStyle->capStyle());
385 strokeData->setLineJoin(svgStyle->joinStyle());
386 strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
388 RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
389 if (dashes->isEmpty())
393 size_t length = dashes->length();
394 for (size_t i = 0; i < length; ++i)
395 dashArray.append(dashes->at(i)->value(lengthContext));
397 strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
400 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
402 ASSERT(object->isText());
403 // <br> is marked as text, but is not handled by the SVG rendering code-path.
404 return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();