Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / svg / LayoutSVGShape.cpp
blob5836bf0150084eedfa8b41495759da93eec1264c
1 /*
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.
28 #include "config.h"
29 #include "core/layout/svg/LayoutSVGShape.h"
31 #include "core/layout/HitTestResult.h"
32 #include "core/layout/LayoutAnalyzer.h"
33 #include "core/layout/PointerEventsHitRules.h"
34 #include "core/layout/svg/LayoutSVGResourcePaintServer.h"
35 #include "core/layout/svg/SVGLayoutSupport.h"
36 #include "core/layout/svg/SVGResources.h"
37 #include "core/layout/svg/SVGResourcesCache.h"
38 #include "core/paint/SVGShapePainter.h"
39 #include "core/svg/SVGGeometryElement.h"
40 #include "core/svg/SVGLengthContext.h"
41 #include "platform/geometry/FloatPoint.h"
42 #include "platform/graphics/StrokeData.h"
43 #include "wtf/MathExtras.h"
45 namespace blink {
47 LayoutSVGShape::LayoutSVGShape(SVGGeometryElement* node)
48 : LayoutSVGModelObject(node)
49 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
50 , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGeometryElement.
51 , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGeometryElement.
55 LayoutSVGShape::~LayoutSVGShape()
59 void LayoutSVGShape::createPath()
61 if (!m_path)
62 m_path = adoptPtr(new Path());
63 *m_path = toSVGGeometryElement(element())->asPath();
64 if (m_rareData.get())
65 m_rareData->m_cachedNonScalingStrokePath.clear();
68 void LayoutSVGShape::updateShapeFromElement()
70 createPath();
72 m_fillBoundingBox = calculateObjectBoundingBox();
73 m_strokeBoundingBox = calculateStrokeBoundingBox();
76 FloatRect LayoutSVGShape::hitTestStrokeBoundingBox() const
78 if (style()->svgStyle().hasStroke())
79 return m_strokeBoundingBox;
81 // Implementation of http://dev.w3.org/fxtf/css-masking-1/#compute-stroke-bounding-box
82 // for the <rect> / <ellipse> / <circle> case except that we ignore whether
83 // the stroke is none.
85 FloatRect box = m_fillBoundingBox;
86 const float strokeWidth = this->strokeWidth();
87 box.inflate(strokeWidth / 2);
88 return box;
91 bool LayoutSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
93 ASSERT(m_path);
94 StrokeData strokeData;
95 SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, styleRef(), *this);
97 if (hasNonScalingStroke()) {
98 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
99 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
101 return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
104 return m_path->strokeContains(point, strokeData);
107 bool LayoutSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
109 return path().contains(point, fillRule);
112 bool LayoutSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
114 if (!m_fillBoundingBox.contains(point))
115 return false;
117 if (requiresFill && !SVGPaintServer::existsForLayoutObject(*this, styleRef(), ApplyToFillMode))
118 return false;
120 return shapeDependentFillContains(point, fillRule);
123 bool LayoutSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
125 if (requiresStroke) {
126 if (!strokeBoundingBox().contains(point))
127 return false;
129 if (!SVGPaintServer::existsForLayoutObject(*this, styleRef(), ApplyToStrokeMode))
130 return false;
131 } else {
132 if (!hitTestStrokeBoundingBox().contains(point))
133 return false;
136 return shapeDependentStrokeContains(point);
139 void LayoutSVGShape::updateLocalTransform()
141 SVGGraphicsElement* graphicsElement = toSVGGraphicsElement(element());
142 if (graphicsElement->hasAnimatedLocalTransform()) {
143 if (m_localTransform)
144 m_localTransform->setTransform(graphicsElement->calculateAnimatedLocalTransform());
145 else
146 m_localTransform = adoptPtr(new AffineTransform(graphicsElement->calculateAnimatedLocalTransform()));
147 } else {
148 m_localTransform = 0;
152 void LayoutSVGShape::layout()
154 bool updateCachedBoundariesInParents = false;
155 LayoutAnalyzer::Scope analyzer(*this);
157 if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
158 updateShapeFromElement();
159 m_needsShapeUpdate = false;
160 updatePaintInvalidationBoundingBox();
161 m_needsBoundariesUpdate = false;
162 updateCachedBoundariesInParents = true;
165 if (m_needsTransformUpdate) {
166 updateLocalTransform();
167 m_needsTransformUpdate = false;
168 updateCachedBoundariesInParents = true;
171 // Invalidate all resources of this client if our layout changed.
172 if (everHadLayout() && selfNeedsLayout())
173 SVGResourcesCache::clientLayoutChanged(this);
175 // If our bounds changed, notify the parents.
176 if (updateCachedBoundariesInParents)
177 LayoutSVGModelObject::setNeedsBoundariesUpdate();
179 clearNeedsLayout();
182 Path* LayoutSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
184 LayoutSVGShapeRareData& rareData = ensureRareData();
185 if (!rareData.m_cachedNonScalingStrokePath.isEmpty() && strokeTransform == rareData.m_cachedNonScalingStrokeTransform)
186 return &rareData.m_cachedNonScalingStrokePath;
188 rareData.m_cachedNonScalingStrokePath = *path;
189 rareData.m_cachedNonScalingStrokePath.transform(strokeTransform);
190 rareData.m_cachedNonScalingStrokeTransform = strokeTransform;
191 return &rareData.m_cachedNonScalingStrokePath;
194 AffineTransform LayoutSVGShape::nonScalingStrokeTransform() const
196 AffineTransform t = toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
197 // Width of non-scaling stroke is independent of translation, so zero it out here.
198 t.setE(0);
199 t.setF(0);
200 return t;
203 void LayoutSVGShape::paint(const PaintInfo& paintInfo, const LayoutPoint&)
205 SVGShapePainter(*this).paint(paintInfo);
208 // This method is called from inside paintOutline() since we call paintOutline()
209 // while transformed to our coord system, return local coords
210 void LayoutSVGShape::addOutlineRects(Vector<LayoutRect>& rects, const LayoutPoint&, IncludeBlockVisualOverflowOrNot) const
212 rects.append(LayoutRect(paintInvalidationRectInLocalCoordinates()));
215 bool LayoutSVGShape::nodeAtFloatPoint(HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
217 // We only draw in the foreground phase, so we only hit-test then.
218 if (hitTestAction != HitTestForeground)
219 return false;
221 FloatPoint localPoint;
222 if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent, localPoint))
223 return false;
225 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, result.hitTestRequest(), style()->pointerEvents());
226 if (nodeAtFloatPointInternal(result.hitTestRequest(), localPoint, hitRules)) {
227 const LayoutPoint& localLayoutPoint = roundedLayoutPoint(localPoint);
228 updateHitTestResult(result, localLayoutPoint);
229 if (!result.addNodeToListBasedTestResult(element(), localLayoutPoint))
230 return true;
233 return false;
236 bool LayoutSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
238 bool isVisible = (style()->visibility() == VISIBLE);
239 if (isVisible || !hitRules.requireVisible) {
240 const SVGComputedStyle& svgStyle = style()->svgStyle();
241 WindRule fillRule = svgStyle.fillRule();
242 if (request.svgClipContent())
243 fillRule = svgStyle.clipRule();
244 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoint))
245 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
246 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule)))
247 return true;
249 return false;
252 FloatRect LayoutSVGShape::calculateObjectBoundingBox() const
254 return path().boundingRect();
257 FloatRect LayoutSVGShape::calculateStrokeBoundingBox() const
259 ASSERT(m_path);
260 FloatRect strokeBoundingBox = m_fillBoundingBox;
262 if (style()->svgStyle().hasStroke()) {
263 StrokeData strokeData;
264 SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, styleRef(), *this);
265 if (hasNonScalingStroke()) {
266 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
267 if (nonScalingTransform.isInvertible()) {
268 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
269 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
270 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
271 strokeBoundingBox.unite(strokeBoundingRect);
273 } else {
274 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
278 return strokeBoundingBox;
281 void LayoutSVGShape::updatePaintInvalidationBoundingBox()
283 m_paintInvalidationBoundingBox = strokeBoundingBox();
284 SVGLayoutSupport::intersectPaintInvalidationRectWithResources(this, m_paintInvalidationBoundingBox);
287 float LayoutSVGShape::strokeWidth() const
289 SVGLengthContext lengthContext(element());
290 return lengthContext.valueForLength(style()->svgStyle().strokeWidth());
293 LayoutRect LayoutSVGShape::clippedOverflowRectForPaintInvalidation(
294 const LayoutBoxModelObject* paintInvalidationContainer,
295 const PaintInvalidationState* paintInvalidationState) const
297 const float strokeWidthForHairlinePadding = style()->svgStyle().hasStroke() ? strokeWidth() : 0;
298 return SVGLayoutSupport::clippedOverflowRectForPaintInvalidation(*this,
299 paintInvalidationContainer, paintInvalidationState, strokeWidthForHairlinePadding);
302 LayoutSVGShapeRareData& LayoutSVGShape::ensureRareData() const
304 if (!m_rareData)
305 m_rareData = adoptPtr(new LayoutSVGShapeRareData());
306 return *m_rareData.get();