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
clippedOverflowRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer)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
computeRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)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
mapLocalToContainer(const RenderObject * object,RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState)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
prepareToRenderSVGContent(RenderObject * object,PaintInfo & paintInfo)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
finishRenderSVGContent(RenderObject * object,PaintInfo & paintInfo,GraphicsContext * savedContext)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
computeContainerBoundingBoxes(const RenderObject * container,FloatRect & objectBoundingBox,FloatRect & strokeBoundingBox,FloatRect & repaintBoundingBox)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
paintInfoIntersectsRepaintRect(const FloatRect & localRepaintRect,const AffineTransform & localTransform,const PaintInfo & paintInfo)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
findTreeRootObject(const RenderObject * start)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
invalidateResourcesOfChildren(RenderObject * start)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
layoutChildren(RenderObject * start,bool selfNeedsLayout)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
isOverflowHidden(const RenderObject * object)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
intersectRepaintRectWithResources(const RenderObject * object,FloatRect & repaintRect)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
pointInClippingArea(RenderObject * object,const FloatPoint & point)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
applyStrokeStyleToContext(GraphicsContext * context,const RenderStyle * style,const RenderObject * object)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
352