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 * Copyright (C) 2009 Google, Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "SVGRenderSupport.h"
28
29 #include "ImageBuffer.h"
30 #include "RenderObject.h"
31 #include "RenderSVGContainer.h"
32 #include "RenderView.h"
33 #include "SVGResourceClipper.h"
34 #include "SVGResourceFilter.h"
35 #include "SVGResourceMasker.h"
36 #include "SVGStyledElement.h"
37 #include "SVGURIReference.h"
38 #include "TransformState.h"
39 #include "TransformationMatrix.h"
40 #include <wtf/UnusedParam.h>
41
42 namespace WebCore {
43
clippedOverflowRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer)44 IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer)
45 {
46 // Return early for any cases where we don't actually paint
47 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
48 return IntRect();
49
50 // Pass our local paint rect to computeRectForRepaint() which will
51 // map to parent coords and recurse up the parent chain.
52 IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates());
53 object->computeRectForRepaint(repaintContainer, repaintRect);
54 return repaintRect;
55 }
56
computeRectForRepaint(RenderObject * object,RenderBoxModelObject * repaintContainer,IntRect & repaintRect,bool fixed)57 void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
58 {
59 // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent
60 repaintRect = object->localToParentTransform().mapRect(repaintRect);
61 object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed);
62 }
63
mapLocalToContainer(const RenderObject * object,RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState)64 void SVGRenderBase::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState)
65 {
66 ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
67 ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless.
68 transformState.applyTransform(object->localToParentTransform());
69 object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
70 }
71
prepareToRenderSVGContent(RenderObject * object,RenderObject::PaintInfo & paintInfo,const FloatRect & boundingBox,SVGResourceFilter * & filter,SVGResourceFilter * rootFilter)72 void SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& boundingBox, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter)
73 {
74 #if !ENABLE(FILTERS)
75 UNUSED_PARAM(filter);
76 UNUSED_PARAM(rootFilter);
77 #endif
78
79 ASSERT(object);
80 SVGElement* svgElement = static_cast<SVGElement*>(object->node());
81 ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
82
83 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
84 const 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 filters!
91 float opacity = style->opacity();
92 if (opacity < 1.0f) {
93 paintInfo.context->clip(enclosingIntRect(boundingBox));
94 paintInfo.context->beginTransparencyLayer(opacity);
95 }
96
97 #if ENABLE(FILTERS)
98 AtomicString filterId(svgStyle->filter());
99 #endif
100
101 AtomicString clipperId(svgStyle->clipPath());
102 AtomicString maskerId(svgStyle->maskElement());
103
104 Document* document = object->document();
105
106 #if ENABLE(FILTERS)
107 SVGResourceFilter* newFilter = getFilterById(document, filterId);
108 if (newFilter == rootFilter) {
109 // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>.
110 // The filter is NOT meant to be applied twice in that case!
111 filter = 0;
112 filterId = String();
113 } else
114 filter = newFilter;
115 #endif
116
117 SVGResourceClipper* clipper = getClipperById(document, clipperId);
118 SVGResourceMasker* masker = getMaskerById(document, maskerId);
119
120 #if ENABLE(FILTERS)
121 if (filter) {
122 filter->addClient(styledElement);
123 filter->prepareFilter(paintInfo.context, object);
124 } else if (!filterId.isEmpty())
125 svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement);
126 #endif
127
128 if (clipper) {
129 clipper->addClient(styledElement);
130 clipper->applyClip(paintInfo.context, boundingBox);
131 } else if (!clipperId.isEmpty())
132 svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement);
133
134 if (masker) {
135 masker->addClient(styledElement);
136 masker->applyMask(paintInfo.context, boundingBox);
137 } else if (!maskerId.isEmpty())
138 svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement);
139 }
140
finishRenderSVGContent(RenderObject * object,RenderObject::PaintInfo & paintInfo,SVGResourceFilter * & filter,GraphicsContext * savedContext)141 void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, SVGResourceFilter*& filter, GraphicsContext* savedContext)
142 {
143 #if !ENABLE(FILTERS)
144 UNUSED_PARAM(filter);
145 UNUSED_PARAM(savedContext);
146 #endif
147
148 ASSERT(object);
149
150 const RenderStyle* style = object->style();
151 ASSERT(style);
152
153 #if ENABLE(FILTERS)
154 if (filter) {
155 filter->applyFilter(paintInfo.context, object);
156 paintInfo.context = savedContext;
157 }
158 #endif
159
160 float opacity = style->opacity();
161 if (opacity < 1.0f)
162 paintInfo.context->endTransparencyLayer();
163 }
164
renderSubtreeToImage(ImageBuffer * image,RenderObject * item)165 void renderSubtreeToImage(ImageBuffer* image, RenderObject* item)
166 {
167 ASSERT(item);
168 ASSERT(image);
169 ASSERT(image->context());
170 RenderObject::PaintInfo info(image->context(), IntRect(), PaintPhaseForeground, 0, 0, 0);
171
172 // FIXME: isSVGContainer returns true for RenderSVGViewportContainer, so if this is ever
173 // called with one of those, we will read from the wrong offset in an object due to a bad cast.
174 RenderSVGContainer* svgContainer = 0;
175 if (item && item->isSVGContainer())
176 svgContainer = toRenderSVGContainer(item);
177
178 bool drawsContents = svgContainer ? svgContainer->drawsContents() : false;
179 if (svgContainer && !drawsContents)
180 svgContainer->setDrawsContents(true);
181
182 item->layoutIfNeeded();
183 item->paint(info, 0, 0);
184
185 if (svgContainer && !drawsContents)
186 svgContainer->setDrawsContents(false);
187 }
188
clampImageBufferSizeToViewport(FrameView * frameView,IntSize & size)189 void clampImageBufferSizeToViewport(FrameView* frameView, IntSize& size)
190 {
191 if (!frameView)
192 return;
193
194 int viewWidth = frameView->visibleWidth();
195 int viewHeight = frameView->visibleHeight();
196
197 if (size.width() > viewWidth)
198 size.setWidth(viewWidth);
199
200 if (size.height() > viewHeight)
201 size.setHeight(viewHeight);
202 }
203
computeContainerBoundingBox(const RenderObject * container,bool includeAllPaintedContent)204 FloatRect SVGRenderBase::computeContainerBoundingBox(const RenderObject* container, bool includeAllPaintedContent)
205 {
206 FloatRect boundingBox;
207
208 RenderObject* current = container->firstChild();
209 for (; current != 0; current = current->nextSibling()) {
210 FloatRect childBBox = includeAllPaintedContent ? current->repaintRectInLocalCoordinates() : current->objectBoundingBox();
211 FloatRect childBBoxInLocalCoords = current->localToParentTransform().mapRect(childBBox);
212 boundingBox.unite(childBBoxInLocalCoords);
213 }
214
215 return boundingBox;
216 }
217
filterBoundingBoxForRenderer(const RenderObject * object)218 FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object)
219 {
220 #if ENABLE(FILTERS)
221 SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter());
222 if (filter)
223 return filter->filterBBoxForItemBBox(object->objectBoundingBox());
224 #else
225 UNUSED_PARAM(object);
226 #endif
227 return FloatRect();
228 }
229
applyTransformToPaintInfo(RenderObject::PaintInfo & paintInfo,const TransformationMatrix & localToAncestorTransform)230 void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const TransformationMatrix& localToAncestorTransform)
231 {
232 if (localToAncestorTransform.isIdentity())
233 return;
234
235 paintInfo.context->concatCTM(localToAncestorTransform);
236 paintInfo.rect = localToAncestorTransform.inverse().mapRect(paintInfo.rect);
237 }
238
239 } // namespace WebCore
240
241 #endif // ENABLE(SVG)
242