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