summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/SVGRenderSupport.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/rendering/SVGRenderSupport.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/rendering/SVGRenderSupport.cpp')
-rw-r--r--Source/WebCore/rendering/SVGRenderSupport.cpp352
1 files changed, 352 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/SVGRenderSupport.cpp b/Source/WebCore/rendering/SVGRenderSupport.cpp
new file mode 100644
index 0000000..cbde49b
--- /dev/null
+++ b/Source/WebCore/rendering/SVGRenderSupport.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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 "RenderSVGPath.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "RenderSVGRoot.h"
+#include "SVGResources.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)
+{
+ const SVGRenderStyle* svgStyle = object->style()->svgStyle();
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(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);
+
+ RenderStyle* style = object->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ // Setup transparency layers before setting up SVG resources!
+ float opacity = style->opacity();
+ const ShadowData* shadow = svgStyle->shadow();
+ if (opacity < 1 || shadow) {
+ FloatRect repaintRect = object->repaintRectInLocalCoordinates();
+
+ if (opacity < 1) {
+ paintInfo.context->clip(repaintRect);
+ paintInfo.context->beginTransparencyLayer(opacity);
+ }
+
+ if (shadow) {
+ paintInfo.context->clip(repaintRect);
+ paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
+ paintInfo.context->beginTransparencyLayer(1);
+ }
+ }
+
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources)
+ return true;
+
+ if (RenderSVGResourceMasker* masker = resources->masker()) {
+ if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper()) {
+ if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+
+#if ENABLE(FILTERS)
+ if (RenderSVGResourceFilter* filter = resources->filter()) {
+ if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+#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)
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (resources) {
+ if (RenderSVGResourceFilter* filter = resources->filter()) {
+ filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode, /* path */0);
+ paintInfo.context = savedContext;
+ }
+ }
+#endif
+
+ if (style->opacity() < 1)
+ paintInfo.context->endTransparencyLayer();
+
+ if (svgStyle->shadow())
+ paintInfo.context->endTransparencyLayer();
+}
+
+void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
+{
+ for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
+ if (current->isSVGHiddenContainer())
+ continue;
+
+ const AffineTransform& transform = current->localToParentTransform();
+ if (transform.isIdentity()) {
+ objectBoundingBox.unite(current->objectBoundingBox());
+ strokeBoundingBox.unite(current->strokeBoundingBox());
+ repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
+ } else {
+ objectBoundingBox.unite(transform.mapRect(current->objectBoundingBox()));
+ strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
+ repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
+ }
+ }
+}
+
+bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
+{
+ if (localTransform.isIdentity())
+ return localRepaintRect.intersects(paintInfo.rect);
+
+ return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
+}
+
+const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
+{
+ while (start && !start->isSVGRoot())
+ start = start->parent();
+
+ ASSERT(start);
+ ASSERT(start->isSVGRoot());
+ return toRenderSVGRoot(start);
+}
+
+static inline void invalidateResourcesOfChildren(RenderObject* start)
+{
+ ASSERT(!start->needsLayout());
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
+ resources->removeClientFromCache(start, false);
+
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
+ invalidateResourcesOfChildren(child);
+}
+
+void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
+{
+ bool layoutSizeChanged = findTreeRootObject(start)->isLayoutSizeChanged();
+ HashSet<RenderObject*> notlayoutedObjects;
+
+ 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 RenderSVGPath to update its Path object
+ if (child->isSVGPath())
+ toRenderSVGPath(child)->setNeedsPathUpdate();
+
+ needsLayout = true;
+ }
+ }
+ }
+
+ if (needsLayout) {
+ child->setNeedsLayout(true, false);
+ child->layout();
+ } else {
+ if (child->needsLayout())
+ child->layout();
+ else if (layoutSizeChanged)
+ notlayoutedObjects.add(child);
+ }
+
+ ASSERT(!child->needsLayout());
+ }
+
+ if (!layoutSizeChanged) {
+ ASSERT(notlayoutedObjects.isEmpty());
+ return;
+ }
+
+ // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
+ HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
+ for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
+ invalidateResourcesOfChildren(*it);
+}
+
+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);
+
+ RenderStyle* style = object->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ RenderObject* renderer = const_cast<RenderObject*>(object);
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
+ if (!resources) {
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+ return;
+ }
+
+#if ENABLE(FILTERS)
+ if (RenderSVGResourceFilter* filter = resources->filter())
+ repaintRect = filter->resourceBoundingBox(renderer);
+#endif
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper())
+ repaintRect.intersect(clipper->resourceBoundingBox(renderer));
+
+ if (RenderSVGResourceMasker* masker = resources->masker())
+ repaintRect.intersect(masker->resourceBoundingBox(renderer));
+
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+}
+
+bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
+{
+ ASSERT(object);
+
+ // 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.
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources)
+ return true;
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper())
+ return clipper->hitTestClipContent(object->objectBoundingBox(), point);
+
+ return true;
+}
+
+void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
+{
+ ASSERT(context);
+ ASSERT(style);
+ ASSERT(object);
+ ASSERT(object->node());
+ ASSERT(object->node()->isSVGElement());
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ SVGElement* lengthContext = static_cast<SVGElement*>(object->node());
+ context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
+ context->setLineCap(svgStyle->capStyle());
+ context->setLineJoin(svgStyle->joinStyle());
+ if (svgStyle->joinStyle() == MiterJoin)
+ context->setMiterLimit(svgStyle->strokeMiterLimit());
+
+ const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
+ if (dashes.isEmpty())
+ context->setStrokeStyle(SolidStroke);
+ else {
+ DashArray dashArray;
+ const Vector<SVGLength>::const_iterator end = dashes.end();
+ for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
+ dashArray.append((*it).value(lengthContext));
+
+ context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
+ }
+}
+
+}
+
+#endif