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/frame/FrameView.h"
29 #include "core/frame/LocalFrame.h"
30 #include "core/rendering/PaintInfo.h"
31 #include "core/rendering/RenderGeometryMap.h"
32 #include "core/rendering/RenderLayer.h"
33 #include "core/rendering/SubtreeLayoutScope.h"
34 #include "core/rendering/svg/RenderSVGInlineText.h"
35 #include "core/rendering/svg/RenderSVGResourceClipper.h"
36 #include "core/rendering/svg/RenderSVGResourceFilter.h"
37 #include "core/rendering/svg/RenderSVGResourceMasker.h"
38 #include "core/rendering/svg/RenderSVGRoot.h"
39 #include "core/rendering/svg/RenderSVGShape.h"
40 #include "core/rendering/svg/RenderSVGText.h"
41 #include "core/rendering/svg/RenderSVGViewportContainer.h"
42 #include "core/rendering/svg/SVGResources.h"
43 #include "core/rendering/svg/SVGResourcesCache.h"
44 #include "core/svg/SVGElement.h"
45 #include "platform/geometry/TransformState.h"
49 LayoutRect SVGRenderSupport::clippedOverflowRectForPaintInvalidation(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState)
51 // Return early for any cases where we don't actually paint
52 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
55 // Pass our local paint rect to computeRectForPaintInvalidation() which will
56 // map to parent coords and recurse up the parent chain.
57 FloatRect paintInvalidationRect = object->paintInvalidationRectInLocalCoordinates();
58 paintInvalidationRect.inflate(object->style()->outlineWidth());
60 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) {
61 // Compute accumulated SVG transform and apply to local paint rect.
62 AffineTransform transform = paintInvalidationState->svgTransform() * object->localToParentTransform();
63 paintInvalidationRect = transform.mapRect(paintInvalidationRect);
64 // FIXME: These are quirks carried forward from RenderSVGRoot::computeFloatRectForPaintInvalidation.
66 if (!paintInvalidationRect.isEmpty())
67 rect = enclosingIntRect(paintInvalidationRect);
68 // Offset by SVG root paint offset and apply clipping as needed.
69 rect.move(paintInvalidationState->paintOffset());
70 if (paintInvalidationState->isClipped())
71 rect.intersect(paintInvalidationState->clipRect());
75 object->computeFloatRectForPaintInvalidation(paintInvalidationContainer, paintInvalidationRect, paintInvalidationState);
76 return enclosingLayoutRect(paintInvalidationRect);
79 void SVGRenderSupport::computeFloatRectForPaintInvalidation(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, FloatRect& paintInvalidationRect, const PaintInvalidationState* paintInvalidationState)
81 // Translate to coords in our parent renderer, and then call computeFloatRectForPaintInvalidation() on our parent.
82 paintInvalidationRect = object->localToParentTransform().mapRect(paintInvalidationRect);
83 object->parent()->computeFloatRectForPaintInvalidation(paintInvalidationContainer, paintInvalidationRect, paintInvalidationState);
86 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, TransformState& transformState, bool* wasFixed, const PaintInvalidationState* paintInvalidationState)
88 transformState.applyTransform(object->localToParentTransform());
90 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) {
91 // |svgTransform| contains localToBorderBoxTransform mentioned below.
92 transformState.applyTransform(paintInvalidationState->svgTransform());
93 transformState.move(paintInvalidationState->paintOffset());
97 RenderObject* parent = object->parent();
99 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
100 // to map an element from SVG viewport coordinates to CSS box coordinates.
101 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
102 if (parent->isSVGRoot())
103 transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
105 MapCoordinatesFlags mode = UseTransforms;
106 parent->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed, paintInvalidationState);
109 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
111 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
113 RenderObject* parent = object->parent();
115 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
116 // to map an element from SVG viewport coordinates to CSS box coordinates.
117 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
118 if (parent->isSVGRoot()) {
119 TransformationMatrix matrix(object->localToParentTransform());
120 matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
121 geometryMap.push(object, matrix);
123 geometryMap.push(object, object->localToParentTransform());
128 // Update a bounding box taking into account the validity of the other bounding box.
129 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
131 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
135 if (!objectBoundingBoxValid) {
136 objectBoundingBox = otherBoundingBox;
137 objectBoundingBoxValid = true;
141 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
144 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& paintInvalidationBoundingBox)
146 objectBoundingBox = FloatRect();
147 objectBoundingBoxValid = false;
148 strokeBoundingBox = FloatRect();
150 // When computing the strokeBoundingBox, we use the paintInvalidationRects of the container's children so that the container's stroke includes
151 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
152 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
153 for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) {
154 if (current->isSVGHiddenContainer())
157 // Don't include elements in the union that do not render.
158 if (current->isSVGShape() && toRenderSVGShape(current)->isShapeEmpty())
161 const AffineTransform& transform = current->localToParentTransform();
162 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
163 transform.mapRect(current->objectBoundingBox()));
164 strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates()));
167 paintInvalidationBoundingBox = strokeBoundingBox;
170 bool SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(const FloatRect& localPaintInvalidationRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
172 return localTransform.mapRect(localPaintInvalidationRect).intersects(paintInfo.rect);
175 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
177 while (start && !start->isSVGRoot())
178 start = start->parent();
181 ASSERT(start->isSVGRoot());
182 return toRenderSVGRoot(start);
185 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start)
187 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
188 start = start->parent();
191 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
192 if (start->isSVGViewportContainer())
193 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
195 return toRenderSVGRoot(start)->isLayoutSizeChanged();
198 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
200 while (ancestor && !ancestor->isSVGRoot()) {
201 if (ancestor->isSVGTransformableContainer())
202 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
203 if (ancestor->isSVGViewportContainer())
204 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
205 ancestor = ancestor->parent();
211 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
213 // When hasRelativeLengths() is false, no descendants have relative lengths
214 // (hence no one is interested in viewport size changes).
215 bool layoutSizeChanged = toSVGElement(start->node())->hasRelativeLengths()
216 && layoutSizeOfNearestViewportChanged(start);
217 bool transformChanged = transformToRootChanged(start);
219 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
220 bool forceLayout = selfNeedsLayout;
222 if (transformChanged) {
223 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
224 if (child->isSVGText())
225 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
229 if (layoutSizeChanged) {
230 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
231 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
232 if (element->hasRelativeLengths()) {
233 // FIXME: this should be done on invalidation, not during layout.
234 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
235 if (child->isSVGShape()) {
236 toRenderSVGShape(child)->setNeedsShapeUpdate();
237 } else if (child->isSVGText()) {
238 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
239 toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
247 SubtreeLayoutScope layoutScope(*child);
248 // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
249 // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
250 // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
251 // SubtreeLayoutScope (in RenderView::layout()).
252 if (forceLayout && !child->isSVGResourceContainer())
253 layoutScope.setNeedsLayout(child);
255 // Lay out any referenced resources before the child.
256 layoutResourcesIfNeeded(child);
257 child->layoutIfNeeded();
261 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
265 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
267 resources->layoutIfNeeded();
270 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
272 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
273 ASSERT(!object->isDocumentElement());
275 return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL;
278 bool SVGRenderSupport::isRenderingClipPathAsMaskImage(const RenderObject& object)
280 return object.frame() && object.frame()->view() && object.frame()->view()->paintBehavior() & PaintBehaviorRenderingClipPathAsMask;
283 void SVGRenderSupport::intersectPaintInvalidationRectWithResources(const RenderObject* renderer, FloatRect& paintInvalidationRect)
287 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
291 if (RenderSVGResourceFilter* filter = resources->filter())
292 paintInvalidationRect = filter->resourceBoundingBox(renderer);
294 if (RenderSVGResourceClipper* clipper = resources->clipper())
295 paintInvalidationRect.intersect(clipper->resourceBoundingBox(renderer));
297 if (RenderSVGResourceMasker* masker = resources->masker())
298 paintInvalidationRect.intersect(masker->resourceBoundingBox(renderer));
301 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
303 // If any of this container's children need to be laid out, and a filter is applied
304 // to the container, we need to issue paint invalidations the entire container.
305 if (!object->normalChildNeedsLayout())
308 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
309 if (!resources || !resources->filter())
315 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
319 // We just take clippers into account to determine if a point is on the node. The Specification may
320 // change later and we also need to check maskers.
321 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
325 if (RenderSVGResourceClipper* clipper = resources->clipper())
326 return clipper->hitTestClipContent(object->objectBoundingBox(), point);
331 bool SVGRenderSupport::transformToUserSpaceAndCheckClipping(RenderObject* object, const AffineTransform& localTransform, const FloatPoint& pointInParent, FloatPoint& localPoint)
333 if (!localTransform.isInvertible())
335 localPoint = localTransform.inverse().mapPoint(pointInParent);
336 return pointInClippingArea(object, localPoint);
339 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
344 ASSERT(object->node());
345 ASSERT(object->node()->isSVGElement());
347 const SVGRenderStyle& svgStyle = style->svgStyle();
349 SVGLengthContext lengthContext(toSVGElement(object->node()));
350 context->setStrokeThickness(svgStyle.strokeWidth()->value(lengthContext));
351 context->setLineCap(svgStyle.capStyle());
352 context->setLineJoin(svgStyle.joinStyle());
353 context->setMiterLimit(svgStyle.strokeMiterLimit());
355 RefPtr<SVGLengthList> dashes = svgStyle.strokeDashArray();
356 if (dashes->isEmpty())
360 SVGLengthList::ConstIterator it = dashes->begin();
361 SVGLengthList::ConstIterator itEnd = dashes->end();
362 for (; it != itEnd; ++it)
363 dashArray.append(it->value(lengthContext));
365 context->setLineDash(dashArray, svgStyle.strokeDashOffset()->value(lengthContext));
368 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
373 ASSERT(object->node());
374 ASSERT(object->node()->isSVGElement());
376 const SVGRenderStyle& svgStyle = style->svgStyle();
378 SVGLengthContext lengthContext(toSVGElement(object->node()));
379 strokeData->setThickness(svgStyle.strokeWidth()->value(lengthContext));
380 strokeData->setLineCap(svgStyle.capStyle());
381 strokeData->setLineJoin(svgStyle.joinStyle());
382 strokeData->setMiterLimit(svgStyle.strokeMiterLimit());
384 RefPtr<SVGLengthList> dashes = svgStyle.strokeDashArray();
385 if (dashes->isEmpty())
389 size_t length = dashes->length();
390 for (size_t i = 0; i < length; ++i)
391 dashArray.append(dashes->at(i)->value(lengthContext));
393 strokeData->setLineDash(dashArray, svgStyle.strokeDashOffset()->value(lengthContext));
396 bool SVGRenderSupport::updateGraphicsContext(GraphicsContextStateSaver& stateSaver, RenderStyle* style, RenderObject& renderer, RenderSVGResourceMode resourceMode, const AffineTransform* additionalPaintServerTransform)
400 GraphicsContext* context = stateSaver.context();
401 if (isRenderingClipPathAsMaskImage(renderer)) {
402 if (resourceMode == ApplyToStrokeMode)
404 context->setAlphaAsFloat(1);
405 context->setFillColor(SVGRenderStyle::initialFillPaintColor());
409 SVGPaintServer paintServer = SVGPaintServer::requestForRenderer(renderer, style, resourceMode);
410 if (!paintServer.isValid())
413 if (additionalPaintServerTransform && paintServer.isTransformDependent())
414 paintServer.prependTransform(*additionalPaintServerTransform);
416 paintServer.apply(*context, resourceMode, &stateSaver);
418 const SVGRenderStyle& svgStyle = style->svgStyle();
420 if (resourceMode == ApplyToFillMode) {
421 context->setAlphaAsFloat(svgStyle.fillOpacity());
422 context->setFillRule(svgStyle.fillRule());
424 context->setAlphaAsFloat(svgStyle.strokeOpacity());
425 applyStrokeStyleToContext(context, style, &renderer);
430 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
432 ASSERT(object->isText());
433 // <br> is marked as text, but is not handled by the SVG rendering code-path.
434 return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();