2 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3 2004, 2005 Rob Buis <buis@kde.org>
5 This file is part of the KDE project
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.
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.
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.
25 #include "RenderSVGContainer.h"
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"
37 RenderSVGContainer::RenderSVGContainer(SVGStyledElement *node)
38 : RenderContainer(node)
39 , m_drawsContents(true)
45 RenderSVGContainer::~RenderSVGContainer()
49 bool RenderSVGContainer::drawsContents() const
51 return m_drawsContents;
54 void RenderSVGContainer::setDrawsContents(bool drawsContents)
56 m_drawsContents = drawsContents;
59 AffineTransform RenderSVGContainer::localTransform() const
64 void RenderSVGContainer::setLocalTransform(const AffineTransform& matrix)
69 bool RenderSVGContainer::canHaveChildren() const
74 bool RenderSVGContainer::requiresLayer()
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());
80 short RenderSVGContainer::lineHeight(bool b, bool isRootLineBox) const
82 return height() + marginTop() + marginBottom();
85 short RenderSVGContainer::baselinePosition(bool b, bool isRootLineBox) const
87 return height() + marginTop() + marginBottom();
90 void RenderSVGContainer::calcMinMaxWidth()
92 ASSERT(!minMaxKnown());
93 m_minWidth = m_maxWidth = 0;
97 void RenderSVGContainer::layout()
99 ASSERT(needsLayout());
100 ASSERT(minMaxKnown());
103 bool checkForRepaint = checkForRepaintDuringLayout();
104 if (selfNeedsLayout() && checkForRepaint)
105 oldBounds = m_absoluteBounds;
107 RenderObject* child = firstChild();
109 if (!child->isRenderPath() || static_cast<RenderPath*>(child)->hasPercentageValues())
110 child->setNeedsLayout(true);
111 child = child->nextSibling();
113 RenderContainer::layout();
117 m_absoluteBounds = getAbsoluteRepaintRect();
119 if (selfNeedsLayout() && checkForRepaint)
120 repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
123 void RenderSVGContainer::paint(PaintInfo& paintInfo, int parentX, int parentY)
125 if (paintInfo.p->paintingDisabled())
128 // No one should be transforming us via these.
132 if (shouldPaintBackgroundOrBorder() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
133 paintBoxDecorations(paintInfo, parentX, parentY);
135 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
136 paintOutline(paintInfo.p, parentX, parentY, width(), height(), style());
138 if (paintInfo.phase != PaintPhaseForeground || !drawsContents() || style()->visibility() == HIDDEN)
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.
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;
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;
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()));
168 if (!localTransform().isIdentity())
169 deviceContext->concatCTM(localTransform());
171 if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().substring(1)))
172 clipper->applyClip(relativeBBox(true));
174 if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().substring(1)))
175 masker->applyMask(relativeBBox(true));
177 float opacity = style()->opacity();
179 paintInfo.p->beginTransparencyLayer(opacity);
182 filter->prepareFilter(relativeBBox(true));
184 if (!viewBox().isEmpty())
185 deviceContext->concatCTM(viewportTransform());
187 RenderContainer::paint(paintInfo, 0, 0);
190 filter->applyFilter(relativeBBox(true));
193 paintInfo.p->endTransparencyLayer();
195 // restore drawing state
196 if (shouldPopContext) {
197 device->popContext();
198 delete deviceContext;
200 paintInfo.p->restore();
203 void RenderSVGContainer::setViewport(const FloatRect& viewport)
205 m_viewport = viewport;
208 FloatRect RenderSVGContainer::viewport() const
213 void RenderSVGContainer::setViewBox(const FloatRect& viewBox)
218 FloatRect RenderSVGContainer::viewBox() const
223 void RenderSVGContainer::setAlign(KCAlign align)
228 KCAlign RenderSVGContainer::align() const
233 AffineTransform RenderSVGContainer::viewportTransform() const
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);
241 return AffineTransform();
244 IntRect RenderSVGContainer::getAbsoluteRepaintRect()
248 for (RenderObject *current = firstChild(); current != 0; current = current->nextSibling())
249 repaintRect.unite(current->getAbsoluteRepaintRect());
251 // Filters can expand the bounding box
252 KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
254 repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect)));
256 // FIXME: what about transform?
261 void RenderSVGContainer::computeAbsoluteRepaintRect(IntRect& r, bool f)
263 AffineTransform transform = localTransform();
264 r = transform.mapRect(r);
266 // FIXME: consider filter
268 RenderContainer::computeAbsoluteRepaintRect(r, f);
271 AffineTransform RenderSVGContainer::absoluteTransform() const
273 return viewportTransform() * RenderContainer::absoluteTransform();
276 bool RenderSVGContainer::fillContains(const FloatPoint& p) const
278 RenderObject *current = firstChild();
279 while (current != 0) {
280 if (current->isRenderPath() && static_cast<RenderPath*>(current)->fillContains(p))
282 current = current->nextSibling();
288 bool RenderSVGContainer::strokeContains(const FloatPoint& p) const
290 RenderObject *current = firstChild();
291 while (current != 0) {
292 if (current->isRenderPath() && static_cast<RenderPath*>(current)->strokeContains(p))
294 current = current->nextSibling();
300 FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
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);
314 void RenderSVGContainer::setSlice(bool slice)
319 bool RenderSVGContainer::slice() const
324 AffineTransform RenderSVGContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
326 AffineTransform temp;
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();
335 float vpar = logicWidth / logicHeight;
336 float svgar = physWidth / physHeight;
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);
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);
349 temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
351 temp.scale(physWidth / logicWidth, physWidth / logicWidth);
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);
358 temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
367 #endif // SVG_SUPPORT