2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org>
4 * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Google, Inc.
6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8 * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com>
9 * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
10 * Copyright (C) 2011 University of Szeged
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
29 #include "core/rendering/svg/RenderSVGShape.h"
31 #include "core/paint/SVGShapePainter.h"
32 #include "core/rendering/HitTestRequest.h"
33 #include "core/rendering/PointerEventsHitRules.h"
34 #include "core/rendering/svg/SVGPathData.h"
35 #include "core/rendering/svg/SVGRenderSupport.h"
36 #include "core/rendering/svg/SVGResources.h"
37 #include "core/rendering/svg/SVGResourcesCache.h"
38 #include "core/svg/SVGGraphicsElement.h"
39 #include "platform/geometry/FloatPoint.h"
40 #include "platform/graphics/StrokeData.h"
41 #include "wtf/MathExtras.h"
45 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
46 : RenderSVGModelObject(node)
47 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
48 , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
49 , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
53 RenderSVGShape::~RenderSVGShape()
57 void RenderSVGShape::updateShapeFromElement()
60 m_path = adoptPtr(new Path);
61 ASSERT(RenderSVGShape::isShapeEmpty());
63 updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
64 processMarkerPositions();
66 m_fillBoundingBox = calculateObjectBoundingBox();
67 m_strokeBoundingBox = calculateStrokeBoundingBox();
70 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
73 StrokeData strokeData;
74 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
76 if (hasNonScalingStroke()) {
77 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
78 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
80 return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
83 return m_path->strokeContains(point, strokeData);
86 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
88 return path().contains(point, fillRule);
91 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
93 if (!m_fillBoundingBox.contains(point))
96 if (requiresFill && !SVGPaintServer::existsForRenderer(*this, style(), ApplyToFillMode))
99 return shapeDependentFillContains(point, fillRule);
102 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
104 if (!strokeBoundingBox().contains(point))
107 if (requiresStroke && !SVGPaintServer::existsForRenderer(*this, style(), ApplyToStrokeMode))
110 return shapeDependentStrokeContains(point);
113 void RenderSVGShape::layout()
115 bool updateCachedBoundariesInParents = false;
117 if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
118 updateShapeFromElement();
119 m_needsShapeUpdate = false;
120 updatePaintInvalidationBoundingBox();
121 m_needsBoundariesUpdate = false;
122 updateCachedBoundariesInParents = true;
125 if (m_needsTransformUpdate) {
126 m_localTransform = toSVGGraphicsElement(element())->calculateAnimatedLocalTransform();
127 m_needsTransformUpdate = false;
128 updateCachedBoundariesInParents = true;
131 // Invalidate all resources of this client if our layout changed.
132 if (everHadLayout() && selfNeedsLayout())
133 SVGResourcesCache::clientLayoutChanged(this);
135 // If our bounds changed, notify the parents.
136 if (updateCachedBoundariesInParents)
137 RenderSVGModelObject::setNeedsBoundariesUpdate();
142 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
144 DEFINE_STATIC_LOCAL(Path, tempPath, ());
147 tempPath.transform(strokeTransform);
152 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
154 return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
157 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
159 SVGShapePainter(*this).paint(paintInfo);
162 // This method is called from inside paintOutline() since we call paintOutline()
163 // while transformed to our coord system, return local coords
164 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) const
166 LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates());
171 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
173 // We only draw in the foreground phase, so we only hit-test then.
174 if (hitTestAction != HitTestForeground)
177 FloatPoint localPoint;
178 if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, m_localTransform, pointInParent, localPoint))
181 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, style()->pointerEvents());
182 if (nodeAtFloatPointInternal(request, localPoint, hitRules)) {
183 updateHitTestResult(result, roundedLayoutPoint(localPoint));
190 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
192 bool isVisible = (style()->visibility() == VISIBLE);
193 if (isVisible || !hitRules.requireVisible) {
194 const SVGRenderStyle& svgStyle = style()->svgStyle();
195 WindRule fillRule = svgStyle.fillRule();
196 if (request.svgClipContent())
197 fillRule = svgStyle.clipRule();
198 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoint))
199 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
200 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule)))
206 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
208 return path().boundingRect();
211 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
214 FloatRect strokeBoundingBox = m_fillBoundingBox;
216 if (style()->svgStyle().hasStroke()) {
217 StrokeData strokeData;
218 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
219 if (hasNonScalingStroke()) {
220 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
221 if (nonScalingTransform.isInvertible()) {
222 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
223 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
224 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
225 strokeBoundingBox.unite(strokeBoundingRect);
228 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
232 return strokeBoundingBox;
235 void RenderSVGShape::updatePaintInvalidationBoundingBox()
237 m_paintInvalidationBoundingBox = strokeBoundingBox();
238 if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty())
239 m_paintInvalidationBoundingBox.inflate(1);
240 SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintInvalidationBoundingBox);
243 float RenderSVGShape::strokeWidth() const
245 SVGLengthContext lengthContext(element());
246 return style()->svgStyle().strokeWidth()->value(lengthContext);
249 bool RenderSVGShape::hasSmoothStroke() const
251 const SVGRenderStyle& svgStyle = style()->svgStyle();
252 return svgStyle.strokeDashArray()->isEmpty()
253 && svgStyle.strokeMiterLimit() == SVGRenderStyle::initialStrokeMiterLimit()
254 && svgStyle.joinStyle() == SVGRenderStyle::initialJoinStyle()
255 && svgStyle.capStyle() == SVGRenderStyle::initialCapStyle();