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