2011-01-09 Dirk Schulze <krit@webkit.org>
[WebKit.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 "FrameView.h"
31 #include "ImageBuffer.h"
32 #include "NodeRenderStyle.h"
33 #include "RenderLayer.h"
34 #include "RenderSVGPath.h"
35 #include "RenderSVGResource.h"
36 #include "RenderSVGResourceClipper.h"
37 #include "RenderSVGResourceFilter.h"
38 #include "RenderSVGResourceMarker.h"
39 #include "RenderSVGResourceMasker.h"
40 #include "RenderSVGRoot.h"
41 #include "SVGResources.h"
42 #include "SVGStyledElement.h"
43 #include "TransformState.h"
44 #include <wtf/UnusedParam.h>
45
46 namespace WebCore {
47
48 IntRect SVGRenderSupport::clippedOverflowRectForRepaint(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 IntRect();
53
54     // Pass our local paint rect to computeRectForRepaint() which will
55     // map to parent coords and recurse up the parent chain.
56     IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates());
57     object->computeRectForRepaint(repaintContainer, repaintRect);
58     return repaintRect;
59 }
60
61 void SVGRenderSupport::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
62 {
63     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
64     if (const ShadowData* shadow = svgStyle->shadow())
65         shadow->adjustRectForShadow(repaintRect);
66
67     // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent
68     repaintRect = object->localToParentTransform().mapRect(repaintRect);
69     object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed);
70 }
71
72 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState)
73 {
74     ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
75     ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless.
76     transformState.applyTransform(object->localToParentTransform());
77     object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
78 }
79
80 bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
81 {
82     ASSERT(object);
83
84     RenderStyle* style = object->style();
85     ASSERT(style);
86
87     const SVGRenderStyle* svgStyle = style->svgStyle();
88     ASSERT(svgStyle);
89
90     // Setup transparency layers before setting up SVG resources!
91     float opacity = style->opacity();
92     const ShadowData* shadow = svgStyle->shadow();
93     if (opacity < 1 || shadow) {
94         FloatRect repaintRect = object->repaintRectInLocalCoordinates();
95
96         if (opacity < 1) {
97             paintInfo.context->clip(repaintRect);
98             paintInfo.context->beginTransparencyLayer(opacity);
99         }
100
101         if (shadow) {
102             paintInfo.context->clip(repaintRect);
103             paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
104             paintInfo.context->beginTransparencyLayer(1);
105         }
106     }
107
108     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
109     if (!resources)
110         return true;
111
112     if (RenderSVGResourceMasker* masker = resources->masker()) {
113         if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
114             return false;
115     }
116
117     if (RenderSVGResourceClipper* clipper = resources->clipper()) {
118         if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
119             return false;
120     }
121
122 #if ENABLE(FILTERS)
123     if (RenderSVGResourceFilter* filter = resources->filter()) {
124         if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
125             return false;
126     }
127 #endif
128
129     return true;
130 }
131
132 void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
133 {
134 #if !ENABLE(FILTERS)
135     UNUSED_PARAM(savedContext);
136 #endif
137
138     ASSERT(object);
139
140     const RenderStyle* style = object->style();
141     ASSERT(style);
142
143     const SVGRenderStyle* svgStyle = style->svgStyle();
144     ASSERT(svgStyle);
145
146 #if ENABLE(FILTERS)
147     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
148     if (resources) {
149         if (RenderSVGResourceFilter* filter = resources->filter()) {
150             filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode, /* path */0);
151             paintInfo.context = savedContext;
152         }
153     }
154 #endif
155
156     if (style->opacity() < 1)
157         paintInfo.context->endTransparencyLayer();
158
159     if (svgStyle->shadow())
160         paintInfo.context->endTransparencyLayer();
161 }
162
163 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
164 {
165     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
166         if (current->isSVGHiddenContainer())
167             continue;
168
169         const AffineTransform& transform = current->localToParentTransform();
170         if (transform.isIdentity()) {
171             objectBoundingBox.unite(current->objectBoundingBox());
172             strokeBoundingBox.unite(current->strokeBoundingBox());
173             repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
174         } else {
175             objectBoundingBox.unite(transform.mapRect(current->objectBoundingBox()));
176             strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
177             repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
178         }
179     }
180 }
181
182 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
183 {
184     if (localTransform.isIdentity())
185         return localRepaintRect.intersects(paintInfo.rect);
186
187     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
188 }
189
190 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
191 {
192     while (start && !start->isSVGRoot())
193         start = start->parent();
194
195     ASSERT(start);
196     ASSERT(start->isSVGRoot());
197     return toRenderSVGRoot(start);
198 }
199
200 static inline void invalidateResourcesOfChildren(RenderObject* start)
201 {
202     ASSERT(!start->needsLayout());
203     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
204         resources->removeClientFromCache(start, false);
205
206     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
207         invalidateResourcesOfChildren(child);
208 }
209
210 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
211 {
212     bool layoutSizeChanged = findTreeRootObject(start)->isLayoutSizeChanged();
213     HashSet<RenderObject*> notlayoutedObjects;
214
215     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
216         bool needsLayout = selfNeedsLayout;
217
218         if (layoutSizeChanged) {
219             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
220             if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
221                 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
222                     // When the layout size changed and when using relative values tell the RenderSVGPath to update its Path object
223                     if (child->isSVGPath())
224                         toRenderSVGPath(child)->setNeedsPathUpdate();
225
226                     needsLayout = true;
227                 }
228             }
229         }
230
231         if (needsLayout) {
232             child->setNeedsLayout(true, false);
233             child->layout();
234         } else {
235             if (child->needsLayout())
236                 child->layout();
237             else if (layoutSizeChanged)
238                 notlayoutedObjects.add(child);
239         }
240
241         ASSERT(!child->needsLayout());
242     }
243
244     if (!layoutSizeChanged) {
245         ASSERT(notlayoutedObjects.isEmpty());
246         return;
247     }
248
249     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
250     HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
251     for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
252         invalidateResourcesOfChildren(*it);
253 }
254
255 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
256 {
257     // SVG doesn't support independent x/y overflow
258     ASSERT(object->style()->overflowX() == object->style()->overflowY());
259
260     // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
261     ASSERT(object->style()->overflowX() != OSCROLL);
262
263     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
264     ASSERT(!object->isRoot());
265
266     return object->style()->overflowX() == OHIDDEN;
267 }
268
269 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
270 {
271     ASSERT(object);
272
273     RenderStyle* style = object->style();
274     ASSERT(style);
275
276     const SVGRenderStyle* svgStyle = style->svgStyle();
277     ASSERT(svgStyle);
278
279     RenderObject* renderer = const_cast<RenderObject*>(object);
280     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
281     if (!resources) {
282         if (const ShadowData* shadow = svgStyle->shadow())
283             shadow->adjustRectForShadow(repaintRect);
284         return;
285     }
286
287 #if ENABLE(FILTERS)
288     if (RenderSVGResourceFilter* filter = resources->filter())
289         repaintRect = filter->resourceBoundingBox(renderer);
290 #endif
291
292     if (RenderSVGResourceClipper* clipper = resources->clipper())
293         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
294
295     if (RenderSVGResourceMasker* masker = resources->masker())
296         repaintRect.intersect(masker->resourceBoundingBox(renderer));
297
298     if (const ShadowData* shadow = svgStyle->shadow())
299         shadow->adjustRectForShadow(repaintRect);
300 }
301
302 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
303 {
304     ASSERT(object);
305
306     // We just take clippers into account to determine if a point is on the node. The Specification may
307     // change later and we also need to check maskers.
308     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
309     if (!resources)
310         return true;
311
312     if (RenderSVGResourceClipper* clipper = resources->clipper())
313         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
314
315     return true;
316 }
317
318 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
319 {
320     ASSERT(context);
321     ASSERT(style);
322     ASSERT(object);
323     ASSERT(object->node());
324     ASSERT(object->node()->isSVGElement());
325
326     const SVGRenderStyle* svgStyle = style->svgStyle();
327     ASSERT(svgStyle);
328
329     SVGElement* lengthContext = static_cast<SVGElement*>(object->node());
330     context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
331     context->setLineCap(svgStyle->capStyle());
332     context->setLineJoin(svgStyle->joinStyle());
333     if (svgStyle->joinStyle() == MiterJoin)
334         context->setMiterLimit(svgStyle->strokeMiterLimit());
335
336     const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
337     if (dashes.isEmpty())
338         context->setStrokeStyle(SolidStroke);
339     else {
340         DashArray dashArray;
341         const Vector<SVGLength>::const_iterator end = dashes.end();
342         for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
343             dashArray.append((*it).value(lengthContext));
344
345         context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
346     }
347 }
348
349 }
350
351 #endif