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