webengine/osswebengine/WebCore/rendering/RenderSVGContainer.cpp
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/RenderSVGContainer.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,491 @@
+/*
+    Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+                  2004, 2005, 2007 Rob Buis <buis@kde.org>
+
+    This file is part of the KDE project
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    aint with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+
+#include "GraphicsContext.h"
+#include "RenderView.h"
+#include "SVGLength.h"
+#include "SVGMarkerElement.h"
+#include "SVGResourceClipper.h"
+#include "SVGResourceFilter.h"
+#include "SVGResourceMasker.h"
+#include "SVGSVGElement.h"
+#include "SVGStyledElement.h"
+#include "SVGURIReference.h"
+
+namespace WebCore {
+
+RenderSVGContainer::RenderSVGContainer(SVGStyledElement *node)
+    : RenderContainer(node)
+    , m_drawsContents(true)
+    , m_slice(false)
+{
+    setReplaced(true);
+}
+
+RenderSVGContainer::~RenderSVGContainer()
+{
+}
+
+bool RenderSVGContainer::drawsContents() const
+{
+    return m_drawsContents;
+}
+
+void RenderSVGContainer::setDrawsContents(bool drawsContents)
+{
+    m_drawsContents = drawsContents;
+}
+
+AffineTransform RenderSVGContainer::localTransform() const
+{
+    return m_matrix;
+}
+
+void RenderSVGContainer::setLocalTransform(const AffineTransform& matrix)
+{
+    m_matrix = matrix;
+}
+
+bool RenderSVGContainer::requiresLayer()
+{
+    // Only allow an <svg> element to generate a layer when it's positioned in a non-SVG context
+    return (isPositioned() || isRelPositioned()) && (element()->parent() && !element()->parent()->isSVGElement());
+}
+
+short RenderSVGContainer::lineHeight(bool b, bool isRootLineBox) const
+{
+    return height() + marginTop() + marginBottom();
+}
+
+short RenderSVGContainer::baselinePosition(bool b, bool isRootLineBox) const
+{
+    return height() + marginTop() + marginBottom();
+}
+
+void RenderSVGContainer::layout()
+{
+    ASSERT(needsLayout());
+
+    calcViewport();
+
+    // Arbitrary affine transforms are incompatible with LayoutState.
+    view()->disableLayoutState();
+
+    IntRect oldBounds;
+    IntRect oldOutlineBox;
+    bool checkForRepaint = checkForRepaintDuringLayout();
+    if (selfNeedsLayout() && checkForRepaint) {
+        oldBounds = m_absoluteBounds;
+        oldOutlineBox = absoluteOutlineBox();
+    }
+
+    RenderObject* child = firstChild();
+    while (child) {
+        if (!child->isRenderPath() || static_cast<RenderPath*>(child)->hasRelativeValues())
+            child->setNeedsLayout(true);
+
+        child->layoutIfNeeded();
+        ASSERT(!child->needsLayout());
+        child = child->nextSibling();
+    }
+
+    calcWidth();
+    calcHeight();
+
+    m_absoluteBounds = absoluteClippedOverflowRect();
+    if (!parent()->isSVGContainer()) {
+        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
+        m_width = static_cast<int>(static_cast<float>(m_width) * svg->currentScale());
+        m_height = static_cast<int>(static_cast<float>(m_height) * svg->currentScale());
+    }
+
+    if (selfNeedsLayout() && checkForRepaint)
+        repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
+
+    view()->enableLayoutState();
+    setNeedsLayout(false);
+}
+
+void RenderSVGContainer::paint(PaintInfo& paintInfo, int parentX, int parentY)
+{
+    if (paintInfo.context->paintingDisabled())
+        return;
+
+    // This should only exist for <svg> renderers
+    if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 
+        paintBoxDecorations(paintInfo, m_x + parentX, m_y + parentY);
+
+    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE)
+        paintOutline(paintInfo.context, parentX, parentY, width(), height(), style());
+    
+    if (paintInfo.phase != PaintPhaseForeground || !drawsContents())
+        return;
+
+    const SVGRenderStyle* svgStyle = style()->svgStyle();
+    AtomicString filterId(SVGURIReference::getTarget(svgStyle->filter()));
+
+#if ENABLE(SVG_EXPERIMENTAL_FEATURES) 
+    SVGResourceFilter* filter = getFilterById(document(), filterId);
+#endif
+
+    if (!firstChild()
+#if ENABLE(SVG_EXPERIMENTAL_FEATURES) 
+        && !filter
+#endif
+        )
+        return; // Spec: groups w/o children still may render filter content.
+    
+    paintInfo.context->save();
+
+    if (!parent()->isSVGContainer()) {
+        // Translate from parent offsets (html renderers) to a relative transform (svg renderers)
+        IntPoint origin;
+        origin.move(parentX, parentY);
+        origin.move(m_x, m_y);
+        origin.move(borderLeft(), borderTop());
+        origin.move(paddingLeft(), paddingTop());
+        if (origin.x() || origin.y()) {
+            paintInfo.context->concatCTM(AffineTransform().translate(origin.x(), origin.y()));
+            paintInfo.rect.move(-origin.x(), -origin.y());
+        }
+        parentX = parentY = 0;
+        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
+        paintInfo.context->concatCTM(AffineTransform().scale(svg->currentScale()));
+    } else {
+        // Only the root <svg> element should need any translations using the HTML/CSS system
+        // parentX, parentY are also non-zero for first-level kids of these
+        // CSS-transformed <svg> root-elements (due to RenderBox::paint) for any other element
+        // they should be 0.   m_x, m_y should always be 0 for non-root svg containers
+        ASSERT(m_x == 0);
+        ASSERT(m_y == 0);
+    }
+
+    if (!viewport().isEmpty()) {
+        if (style()->overflowX() != OVISIBLE)
+            paintInfo.context->clip(enclosingIntRect(viewport())); // FIXME: Eventually we'll want float-precision clipping
+
+        paintInfo.context->concatCTM(AffineTransform().translate(viewport().x(), viewport().y()));
+    }
+    if (!localTransform().isIdentity())
+        paintInfo.context->concatCTM(localTransform());
+    if (!parent()->isSVGContainer()) {
+        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
+        paintInfo.context->concatCTM(AffineTransform().translate(svg->currentTranslate().x(), svg->currentTranslate().y()));
+    }
+
+    FloatRect strokeBBox = relativeBBox(true);
+
+    SVGElement* svgElement = static_cast<SVGElement*>(element());
+    ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
+
+    SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
+ 
+    AtomicString clipperId(SVGURIReference::getTarget(svgStyle->clipPath()));
+    AtomicString maskerId(SVGURIReference::getTarget(svgStyle->maskElement()));
+
+    SVGResourceClipper* clipper = getClipperById(document(), clipperId);
+    SVGResourceMasker* masker = getMaskerById(document(), maskerId);
+
+    if (clipper) {
+        clipper->addClient(styledElement);
+        clipper->applyClip(paintInfo.context, strokeBBox);
+    } else if (!clipperId.isEmpty())
+        svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement);
+
+    if (masker) {
+        masker->addClient(styledElement);
+        masker->applyMask(paintInfo.context, strokeBBox);
+    } else if (!maskerId.isEmpty())
+        svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement);
+
+    float opacity = style()->opacity();
+    if (opacity < 1.0f) {
+        paintInfo.context->clip(enclosingIntRect(strokeBBox));
+        paintInfo.context->beginTransparencyLayer(opacity);
+    }
+
+#if ENABLE(SVG_EXPERIMENTAL_FEATURES)
+    if (filter)
+        filter->prepareFilter(paintInfo.context, strokeBBox);
+    else if (!filterId.isEmpty())
+        svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement);
+#endif
+
+    if (!viewBox().isEmpty())
+        paintInfo.context->concatCTM(viewportTransform());
+
+    RenderContainer::paint(paintInfo, 0, 0);
+
+#if ENABLE(SVG_EXPERIMENTAL_FEATURES)
+    if (filter)
+        filter->applyFilter(paintInfo.context, strokeBBox);
+#endif
+
+    if (opacity < 1.0f)
+        paintInfo.context->endTransparencyLayer();
+
+    paintInfo.context->restore();
+}
+
+FloatRect RenderSVGContainer::viewport() const
+{
+    return m_viewport;
+}
+
+void RenderSVGContainer::calcViewport()
+{
+    SVGElement* svgelem = static_cast<SVGElement*>(element());
+    if (svgelem->hasTagName(SVGNames::svgTag)) {
+        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
+
+        if (!selfNeedsLayout() && !svg->hasRelativeValues())
+            return;
+
+        float x = 0.0f;
+        float y = 0.0f;
+        if (parent()->isSVGContainer()) {
+            x = svg->x().value();
+            y = svg->y().value();
+        }
+        float w = svg->width().value();
+        float h = svg->height().value();
+        m_viewport = FloatRect(x, y, w, h);
+    } else if (svgelem->hasTagName(SVGNames::markerTag)) {
+        if (!selfNeedsLayout())
+            return;
+
+        SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(element());
+        float w = svg->markerWidth().value();
+        float h = svg->markerHeight().value();
+        m_viewport = FloatRect(0.0f, 0.0f, w, h);
+    }
+}
+
+void RenderSVGContainer::setViewBox(const FloatRect& viewBox)
+{
+    m_viewBox = viewBox;
+
+    if (style())
+        setNeedsLayout(true);
+}
+
+FloatRect RenderSVGContainer::viewBox() const
+{
+    return m_viewBox;
+}
+
+void RenderSVGContainer::setAlign(KCAlign align)
+{
+    m_align = align;
+    if (style())
+        setNeedsLayout(true);
+}
+
+KCAlign RenderSVGContainer::align() const
+{
+    return m_align;
+}
+
+AffineTransform RenderSVGContainer::viewportTransform() const
+{
+    // FIXME: The method name is confusing, since it does not
+    // do viewport translating anymore. Look into this while
+    //  fixing bug 12207.
+    if (!viewBox().isEmpty()) {
+        FloatRect viewportRect = viewport();
+        if (!parent()->isSVGContainer())
+            viewportRect = FloatRect(viewport().x(), viewport().y(), width(), height());
+
+        return getAspectRatio(viewBox(), viewportRect);
+    }
+
+    return AffineTransform();
+}
+
+IntRect RenderSVGContainer::absoluteClippedOverflowRect()
+{
+    IntRect repaintRect;
+
+    for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling())
+        repaintRect.unite(current->absoluteClippedOverflowRect());
+
+#if ENABLE(SVG_EXPERIMENTAL_FEATURES)
+    // Filters can expand the bounding box
+    SVGResourceFilter* filter = getFilterById(document(), SVGURIReference::getTarget(style()->svgStyle()->filter()));
+    if (filter)
+        repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect)));
+#endif
+
+    return repaintRect;
+}
+
+void RenderSVGContainer::absoluteRects(Vector<IntRect>& rects, int, int, bool)
+{
+    rects.append(absoluteClippedOverflowRect());
+}
+
+AffineTransform RenderSVGContainer::absoluteTransform() const
+{
+    AffineTransform ctm = RenderContainer::absoluteTransform();
+    if (!parent()->isSVGContainer()) {
+        SVGSVGElement* svg = static_cast<SVGSVGElement*>(element());
+        ctm.scale(svg->currentScale());
+        ctm.translate(svg->currentTranslate().x(), svg->currentTranslate().y());
+    }
+    ctm.translate(viewport().x(), viewport().y());
+    return viewportTransform() * ctm;
+}
+
+bool RenderSVGContainer::fillContains(const FloatPoint& p) const
+{
+    RenderObject* current = firstChild();
+    while (current != 0) {
+        if (current->isRenderPath() && static_cast<RenderPath*>(current)->fillContains(p))
+            return true;
+
+        current = current->nextSibling();
+    }
+
+    return false;
+}
+
+bool RenderSVGContainer::strokeContains(const FloatPoint& p) const
+{
+    RenderObject* current = firstChild();
+    while (current != 0) {
+        if (current->isRenderPath() && static_cast<RenderPath*>(current)->strokeContains(p))
+            return true;
+
+        current = current->nextSibling();
+    }
+
+    return false;
+}
+
+FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const
+{
+    FloatRect rect;
+    
+    RenderObject* current = firstChild();
+    for (; current != 0; current = current->nextSibling()) {
+        FloatRect childBBox = current->relativeBBox(includeStroke);
+        FloatRect mappedBBox = current->localTransform().mapRect(childBBox);
+        rect.unite(mappedBBox);
+    }
+
+    return rect;
+}
+
+void RenderSVGContainer::setSlice(bool slice)
+{
+    m_slice = slice;
+
+    if (style())
+        setNeedsLayout(true);
+}
+
+bool RenderSVGContainer::slice() const
+{
+    return m_slice;
+}
+
+AffineTransform RenderSVGContainer::getAspectRatio(const FloatRect& logical, const FloatRect& physical) const
+{
+    AffineTransform temp;
+
+    float logicX = logical.x();
+    float logicY = logical.y();
+    float logicWidth = logical.width();
+    float logicHeight = logical.height();
+    float physWidth = physical.width();
+    float physHeight = physical.height();
+
+    float vpar = logicWidth / logicHeight;
+    float svgar = physWidth / physHeight;
+
+    if (align() == ALIGN_NONE) {
+        temp.scale(physWidth / logicWidth, physHeight / logicHeight);
+        temp.translate(-logicX, -logicY);
+    } else if ((vpar < svgar && !slice()) || (vpar >= svgar && slice())) {
+        temp.scale(physHeight / logicHeight, physHeight / logicHeight);
+
+        if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMINYMID || align() == ALIGN_XMINYMAX)
+            temp.translate(-logicX, -logicY);
+        else if (align() == ALIGN_XMIDYMIN || align() == ALIGN_XMIDYMID || align() == ALIGN_XMIDYMAX)
+            temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
+        else
+            temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
+    } else {
+        temp.scale(physWidth / logicWidth, physWidth / logicWidth);
+
+        if (align() == ALIGN_XMINYMIN || align() == ALIGN_XMIDYMIN || align() == ALIGN_XMAXYMIN)
+            temp.translate(-logicX, -logicY);
+        else if (align() == ALIGN_XMINYMID || align() == ALIGN_XMIDYMID || align() == ALIGN_XMAXYMID)
+            temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
+        else
+            temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
+    }
+
+    return temp;
+}
+
+bool RenderSVGContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
+{
+    if (!viewport().isEmpty()
+        && style()->overflowX() == OHIDDEN
+        && style()->overflowY() == OHIDDEN) {
+        int tx = _tx + m_x;
+        int ty = _ty + m_y;
+
+        // Check if we need to do anything at all.
+        IntRect overflowBox = overflowRect(false);
+        overflowBox.move(tx, ty);
+        AffineTransform ctm = RenderContainer::absoluteTransform();
+        ctm.translate(viewport().x(), viewport().y());
+        double localX, localY;
+        ctm.inverse().map(_x + _tx, _y + _ty, &localX, &localY);
+        if (!overflowBox.contains((int)localX, (int)localY))
+            return false;
+    }
+
+    for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+        if (child->nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction)) {
+            updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty));
+            return true;
+        }
+    }
+    
+    // Spec: Only graphical elements can be targeted by the mouse, period.
+    // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched."
+    return false;
+}
+
+}
+
+#endif // ENABLE(SVG)
+
+// vim:ts=4:noet