• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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