f94ee8404ebd8f9c8ede0b40e9ca5547afb52610
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGRenderSupport.cpp
1 /*
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.
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "SVGRenderSupport.h"
29
30 #include "NodeRenderStyle.h"
31 #include "RenderLayer.h"
32 #include "RenderSVGResource.h"
33 #include "RenderSVGResourceClipper.h"
34 #include "RenderSVGResourceFilter.h"
35 #include "RenderSVGResourceMarker.h"
36 #include "RenderSVGResourceMasker.h"
37 #include "RenderSVGRoot.h"
38 #include "RenderSVGText.h"
39 #include "RenderSVGViewportContainer.h"
40 #include "SVGResources.h"
41 #include "SVGResourcesCache.h"
42 #include "SVGStyledElement.h"
43 #include "TransformState.h"
44 #include <wtf/UnusedParam.h>
45
46 namespace WebCore {
47
48 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer)
49 {
50     // Return early for any cases where we don't actually paint
51     if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
52         return LayoutRect();
53
54     // Pass our local paint rect to computeRectForRepaint() which will
55     // map to parent coords and recurse up the parent chain.
56     FloatRect repaintRect = object->repaintRectInLocalCoordinates();
57     object->computeFloatRectForRepaint(repaintContainer, repaintRect);
58     return enclosingLayoutRect(repaintRect);
59 }
60
61 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, RenderBoxModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
62 {
63     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
64     if (const ShadowData* shadow = svgStyle->shadow())
65         shadow->adjustRectForShadow(repaintRect);
66     repaintRect.inflate(object->style()->outlineWidth());
67
68     // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
69     repaintRect = object->localToParentTransform().mapRect(repaintRect);
70     object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
71 }
72
73 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
74 {
75     transformState.applyTransform(object->localToParentTransform());
76
77     RenderObject* parent = object->parent();
78     
79     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 
80     // to map an element from SVG viewport coordinates to CSS box coordinates.
81     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
82     if (parent->isSVGRoot())
83         transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
84     
85     parent->mapLocalToContainer(repaintContainer, false, true, transformState, wasFixed);
86 }
87
88 // Update a bounding box taking into account the validity of the other bounding box.
89 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
90 {
91     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
92     if (!otherValid)
93         return;
94
95     if (!objectBoundingBoxValid) {
96         objectBoundingBox = otherBoundingBox;
97         objectBoundingBoxValid = true;
98         return;
99     }
100
101     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
102 }
103
104 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
105 {
106     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
107         if (current->isSVGHiddenContainer())
108             continue;
109
110         const AffineTransform& transform = current->localToParentTransform();
111         if (transform.isIdentity()) {
112             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
113             strokeBoundingBox.unite(current->strokeBoundingBox());
114             repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
115         } else {
116             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
117             strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
118             repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
119         }
120     }
121 }
122
123 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
124 {
125     if (localTransform.isIdentity())
126         return localRepaintRect.intersects(paintInfo.rect);
127
128     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
129 }
130
131 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
132 {
133     while (start && !start->isSVGRoot())
134         start = start->parent();
135
136     ASSERT(start);
137     ASSERT(start->isSVGRoot());
138     return toRenderSVGRoot(start);
139 }
140
141 static inline void invalidateResourcesOfChildren(RenderObject* start)
142 {
143     ASSERT(!start->needsLayout());
144     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
145         resources->removeClientFromCache(start, false);
146
147     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
148         invalidateResourcesOfChildren(child);
149 }
150
151 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
152 {
153     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
154         start = start->parent();
155
156     ASSERT(start);
157     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
158     if (start->isSVGViewportContainer())
159         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
160
161     return toRenderSVGRoot(start)->isLayoutSizeChanged();
162 }
163
164 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
165 {
166     while (ancestor && !ancestor->isSVGRoot()) {
167         if (ancestor->isSVGTransformableContainer())
168             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
169         if (ancestor->isSVGViewportContainer())
170             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
171         ancestor = ancestor->parent();
172     }
173
174     return false;
175 }
176
177 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
178 {
179     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
180     bool transformChanged = transformToRootChanged(start);
181     HashSet<RenderObject*> notlayoutedObjects;
182
183     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
184         bool needsLayout = selfNeedsLayout;
185
186         if (transformChanged) {
187             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
188             if (child->isSVGText())
189                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
190             needsLayout = true;
191         }
192
193         if (layoutSizeChanged) {
194             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
195             if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
196                 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
197                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
198                     if (child->isSVGShape())
199                         toRenderSVGShape(child)->setNeedsShapeUpdate();
200                     else if (child->isSVGText())
201                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
202
203                     needsLayout = true;
204                 }
205             }
206         }
207
208         if (needsLayout) {
209             child->setNeedsLayout(true, MarkOnlyThis);
210             child->layout();
211         } else {
212             if (child->needsLayout())
213                 child->layout();
214             else if (layoutSizeChanged)
215                 notlayoutedObjects.add(child);
216         }
217
218         ASSERT(!child->needsLayout());
219     }
220
221     if (!layoutSizeChanged) {
222         ASSERT(notlayoutedObjects.isEmpty());
223         return;
224     }
225
226     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
227     HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
228     for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
229         invalidateResourcesOfChildren(*it);
230 }
231
232 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
233 {
234     // SVG doesn't support independent x/y overflow
235     ASSERT(object->style()->overflowX() == object->style()->overflowY());
236
237     // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
238     ASSERT(object->style()->overflowX() != OSCROLL);
239
240     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
241     ASSERT(!object->isRoot());
242
243     return object->style()->overflowX() == OHIDDEN;
244 }
245
246 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
247 {
248     ASSERT(object);
249
250     RenderStyle* style = object->style();
251     ASSERT(style);
252
253     const SVGRenderStyle* svgStyle = style->svgStyle();
254     ASSERT(svgStyle);
255
256     RenderObject* renderer = const_cast<RenderObject*>(object);
257     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
258     if (!resources) {
259         if (const ShadowData* shadow = svgStyle->shadow())
260             shadow->adjustRectForShadow(repaintRect);
261         return;
262     }
263
264 #if ENABLE(FILTERS)
265     if (RenderSVGResourceFilter* filter = resources->filter())
266         repaintRect = filter->resourceBoundingBox(renderer);
267 #endif
268
269     if (RenderSVGResourceClipper* clipper = resources->clipper())
270         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
271
272     if (RenderSVGResourceMasker* masker = resources->masker())
273         repaintRect.intersect(masker->resourceBoundingBox(renderer));
274
275     if (const ShadowData* shadow = svgStyle->shadow())
276         shadow->adjustRectForShadow(repaintRect);
277 }
278
279 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
280 {
281     // If any of this container's children need to be laid out, and a filter is applied
282     // to the container, we need to repaint the entire container.
283     if (!object->normalChildNeedsLayout())
284         return false;
285
286     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
287     if (!resources || !resources->filter())
288         return false;
289
290     return true;
291 }
292
293 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
294 {
295     ASSERT(object);
296
297     // We just take clippers into account to determine if a point is on the node. The Specification may
298     // change later and we also need to check maskers.
299     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
300     if (!resources)
301         return true;
302
303     if (RenderSVGResourceClipper* clipper = resources->clipper())
304         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
305
306     return true;
307 }
308
309 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
310 {
311     ASSERT(context);
312     ASSERT(style);
313     ASSERT(object);
314     ASSERT(object->node());
315     ASSERT(object->node()->isSVGElement());
316
317     const SVGRenderStyle* svgStyle = style->svgStyle();
318     ASSERT(svgStyle);
319
320     SVGLengthContext lengthContext(static_cast<SVGElement*>(object->node()));
321     context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
322     context->setLineCap(svgStyle->capStyle());
323     context->setLineJoin(svgStyle->joinStyle());
324     if (svgStyle->joinStyle() == MiterJoin)
325         context->setMiterLimit(svgStyle->strokeMiterLimit());
326
327     const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
328     if (dashes.isEmpty())
329         context->setStrokeStyle(SolidStroke);
330     else {
331         DashArray dashArray;
332         const Vector<SVGLength>::const_iterator end = dashes.end();
333         for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
334             dashArray.append((*it).value(lengthContext));
335
336         context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
337     }
338 }
339
340 }
341
342 #endif