WebCore/rendering/SVGRenderSupport.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/SVGRenderSupport.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
+ *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ *           (C) 2007 Eric Seidel <eric@webkit.org>
+ *           (C) 2009 Google, Inc.  All rights reserved.
+ *           (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * 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
+ * along 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 "SVGRenderSupport.h"
+
+#include "FrameView.h"
+#include "ImageBuffer.h"
+#include "NodeRenderStyle.h"
+#include "RenderLayer.h"
+#include "RenderPath.h"
+#include "RenderSVGContainer.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "RenderSVGRoot.h"
+#include "SVGStyledElement.h"
+#include "TransformState.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+IntRect SVGRenderSupport::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer)
+{
+    // Return early for any cases where we don't actually paint
+    if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
+        return IntRect();
+
+    // Pass our local paint rect to computeRectForRepaint() which will
+    // map to parent coords and recurse up the parent chain.
+    IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates());
+    object->computeRectForRepaint(repaintContainer, repaintRect);
+    return repaintRect;
+}
+
+void SVGRenderSupport::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+    object->style()->svgStyle()->inflateForShadow(repaintRect);
+
+    // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent
+    repaintRect = object->localToParentTransform().mapRect(repaintRect);
+    object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed);
+}
+
+void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState)
+{
+    ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
+    ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless.
+    transformState.applyTransform(object->localToParentTransform());
+    object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
+}
+
+bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
+{
+    ASSERT(object);
+    SVGElement* svgElement = static_cast<SVGElement*>(object->node());
+    ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
+
+    SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
+    RenderStyle* style = object->style();
+    ASSERT(style);
+
+    const SVGRenderStyle* svgStyle = style->svgStyle();
+    ASSERT(svgStyle);
+
+    FloatRect repaintRect;
+
+    // Setup transparency layers before setting up SVG resources!
+    float opacity = style->opacity();
+    if (opacity < 1) {
+        repaintRect = object->repaintRectInLocalCoordinates();
+        paintInfo.context->clip(repaintRect);
+        paintInfo.context->beginTransparencyLayer(opacity);
+    }
+
+    if (const ShadowData* shadow = svgStyle->shadow()) {
+        // Eventually compute repaint rect, if not done so far.
+        if (opacity >= 1)
+            repaintRect = object->repaintRectInLocalCoordinates();
+
+        paintInfo.context->clip(repaintRect);
+        paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
+        paintInfo.context->beginTransparencyLayer(1);
+    }
+
+    Document* document = object->document();
+
+    if (svgStyle->hasMasker()) {
+        AtomicString maskerId(svgStyle->maskerResource());
+        if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(document, maskerId)) {
+            if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+                return false;
+        } else
+            document->accessSVGExtensions()->addPendingResource(maskerId, styledElement);
+    }
+
+    if (svgStyle->hasClipper()) {
+        AtomicString clipperId(svgStyle->clipperResource());
+        if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, clipperId))
+            clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode);
+        else
+            document->accessSVGExtensions()->addPendingResource(clipperId, styledElement);
+    }
+
+#if ENABLE(FILTERS)
+    if (svgStyle->hasFilter()) {
+        AtomicString filterId(svgStyle->filterResource());
+        if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(document, filterId)) { 
+            if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+                return false;
+        } else
+            document->accessSVGExtensions()->addPendingResource(filterId, styledElement);
+    }
+#endif
+
+    return true;
+}
+
+void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
+{
+#if !ENABLE(FILTERS)
+    UNUSED_PARAM(savedContext);
+#endif
+
+    ASSERT(object);
+
+    const RenderStyle* style = object->style();
+    ASSERT(style);
+
+    const SVGRenderStyle* svgStyle = style->svgStyle();
+    ASSERT(svgStyle);
+
+#if ENABLE(FILTERS)
+    if (svgStyle->hasFilter()) {
+        AtomicString filterId(svgStyle->filterResource());
+        if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object->document(), filterId)) { 
+            filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode);
+            paintInfo.context = savedContext;
+        }
+    }
+#endif
+
+    float opacity = style->opacity();    
+    if (opacity < 1)
+        paintInfo.context->endTransparencyLayer();
+
+    // This needs to be done separately from opacity, because if both properties are set,
+    // then the transparency layers are nested. 
+    if (svgStyle->shadow())
+        paintInfo.context->endTransparencyLayer();
+}
+
+void SVGRenderSupport::renderSubtreeToImage(ImageBuffer* image, RenderObject* item)
+{
+    ASSERT(item);
+    ASSERT(image);
+    ASSERT(image->context());
+
+    // FIXME: This sets the rect to the viewable area of the current frame. This
+    // is used to support text drawings to the ImageBuffer. See bug 30399.
+    IntRect rect;
+    FrameView* frameView = item->document()->view();
+    if (frameView)
+        rect = IntRect(0, 0, frameView->visibleWidth(), frameView->visibleHeight());
+    PaintInfo info(image->context(), rect, PaintPhaseForeground, 0, 0, 0);
+
+    // FIXME: isSVGContainer returns true for RenderSVGViewportContainer, so if this is ever
+    // called with one of those, we will read from the wrong offset in an object due to a bad cast.
+    RenderSVGContainer* svgContainer = 0;
+    if (item && item->isSVGContainer())
+        svgContainer = toRenderSVGContainer(item);
+
+    bool drawsContents = svgContainer ? svgContainer->drawsContents() : false;
+    if (svgContainer && !drawsContents)
+        svgContainer->setDrawsContents(true);
+
+    item->layoutIfNeeded();
+    item->paint(info, 0, 0);
+
+    if (svgContainer && !drawsContents)
+        svgContainer->setDrawsContents(false);
+}
+
+FloatRect SVGRenderSupport::computeContainerBoundingBox(const RenderObject* container, ContainerBoundingBoxMode mode)
+{
+    FloatRect boundingBox;
+
+    for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
+        FloatRect childBoundingBox;
+
+        switch (mode) {
+        case ObjectBoundingBox:
+            childBoundingBox = current->objectBoundingBox();
+            break;
+        case StrokeBoundingBox:
+            childBoundingBox = current->strokeBoundingBox();
+            break;
+        case RepaintBoundingBox:
+            childBoundingBox = current->repaintRectInLocalCoordinates();
+            break;
+        }
+
+        boundingBox.unite(current->localToParentTransform().mapRect(childBoundingBox));
+    }
+
+    return boundingBox;
+}
+
+static inline RenderSVGRoot* svgRootTreeObject(RenderObject* start)
+{
+    while (start && !start->isSVGRoot())
+        start = start->parent();
+
+    ASSERT(start);
+    ASSERT(start->isSVGRoot());
+    return toRenderSVGRoot(start);
+}
+
+void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
+{
+    bool layoutSizeChanged = svgRootTreeObject(start)->isLayoutSizeChanged();
+
+    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+        bool needsLayout = selfNeedsLayout;
+
+        if (layoutSizeChanged) {
+            // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
+            if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
+                if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
+                    // When the layout size changed and when using relative values tell the RenderPath to update its Path object
+                    if (child->isRenderPath())
+                        toRenderPath(child)->setNeedsPathUpdate();
+
+                    needsLayout = true;
+                }
+            }
+        }
+
+        if (needsLayout) {
+            child->setNeedsLayout(true, false);
+            child->layout();
+        } else
+            child->layoutIfNeeded();
+
+        ASSERT(!child->needsLayout());
+    }
+}
+
+bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
+{
+    // SVG doesn't support independent x/y overflow
+    ASSERT(object->style()->overflowX() == object->style()->overflowY());
+
+    // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
+    ASSERT(object->style()->overflowX() != OSCROLL);
+
+    // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
+    ASSERT(!object->isRoot());
+
+    return object->style()->overflowX() == OHIDDEN;
+}
+
+void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
+{
+    ASSERT(object);
+    ASSERT(object->style());
+    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
+    if (!svgStyle)
+        return;
+        
+    RenderObject* renderer = const_cast<RenderObject*>(object);
+#if ENABLE(FILTERS)
+    if (svgStyle->hasFilter()) {
+        if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object->document(), svgStyle->filterResource()))
+            repaintRect = filter->resourceBoundingBox(renderer);
+    }
+#endif
+
+    if (svgStyle->hasClipper()) {
+        if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object->document(), svgStyle->clipperResource()))
+            repaintRect.intersect(clipper->resourceBoundingBox(renderer));
+    }
+    
+    if (svgStyle->hasMasker()) {
+        if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object->document(), svgStyle->maskerResource()))
+            repaintRect.intersect(masker->resourceBoundingBox(renderer));
+    }
+    
+    svgStyle->inflateForShadow(repaintRect);
+}
+
+bool SVGRenderSupport::pointInClippingArea(const RenderObject* object, const FloatPoint& point)
+{
+    ASSERT(object);
+    ASSERT(object->style());
+
+    Document* document = object->document();
+    ASSERT(document);
+
+    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
+    ASSERT(svgStyle);
+
+    // We just take clippers into account to determine if a point is on the node. The Specification may
+    // change later and we also need to check maskers.
+    if (svgStyle->hasClipper()) {
+        if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, svgStyle->clipperResource()))
+            return clipper->hitTestClipContent(object->objectBoundingBox(), point);
+    }
+
+    return true;
+}
+
+DashArray SVGRenderSupport::dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle)
+{
+    DashArray array;
+    
+    CSSValueList* dashes = style->svgStyle()->strokeDashArray();
+    if (!dashes)
+        return array;
+
+    CSSPrimitiveValue* dash = 0;
+    unsigned long len = dashes->length();
+    for (unsigned long i = 0; i < len; ++i) {
+        dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i));
+        if (!dash)
+            continue;
+
+        array.append(dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle));
+    }
+
+    return array;
+}
+
+void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
+{
+    context->setStrokeThickness(SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeWidth(), 1.0f));
+    context->setLineCap(style->svgStyle()->capStyle());
+    context->setLineJoin(style->svgStyle()->joinStyle());
+    if (style->svgStyle()->joinStyle() == MiterJoin)
+        context->setMiterLimit(style->svgStyle()->strokeMiterLimit());
+
+    const DashArray& dashes = dashArrayFromRenderingStyle(object->style(), object->document()->documentElement()->renderStyle());
+    float dashOffset = SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeDashOffset(), 0.0f);
+    if (dashes.isEmpty())
+        context->setStrokeStyle(SolidStroke);
+    else
+        context->setLineDash(dashes, dashOffset);
+}
+
+const RenderObject* SVGRenderSupport::findTextRootObject(const RenderObject* start)
+{
+    while (start && !start->isSVGText())
+        start = start->parent();
+    ASSERT(start);
+    ASSERT(start->isSVGText());
+
+    return start;
+}
+
+}
+
+#endif