1 /*
2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3 * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4 * (C) 2007 Eric Seidel <eric@webkit.org>
5 * (C) 2009 Google, Inc. All rights reserved.
6 * (C) 2009 Dirk Schulze <krit@webkit.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "SVGRenderSupport.h"
29
30 #include "AffineTransform.h"
31 #include "ImageBuffer.h"
32 #include "RenderObject.h"
33 #include "RenderSVGContainer.h"
34 #include "RenderView.h"
35 #include "SVGResourceClipper.h"
36 #include "SVGResourceFilter.h"
37 #include "SVGResourceMasker.h"
38 #include "SVGStyledElement.h"
39 #include "SVGURIReference.h"
40 #include "TransformState.h"
41 #include <wtf/UnusedParam.h>
42
43 namespace WebCore {
44
~SVGRenderBase()45 SVGRenderBase::~SVGRenderBase()
46 {
47 }
48
clippedOverflowRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer)49 IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer)
50 {
51 // Return early for any cases where we don't actually paint
52 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
53 return IntRect();
54
55 // Pass our local paint rect to computeRectForRepaint() which will
56 // map to parent coords and recurse up the parent chain.
57 IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates());
58 object->computeRectForRepaint(repaintContainer, repaintRect);
59 return repaintRect;
60 }
61
computeRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)62 void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
63 {
64 object->style()->svgStyle()->inflateForShadow(repaintRect);
65
66 // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent
67 repaintRect = object->localToParentTransform().mapRect(repaintRect);
68 object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed);
69 }
70
mapLocalToContainer(const RenderObject * object,RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState)71 void SVGRenderBase::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState)
72 {
73 ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
74 ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless.
75 transformState.applyTransform(object->localToParentTransform());
76 object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
77 }
78
prepareToRenderSVGContent(RenderObject * object,RenderObject::PaintInfo & paintInfo,const FloatRect & repaintRect,SVGResourceFilter * & filter,SVGResourceFilter * rootFilter)79 bool SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& repaintRect, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter)
80 {
81 #if !ENABLE(FILTERS)
82 UNUSED_PARAM(filter);
83 UNUSED_PARAM(rootFilter);
84 #endif
85
86 ASSERT(object);
87 SVGElement* svgElement = static_cast<SVGElement*>(object->node());
88 ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
89
90 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
91 const RenderStyle* style = object->style();
92 ASSERT(style);
93
94 const SVGRenderStyle* svgStyle = style->svgStyle();
95 ASSERT(svgStyle);
96
97 // Setup transparency layers before setting up filters!
98 float opacity = style->opacity();
99 if (opacity < 1.0f) {
100 paintInfo.context->clip(repaintRect);
101 paintInfo.context->beginTransparencyLayer(opacity);
102 }
103
104 if (ShadowData* shadow = svgStyle->shadow()) {
105 paintInfo.context->clip(repaintRect);
106 paintInfo.context->setShadow(IntSize(shadow->x, shadow->y), shadow->blur, shadow->color, style->colorSpace());
107 paintInfo.context->beginTransparencyLayer(1.0f);
108 }
109
110 #if ENABLE(FILTERS)
111 AtomicString filterId(svgStyle->filter());
112 #endif
113
114 AtomicString clipperId(svgStyle->clipPath());
115 AtomicString maskerId(svgStyle->maskElement());
116
117 Document* document = object->document();
118
119 #if ENABLE(FILTERS)
120 SVGResourceFilter* newFilter = getFilterById(document, filterId, object);
121 if (newFilter == rootFilter) {
122 // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>.
123 // The filter is NOT meant to be applied twice in that case!
124 filter = 0;
125 filterId = String();
126 } else
127 filter = newFilter;
128 #endif
129
130 SVGResourceClipper* clipper = getClipperById(document, clipperId, object);
131 SVGResourceMasker* masker = getMaskerById(document, maskerId, object);
132
133 if (masker) {
134 masker->addClient(styledElement);
135 if (!masker->applyMask(paintInfo.context, object))
136 return false;
137 } else if (!maskerId.isEmpty())
138 svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement);
139
140 if (clipper) {
141 clipper->addClient(styledElement);
142 clipper->applyClip(paintInfo.context, object->objectBoundingBox());
143 } else if (!clipperId.isEmpty())
144 svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement);
145
146 #if ENABLE(FILTERS)
147 if (filter) {
148 filter->addClient(styledElement);
149 if (!filter->prepareFilter(paintInfo.context, object))
150 return false;
151 } else if (!filterId.isEmpty())
152 svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement);
153 #endif
154
155 return true;
156 }
157
finishRenderSVGContent(RenderObject * object,RenderObject::PaintInfo & paintInfo,SVGResourceFilter * & filter,GraphicsContext * savedContext)158 void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, SVGResourceFilter*& filter, GraphicsContext* savedContext)
159 {
160 #if !ENABLE(FILTERS)
161 UNUSED_PARAM(filter);
162 UNUSED_PARAM(savedContext);
163 #endif
164
165 ASSERT(object);
166
167 const RenderStyle* style = object->style();
168 ASSERT(style);
169
170 #if ENABLE(FILTERS)
171 if (filter) {
172 filter->applyFilter(paintInfo.context, object);
173 paintInfo.context = savedContext;
174 }
175 #endif
176
177 float opacity = style->opacity();
178 if (opacity < 1.0f)
179 paintInfo.context->endTransparencyLayer();
180
181 // This needs to be done separately from opacity, because if both properties are set,
182 // then the transparency layers are nested.
183 if (style->svgStyle()->shadow())
184 paintInfo.context->endTransparencyLayer();
185 }
186
renderSubtreeToImage(ImageBuffer * image,RenderObject * item)187 void renderSubtreeToImage(ImageBuffer* image, RenderObject* item)
188 {
189 ASSERT(item);
190 ASSERT(image);
191 ASSERT(image->context());
192 RenderObject::PaintInfo info(image->context(), IntRect(), PaintPhaseForeground, 0, 0, 0);
193
194 // FIXME: isSVGContainer returns true for RenderSVGViewportContainer, so if this is ever
195 // called with one of those, we will read from the wrong offset in an object due to a bad cast.
196 RenderSVGContainer* svgContainer = 0;
197 if (item && item->isSVGContainer())
198 svgContainer = toRenderSVGContainer(item);
199
200 bool drawsContents = svgContainer ? svgContainer->drawsContents() : false;
201 if (svgContainer && !drawsContents)
202 svgContainer->setDrawsContents(true);
203
204 item->layoutIfNeeded();
205 item->paint(info, 0, 0);
206
207 if (svgContainer && !drawsContents)
208 svgContainer->setDrawsContents(false);
209 }
210
clampImageBufferSizeToViewport(FrameView * frameView,IntSize & size)211 void clampImageBufferSizeToViewport(FrameView* frameView, IntSize& size)
212 {
213 if (!frameView)
214 return;
215
216 int viewWidth = frameView->visibleWidth();
217 int viewHeight = frameView->visibleHeight();
218
219 if (size.width() > viewWidth)
220 size.setWidth(viewWidth);
221
222 if (size.height() > viewHeight)
223 size.setHeight(viewHeight);
224 }
225
computeContainerBoundingBox(const RenderObject * container,bool includeAllPaintedContent)226 FloatRect SVGRenderBase::computeContainerBoundingBox(const RenderObject* container, bool includeAllPaintedContent)
227 {
228 FloatRect boundingBox;
229
230 RenderObject* current = container->firstChild();
231 for (; current != 0; current = current->nextSibling()) {
232 FloatRect childBBox = includeAllPaintedContent ? current->repaintRectInLocalCoordinates() : current->objectBoundingBox();
233 FloatRect childBBoxInLocalCoords = current->localToParentTransform().mapRect(childBBox);
234 boundingBox.unite(childBBoxInLocalCoords);
235 }
236
237 return boundingBox;
238 }
239
layoutChildren(RenderObject * start,bool selfNeedsLayout)240 void SVGRenderBase::layoutChildren(RenderObject* start, bool selfNeedsLayout)
241 {
242 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
243 // Only force our kids to layout if we're being asked to relayout as a result of a parent changing
244 // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed
245 // that's a possible future optimization using LayoutState
246 // http://bugs.webkit.org/show_bug.cgi?id=15391
247 bool needsLayout = selfNeedsLayout;
248 if (!needsLayout) {
249 if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
250 if (element->isStyled())
251 needsLayout = static_cast<SVGStyledElement*>(element)->hasRelativeValues();
252 }
253 }
254
255 if (needsLayout)
256 child->setNeedsLayout(true, false);
257
258 child->layoutIfNeeded();
259 ASSERT(!child->needsLayout());
260 }
261 }
262
isOverflowHidden(const RenderObject * object)263 bool SVGRenderBase::isOverflowHidden(const RenderObject* object)
264 {
265 // SVG doesn't support independent x/y overflow
266 ASSERT(object->style()->overflowX() == object->style()->overflowY());
267
268 // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
269 ASSERT(object->style()->overflowX() != OSCROLL);
270
271 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
272 ASSERT(!object->isRoot());
273
274 return object->style()->overflowX() == OHIDDEN;
275 }
276
filterBoundingBoxForRenderer(const RenderObject * object) const277 FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) const
278 {
279 #if ENABLE(FILTERS)
280 SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter(), object);
281 if (filter)
282 return filter->filterBoundingBox(object->objectBoundingBox());
283 #else
284 UNUSED_PARAM(object);
285 #endif
286 return FloatRect();
287 }
288
clipperBoundingBoxForRenderer(const RenderObject * object) const289 FloatRect SVGRenderBase::clipperBoundingBoxForRenderer(const RenderObject* object) const
290 {
291 SVGResourceClipper* clipper = getClipperById(object->document(), object->style()->svgStyle()->clipPath(), object);
292 if (clipper)
293 return clipper->clipperBoundingBox(object->objectBoundingBox());
294
295 return FloatRect();
296 }
297
maskerBoundingBoxForRenderer(const RenderObject * object) const298 FloatRect SVGRenderBase::maskerBoundingBoxForRenderer(const RenderObject* object) const
299 {
300 SVGResourceMasker* masker = getMaskerById(object->document(), object->style()->svgStyle()->maskElement(), object);
301 if (masker)
302 return masker->maskerBoundingBox(object->objectBoundingBox());
303
304 return FloatRect();
305 }
306
applyTransformToPaintInfo(RenderObject::PaintInfo & paintInfo,const AffineTransform & localToAncestorTransform)307 void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const AffineTransform& localToAncestorTransform)
308 {
309 if (localToAncestorTransform.isIdentity())
310 return;
311
312 paintInfo.context->concatCTM(localToAncestorTransform);
313 paintInfo.rect = localToAncestorTransform.inverse().mapRect(paintInfo.rect);
314 }
315
316 } // namespace WebCore
317
318 #endif // ENABLE(SVG)
319