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 "SVGLength.h"
34 #include "SVGMarkerElement.h"
35 #include "SVGSVGElement.h"
36 #include "SVGStyledTransformableElement.h"
40 RenderSVGContainer::RenderSVGContainer(SVGStyledElement *node)
41 : RenderContainer(node)
42 , m_drawsContents(true)
48 RenderSVGContainer::~RenderSVGContainer()
52 bool RenderSVGContainer::drawsContents() const
54 return m_drawsContents;
57 void RenderSVGContainer::setDrawsContents(bool drawsContents)
59 m_drawsContents = drawsContents;
62 AffineTransform RenderSVGContainer::localTransform() const
67 void RenderSVGContainer::setLocalTransform(const AffineTransform& matrix)
72 bool RenderSVGContainer::canHaveChildren() const
77 bool RenderSVGContainer::requiresLayer()
79 // Only allow an <svg> element to generate a layer when it's positioned in a non-SVG context
80 return (isPositioned() || isRelPositioned()) && (parent() && parent()->element() && !parent()->element()->isSVGElement());
83 short RenderSVGContainer::lineHeight(bool b, bool isRootLineBox) const
85 return height() + marginTop() + marginBottom();
88 short RenderSVGContainer::baselinePosition(bool b, bool isRootLineBox) const
90 return height() + marginTop() + marginBottom();
93 void RenderSVGContainer::calcMinMaxWidth()
95 ASSERT(!minMaxKnown());
96 m_minWidth = m_maxWidth = 0;
100 void RenderSVGContainer::layout()
102 ASSERT(needsLayout());
103 ASSERT(minMaxKnown());
105 if (selfNeedsLayout())
109 bool checkForRepaint = checkForRepaintDuringLayout();
110 if (selfNeedsLayout() && checkForRepaint)
111 oldBounds = m_absoluteBounds;
113 RenderObject* child = firstChild();
115 if (!child->isRenderPath() || static_cast<RenderPath*>(child)->hasPercentageValues())
116 child->setNeedsLayout(true);
117 child = child->nextSibling();
119 RenderContainer::layout();
123 m_absoluteBounds = getAbsoluteRepaintRect();
125 if (selfNeedsLayout() && checkForRepaint)
126 repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
129 void RenderSVGContainer::paint(PaintInfo& paintInfo, int parentX, int parentY)
131 if (paintInfo.p->paintingDisabled())
134 // No one should be transforming us via these.
138 if (shouldPaintBackgroundOrBorder() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
139 paintBoxDecorations(paintInfo, parentX, parentY);
141 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
142 paintOutline(paintInfo.p, parentX, parentY, width(), height(), style());
144 if (paintInfo.phase != PaintPhaseForeground || !drawsContents() || style()->visibility() == HIDDEN)
147 KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
148 if (!firstChild() && !filter)
149 return; // Spec: groups w/o children still may render filter content.
151 KRenderingDevice* device = renderingDevice();
152 KRenderingDeviceContext* deviceContext = device->currentContext();
153 bool shouldPopContext = false;
154 if (!deviceContext) {
155 // I only need to setup for KCanvas rendering if it hasn't already been done.
156 deviceContext = paintInfo.p->createRenderingDeviceContext();
157 device->pushContext(deviceContext);
158 shouldPopContext = true;
162 if (parentX != 0 || parentY != 0) {
163 // Translate from parent offsets (khtml) to a relative transform (ksvg2/kcanvas)
164 deviceContext->concatCTM(AffineTransform().translate(parentX, parentY));
165 parentX = parentY = 0;
168 if (!viewport().isEmpty()) {
169 if (style()->overflowX() != OVISIBLE)
170 paintInfo.p->addClip(enclosingIntRect(viewport())); // FIXME: Eventually we'll want float-precision clipping
171 deviceContext->concatCTM(AffineTransform().translate(viewport().x(), viewport().y()));
174 if (!localTransform().isIdentity())
175 deviceContext->concatCTM(localTransform());
177 if (KCanvasClipper *clipper = getClipperById(document(), style()->svgStyle()->clipPath().substring(1)))
178 clipper->applyClip(relativeBBox(true));
180 if (KCanvasMasker *masker = getMaskerById(document(), style()->svgStyle()->maskElement().substring(1)))
181 masker->applyMask(relativeBBox(true));
183 float opacity = style()->opacity();
185 paintInfo.p->beginTransparencyLayer(opacity);
188 filter->prepareFilter(relativeBBox(true));
190 if (!viewBox().isEmpty())
191 deviceContext->concatCTM(viewportTransform());
193 RenderContainer::paint(paintInfo, 0, 0);
196 filter->applyFilter(relativeBBox(true));
199 paintInfo.p->endTransparencyLayer();
201 // restore drawing state
202 if (shouldPopContext) {
203 device->popContext();
204 delete deviceContext;
206 paintInfo.p->restore();
209 FloatRect RenderSVGContainer::viewport() const
214 void RenderSVGContainer::calcViewport()
216 SVGElement* svgelem = static_cast<SVGElement*>(element());
217 if (svgelem->hasTagName(SVGNames::svgTag)) {
218 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
219 double x = svg->x()->value();
220 double y = svg->y()->value();
221 double w = svg->width()->value();
222 double h = svg->height()->value();
223 m_viewport = FloatRect(x, y, w, h);
224 } else if (svgelem->hasTagName(SVGNames::markerTag)) {
225 SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(element());
226 double w = svg->markerWidth()->value();
227 double h = svg->markerHeight()->value();
228 m_viewport = FloatRect(0, 0, w, h);
232 void RenderSVGContainer::setViewBox(const FloatRect& viewBox)
237 FloatRect RenderSVGContainer::viewBox() const
242 void RenderSVGContainer::setAlign(KCAlign align)
247 KCAlign RenderSVGContainer::align() const
252 AffineTransform RenderSVGContainer::viewportTransform() const
254 if (!viewBox().isEmpty()) {
255 FloatRect viewportRect = viewport();
256 if (!parent()->isKCanvasContainer())
257 viewportRect = FloatRect(viewport().x(), viewport().y(), width(), height());
258 return getAspectRatio(viewBox(), viewportRect);
260 return AffineTransform();
263 IntRect RenderSVGContainer::getAbsoluteRepaintRect()
267 for (RenderObject *current = firstChild(); current != 0; current = current->nextSibling())
268 repaintRect.unite(current->getAbsoluteRepaintRect());
270 // Filters can expand the bounding box
271 KCanvasFilter *filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
273 repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect)));
275 // FIXME: what about transform?
280 void RenderSVGContainer::computeAbsoluteRepaintRect(IntRect& r, bool f)
282 AffineTransform transform = localTransform();
283 r = transform.mapRect(r);
285 // FIXME: consider filter
287 RenderContainer::computeAbsoluteRepaintRect(r, f);
290 AffineTransform RenderSVGContainer::absoluteTransform() const
292 return viewportTransform() * RenderContainer::absoluteTransform();
295 bool RenderSVGContainer::fillContains(const FloatPoint& p) const
297 RenderObject *current = firstChild();
298 while (current != 0) {
299 if (current->isRenderPath() && static_cast<RenderPath*>(current)->fillContains(p))
301 current = current->nextSibling();
307 bool RenderSVGContainer::strokeContains(const FloatPoint& p) const
309 RenderObject *current = firstChild();
310 while (current != 0) {
311 if (current->isRenderPath() && static_cast<RenderPath*>(current)->strokeContains(p))
313 current = current->nextSibling();
319 FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
323 RenderObject *current = firstChild();
324 for (; current != 0; current = current->nextSibling()) {
325 FloatRect childBBox = current->relativeBBox(includeStroke);
326 FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
327 rect.unite(mappedBBox);
333 void RenderSVGContainer::setSlice(bool slice)
338 bool RenderSVGContainer::slice() const
343 AffineTransform RenderSVGContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
345 AffineTransform temp;
347 float logicX = logical.x();
348 float logicY = logical.y();
349 float logicWidth = logical.width();
350 float logicHeight = logical.height();
351 float physWidth = physical.width();
352 float physHeight = physical.height();
354 float vpar = logicWidth / logicHeight;
355 float svgar = physWidth / physHeight;
357 if (align() == ALIGN_NONE) {
358 temp.scale(physWidth / logicWidth, physHeight / logicHeight);
359 temp.translate(-logicX, -logicY);
360 } else if ((vpar < svgar && !slice()) || (vpar >= svgar && slice())) {
361 temp.scale(physHeight / logicHeight, physHeight / logicHeight);
363 if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMINYMID || align() == ALIGN_XMINYMAX)
364 temp.translate(-logicX, -logicY);
365 else if (align() == ALIGN_XMIDYMIN || align() == ALIGN_XMIDYMID || align() == ALIGN_XMIDYMAX)
366 temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
368 temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
370 temp.scale(physWidth / logicWidth, physWidth / logicWidth);
372 if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMIDYMIN || align() == ALIGN_XMAXYMIN)
373 temp.translate(-logicX, -logicY);
374 else if (align() == ALIGN_XMINYMID || align() == ALIGN_XMIDYMID || align() == ALIGN_XMAXYMID)
375 temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
377 temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
386 #endif // SVG_SUPPORT