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/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"
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()
62 m_path
= adoptPtr(new Path());
63 *m_path
= toSVGGeometryElement(element())->asPath();
65 m_rareData
->m_cachedNonScalingStrokePath
.clear();
68 void LayoutSVGShape::updateShapeFromElement()
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);
91 bool LayoutSVGShape::shapeDependentStrokeContains(const FloatPoint
& point
)
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
))
117 if (requiresFill
&& !SVGPaintServer::existsForLayoutObject(*this, styleRef(), ApplyToFillMode
))
120 return shapeDependentFillContains(point
, fillRule
);
123 bool LayoutSVGShape::strokeContains(const FloatPoint
& point
, bool requiresStroke
)
125 if (requiresStroke
) {
126 if (!strokeBoundingBox().contains(point
))
129 if (!SVGPaintServer::existsForLayoutObject(*this, styleRef(), ApplyToStrokeMode
))
132 if (!hitTestStrokeBoundingBox().contains(point
))
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());
146 m_localTransform
= adoptPtr(new AffineTransform(graphicsElement
->calculateAnimatedLocalTransform()));
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();
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.
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
)
221 FloatPoint localPoint
;
222 if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent
, localPoint
))
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
))
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
)))
252 FloatRect
LayoutSVGShape::calculateObjectBoundingBox() const
254 return path().boundingRect();
257 FloatRect
LayoutSVGShape::calculateStrokeBoundingBox() const
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
);
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
305 m_rareData
= adoptPtr(new LayoutSVGShapeRareData());
306 return *m_rareData
.get();