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.
28 #include "SVGRenderSupport.h"
31 #include "FrameView.h"
32 #include "ImageBuffer.h"
33 #include "NodeRenderStyle.h"
34 #include "RenderLayer.h"
35 #include "RenderSVGPath.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGResourceClipper.h"
38 #include "RenderSVGResourceFilter.h"
39 #include "RenderSVGResourceMarker.h"
40 #include "RenderSVGResourceMasker.h"
41 #include "RenderSVGRoot.h"
42 #include "RenderSVGViewportContainer.h"
43 #include "SVGResources.h"
44 #include "SVGResourcesCache.h"
45 #include "SVGStyledElement.h"
46 #include "TransformState.h"
47 #include <wtf/UnusedParam.h>
51 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer)
53 // Return early for any cases where we don't actually paint
54 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
57 // Pass our local paint rect to computeRectForRepaint() which will
58 // map to parent coords and recurse up the parent chain.
59 FloatRect repaintRect = object->repaintRectInLocalCoordinates();
60 object->computeFloatRectForRepaint(repaintContainer, repaintRect);
61 return enclosingLayoutRect(repaintRect);
64 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
66 const SVGRenderStyle* svgStyle = object->style()->svgStyle();
67 if (const ShadowData* shadow = svgStyle->shadow())
68 shadow->adjustRectForShadow(repaintRect);
69 repaintRect.inflate(object->style()->outlineWidth());
71 // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
72 repaintRect = object->localToParentTransform().mapRect(repaintRect);
73 object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
76 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
78 transformState.applyTransform(object->localToParentTransform());
79 object->parent()->mapLocalToContainer(repaintContainer, false, true, transformState, wasFixed);
82 bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
86 RenderStyle* style = object->style();
89 const SVGRenderStyle* svgStyle = style->svgStyle();
92 bool isRenderingMask = false;
93 if (object->frame() && object->frame()->view())
94 isRenderingMask = object->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask;
96 // Setup transparency layers before setting up SVG resources!
97 float opacity = isRenderingMask ? 1 : style->opacity();
98 const ShadowData* shadow = svgStyle->shadow();
99 if (opacity < 1 || shadow) {
100 FloatRect repaintRect = object->repaintRectInLocalCoordinates();
103 paintInfo.context->clip(repaintRect);
104 paintInfo.context->beginTransparencyLayer(opacity);
108 paintInfo.context->clip(repaintRect);
109 paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
110 paintInfo.context->beginTransparencyLayer(1);
114 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
117 if (svgStyle->hasFilter())
123 if (!isRenderingMask) {
124 if (RenderSVGResourceMasker* masker = resources->masker()) {
125 if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
130 if (RenderSVGResourceClipper* clipper = resources->clipper()) {
131 if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
136 if (!isRenderingMask) {
137 if (RenderSVGResourceFilter* filter = resources->filter()) {
138 if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
147 void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
150 UNUSED_PARAM(savedContext);
155 const RenderStyle* style = object->style();
158 const SVGRenderStyle* svgStyle = style->svgStyle();
162 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
164 if (RenderSVGResourceFilter* filter = resources->filter()) {
165 filter->postApplyResource(static_cast<RenderSVGShape*>(object), paintInfo.context, ApplyToDefaultMode, 0, 0);
166 paintInfo.context = savedContext;
171 if (style->opacity() < 1)
172 paintInfo.context->endTransparencyLayer();
174 if (svgStyle->shadow())
175 paintInfo.context->endTransparencyLayer();
178 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
180 bool isFirstChild = true;
182 for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
183 if (current->isSVGHiddenContainer())
186 const AffineTransform& transform = current->localToParentTransform();
187 if (transform.isIdentity()) {
189 objectBoundingBox = current->objectBoundingBox();
191 objectBoundingBox.uniteEvenIfEmpty(current->objectBoundingBox());
192 strokeBoundingBox.unite(current->strokeBoundingBox());
193 repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
196 objectBoundingBox = transform.mapRect(current->objectBoundingBox());
198 objectBoundingBox.uniteEvenIfEmpty(transform.mapRect(current->objectBoundingBox()));
199 strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
200 repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
203 isFirstChild = false;
207 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
209 if (localTransform.isIdentity())
210 return localRepaintRect.intersects(paintInfo.rect);
212 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
215 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
217 while (start && !start->isSVGRoot())
218 start = start->parent();
221 ASSERT(start->isSVGRoot());
222 return toRenderSVGRoot(start);
225 static inline void invalidateResourcesOfChildren(RenderObject* start)
227 ASSERT(!start->needsLayout());
228 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
229 resources->removeClientFromCache(start, false);
231 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
232 invalidateResourcesOfChildren(child);
235 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
237 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
238 start = start->parent();
241 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
242 if (start->isSVGViewportContainer())
243 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
245 return toRenderSVGRoot(start)->isLayoutSizeChanged();
248 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
250 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
251 HashSet<RenderObject*> notlayoutedObjects;
253 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
254 bool needsLayout = selfNeedsLayout;
256 if (layoutSizeChanged) {
257 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
258 if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
259 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
260 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
261 if (child->isSVGShape())
262 toRenderSVGShape(child)->setNeedsShapeUpdate();
270 child->setNeedsLayout(true, false);
273 if (child->needsLayout())
275 else if (layoutSizeChanged)
276 notlayoutedObjects.add(child);
279 ASSERT(!child->needsLayout());
282 if (!layoutSizeChanged) {
283 ASSERT(notlayoutedObjects.isEmpty());
287 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
288 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
289 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
290 invalidateResourcesOfChildren(*it);
293 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
295 // SVG doesn't support independent x/y overflow
296 ASSERT(object->style()->overflowX() == object->style()->overflowY());
298 // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
299 ASSERT(object->style()->overflowX() != OSCROLL);
301 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
302 ASSERT(!object->isRoot());
304 return object->style()->overflowX() == OHIDDEN;
307 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
311 RenderStyle* style = object->style();
314 const SVGRenderStyle* svgStyle = style->svgStyle();
317 RenderObject* renderer = const_cast<RenderObject*>(object);
318 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
320 if (const ShadowData* shadow = svgStyle->shadow())
321 shadow->adjustRectForShadow(repaintRect);
326 if (RenderSVGResourceFilter* filter = resources->filter())
327 repaintRect = filter->resourceBoundingBox(renderer);
330 if (RenderSVGResourceClipper* clipper = resources->clipper())
331 repaintRect.intersect(clipper->resourceBoundingBox(renderer));
333 if (RenderSVGResourceMasker* masker = resources->masker())
334 repaintRect.intersect(masker->resourceBoundingBox(renderer));
336 if (const ShadowData* shadow = svgStyle->shadow())
337 shadow->adjustRectForShadow(repaintRect);
340 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
342 // If any of this container's children need to be laid out, and a filter is applied
343 // to the container, we need to repaint the entire container.
344 if (!object->normalChildNeedsLayout())
347 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
348 if (!resources || !resources->filter())
354 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
358 // We just take clippers into account to determine if a point is on the node. The Specification may
359 // change later and we also need to check maskers.
360 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
364 if (RenderSVGResourceClipper* clipper = resources->clipper())
365 return clipper->hitTestClipContent(object->objectBoundingBox(), point);
370 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
375 ASSERT(object->node());
376 ASSERT(object->node()->isSVGElement());
378 const SVGRenderStyle* svgStyle = style->svgStyle();
381 SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node()));
382 context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
383 context->setLineCap(svgStyle->capStyle());
384 context->setLineJoin(svgStyle->joinStyle());
385 if (svgStyle->joinStyle() == MiterJoin)
386 context->setMiterLimit(svgStyle->strokeMiterLimit());
388 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
389 if (dashes.isEmpty())
390 context->setStrokeStyle(SolidStroke);
393 const Vector<SVGLength>::const_iterator end = dashes.end();
394 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
395 dashArray.append((*it).value(lengthContext));
397 context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));