6cbf52e12afec1a4aeb1c1be0b3020f30729e50e
[WebKit-https.git] / WebCore / kcanvas / RenderSVGContainer.cpp
1 /*
2     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE project
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     aint with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20     Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24 #ifdef SVG_SUPPORT
25 #include "RenderSVGContainer.h"
26
27 #include "KCanvasClipper.h"
28 #include "KCanvasMasker.h"
29 #include "KCanvasRenderingStyle.h"
30 #include "KRenderingDevice.h"
31 #include "SVGStyledElement.h"
32 #include "GraphicsContext.h"
33 #include "SVGStyledTransformableElement.h"
34
35 namespace WebCore {
36
37 RenderSVGContainer::RenderSVGContainer(SVGStyledElement *node)
38     : RenderContainer(node)
39     , m_drawsContents(true)
40     , m_slice(false)
41 {
42     setReplaced(true);
43 }
44
45 RenderSVGContainer::~RenderSVGContainer()
46 {
47 }
48
49 bool RenderSVGContainer::drawsContents() const
50 {
51     return m_drawsContents;
52 }
53
54 void RenderSVGContainer::setDrawsContents(bool drawsContents)
55 {
56     m_drawsContents = drawsContents;
57 }
58
59 AffineTransform RenderSVGContainer::localTransform() const
60 {
61     return m_matrix;
62 }
63
64 void RenderSVGContainer::setLocalTransform(const AffineTransform& matrix)
65 {
66     m_matrix = matrix;
67 }
68
69 bool RenderSVGContainer::canHaveChildren() const
70 {
71     return true;
72 }
73     
74 bool RenderSVGContainer::requiresLayer()
75 {
76     // Only allow an <svg> element to generate a layer when it's positioned in a non-SVG context
77     return (isPositioned() || isRelPositioned()) && (parent() && parent()->element() && !parent()->element()->isSVGElement());
78 }
79
80 short RenderSVGContainer::lineHeight(bool b, bool isRootLineBox) const
81 {
82     return height() + marginTop() + marginBottom();
83 }
84
85 short RenderSVGContainer::baselinePosition(bool b, bool isRootLineBox) const
86 {
87     return height() + marginTop() + marginBottom();
88 }
89
90 void RenderSVGContainer::calcMinMaxWidth()
91 {
92     ASSERT(!minMaxKnown());
93     m_minWidth = m_maxWidth = 0;
94     setMinMaxKnown();
95 }
96
97 void RenderSVGContainer::layout()
98 {
99     ASSERT(needsLayout());
100     ASSERT(minMaxKnown());
101
102     IntRect oldBounds;
103     bool checkForRepaint = checkForRepaintDuringLayout();
104     if (selfNeedsLayout() && checkForRepaint)
105         oldBounds = m_absoluteBounds;
106
107     RenderObject* child = firstChild();
108     while (child) {
109         if (!child->isRenderPath() || static_cast<RenderPath*>(child)->hasPercentageValues())
110             child->setNeedsLayout(true);
111         child = child->nextSibling();
112     }
113     RenderContainer::layout();
114     calcWidth();
115     calcHeight();
116
117     m_absoluteBounds = getAbsoluteRepaintRect();
118
119     if (selfNeedsLayout() && checkForRepaint)
120         repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
121 }
122
123 void RenderSVGContainer::paint(PaintInfo& paintInfo, int parentX, int parentY)
124 {
125     if (paintInfo.p->paintingDisabled())
126         return;
127     
128     // No one should be transforming us via these.
129     //ASSERT(m_x == 0);
130     //ASSERT(m_y == 0);
131         
132     if (shouldPaintBackgroundOrBorder() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 
133         paintBoxDecorations(paintInfo, parentX, parentY);
134     
135     if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
136         paintOutline(paintInfo.p, parentX, parentY, width(), height(), style());
137     
138     if (paintInfo.phase != PaintPhaseForeground || !drawsContents() || style()->visibility() == HIDDEN)
139         return;
140     
141     KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
142     if (!firstChild() && !filter)
143         return; // Spec: groups w/o children still may render filter content.
144     
145     KRenderingDevice* device = renderingDevice();
146     KRenderingDeviceContext* deviceContext = device->currentContext();
147     bool shouldPopContext = false;
148     if (!deviceContext) {
149         // I only need to setup for KCanvas rendering if it hasn't already been done.
150         deviceContext = paintInfo.p->createRenderingDeviceContext();
151         device->pushContext(deviceContext);
152         shouldPopContext = true;
153     } else
154         paintInfo.p->save();
155     
156     if (parentX != 0 || parentY != 0) {
157         // Translate from parent offsets (khtml) to a relative transform (ksvg2/kcanvas)
158         deviceContext->concatCTM(AffineTransform().translate(parentX, parentY));
159         parentX = parentY = 0;
160     }
161     
162     if (!viewport().isEmpty()) {
163         if (style()->overflowX() != OVISIBLE)
164             paintInfo.p->addClip(enclosingIntRect(viewport())); // FIXME: Eventually we'll want float-precision clipping
165         deviceContext->concatCTM(AffineTransform().translate(viewport().x(), viewport().y()));
166     }
167     
168     if (!localTransform().isIdentity())
169         deviceContext->concatCTM(localTransform());
170     
171     if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().substring(1)))
172         clipper->applyClip(relativeBBox(true));
173
174     if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().substring(1)))
175         masker->applyMask(relativeBBox(true));
176
177     float opacity = style()->opacity();
178     if (opacity < 1.0f)
179         paintInfo.p->beginTransparencyLayer(opacity);
180
181     if (filter)
182         filter->prepareFilter(relativeBBox(true));
183     
184     if (!viewBox().isEmpty())
185         deviceContext->concatCTM(viewportTransform());
186     
187     RenderContainer::paint(paintInfo, 0, 0);
188     
189     if (filter)
190         filter->applyFilter(relativeBBox(true));
191     
192     if (opacity < 1.0f)
193         paintInfo.p->endTransparencyLayer();
194     
195     // restore drawing state
196     if (shouldPopContext) {
197         device->popContext();
198         delete deviceContext;
199     } else
200         paintInfo.p->restore();
201 }
202
203 void RenderSVGContainer::setViewport(const FloatRect& viewport)
204 {
205     m_viewport = viewport;
206 }
207
208 FloatRect RenderSVGContainer::viewport() const
209 {
210    return m_viewport;
211 }
212
213 void RenderSVGContainer::setViewBox(const FloatRect& viewBox)
214 {
215     m_viewBox = viewBox;
216 }
217
218 FloatRect RenderSVGContainer::viewBox() const
219 {
220     return m_viewBox;
221 }
222
223 void RenderSVGContainer::setAlign(KCAlign align)
224 {
225     m_align = align;
226 }
227
228 KCAlign RenderSVGContainer::align() const
229 {
230     return m_align;
231 }
232
233 AffineTransform RenderSVGContainer::viewportTransform() const
234 {
235     if (!viewBox().isEmpty()) {
236         FloatRect viewportRect = viewport();
237         if (!parent()->isKCanvasContainer())
238             viewportRect = FloatRect(viewport().x(), viewport().y(), width(), height());
239         return getAspectRatio(viewBox(), viewportRect);
240     }
241     return AffineTransform();
242 }
243
244 IntRect RenderSVGContainer::getAbsoluteRepaintRect()
245 {
246     IntRect repaintRect;
247     
248     for (RenderObject *current = firstChild(); current != 0; current = current->nextSibling())
249         repaintRect.unite(current->getAbsoluteRepaintRect());
250     
251     // Filters can expand the bounding box
252     KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
253     if (filter)
254         repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect)));
255
256     // FIXME: what about transform?
257
258     return repaintRect;
259 }
260
261 void RenderSVGContainer::computeAbsoluteRepaintRect(IntRect& r, bool f)
262 {
263     AffineTransform transform = localTransform();
264     r = transform.mapRect(r);
265     
266     // FIXME: consider filter
267
268     RenderContainer::computeAbsoluteRepaintRect(r, f);
269 }
270
271 AffineTransform RenderSVGContainer::absoluteTransform() const
272 {
273     return viewportTransform() * RenderContainer::absoluteTransform();
274 }
275
276 bool RenderSVGContainer::fillContains(const FloatPoint& p) const
277 {
278     RenderObject *current = firstChild();
279     while (current != 0) {
280         if (current->isRenderPath() && static_cast<RenderPath*>(current)->fillContains(p))
281             return true;
282         current = current->nextSibling();
283     }
284
285     return false;
286 }
287
288 bool RenderSVGContainer::strokeContains(const FloatPoint& p) const
289 {
290     RenderObject *current = firstChild();
291     while (current != 0) {
292         if (current->isRenderPath() && static_cast<RenderPath*>(current)->strokeContains(p))
293             return true;
294         current = current->nextSibling();
295     }
296
297     return false;
298 }
299
300 FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
301 {
302     FloatRect rect;
303     
304     RenderObject *current = firstChild();
305     for (; current != 0; current = current->nextSibling()) {
306         FloatRect childBBox = current->relativeBBox(includeStroke);
307         FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
308         rect.unite(mappedBBox);
309     }
310
311     return rect;
312 }
313
314 void RenderSVGContainer::setSlice(bool slice)
315 {
316     m_slice = slice;
317 }
318
319 bool RenderSVGContainer::slice() const
320 {
321     return m_slice;
322 }
323
324 AffineTransform RenderSVGContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
325 {
326     AffineTransform temp;
327
328     float logicX = logical.x();
329     float logicY = logical.y();
330     float logicWidth = logical.width();
331     float logicHeight = logical.height();
332     float physWidth = physical.width();
333     float physHeight = physical.height();
334
335     float vpar = logicWidth / logicHeight;
336     float svgar = physWidth / physHeight;
337
338     if (align() == ALIGN_NONE) {
339         temp.scale(physWidth / logicWidth, physHeight / logicHeight);
340         temp.translate(-logicX, -logicY);
341     } else if ((vpar < svgar && !slice()) || (vpar >= svgar && slice())) {
342         temp.scale(physHeight / logicHeight, physHeight / logicHeight);
343
344         if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMINYMID || align() == ALIGN_XMINYMAX)
345             temp.translate(-logicX, -logicY);
346         else if (align() == ALIGN_XMIDYMIN || align() == ALIGN_XMIDYMID || align() == ALIGN_XMIDYMAX)
347             temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
348         else
349             temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
350     } else {
351         temp.scale(physWidth / logicWidth, physWidth / logicWidth);
352
353         if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMIDYMIN || align() == ALIGN_XMAXYMIN)
354             temp.translate(-logicX, -logicY);
355         else if (align() == ALIGN_XMINYMID || align() == ALIGN_XMIDYMID || align() == ALIGN_XMAXYMID)
356             temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
357         else
358             temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
359     }
360
361     return temp;
362 }
363
364 }
365
366 // vim:ts=4:noet
367 #endif // SVG_SUPPORT
368