OLD | NEW |
| (Empty) |
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 | |
11 * | |
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. | |
16 * | |
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. | |
21 * | |
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. | |
26 */ | |
27 | |
28 #include "config.h" | |
29 #include "core/rendering/svg/RenderSVGShape.h" | |
30 | |
31 #include "core/layout/HitTestRequest.h" | |
32 #include "core/layout/PointerEventsHitRules.h" | |
33 #include "core/layout/svg/SVGLayoutSupport.h" | |
34 #include "core/layout/svg/SVGPathData.h" | |
35 #include "core/layout/svg/SVGResources.h" | |
36 #include "core/layout/svg/SVGResourcesCache.h" | |
37 #include "core/paint/SVGShapePainter.h" | |
38 #include "core/svg/SVGGraphicsElement.h" | |
39 #include "platform/geometry/FloatPoint.h" | |
40 #include "platform/graphics/StrokeData.h" | |
41 #include "wtf/MathExtras.h" | |
42 | |
43 namespace blink { | |
44 | |
45 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node) | |
46 : RenderSVGModelObject(node) | |
47 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are e
mpty 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 AffineTransf
orm object once from SVGGraphicsElement. | |
50 { | |
51 } | |
52 | |
53 RenderSVGShape::~RenderSVGShape() | |
54 { | |
55 } | |
56 | |
57 void RenderSVGShape::createPath() | |
58 { | |
59 clearPath(); | |
60 m_path = adoptPtr(new Path); | |
61 ASSERT(RenderSVGShape::isShapeEmpty()); | |
62 | |
63 updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path()); | |
64 processMarkerPositions(); | |
65 } | |
66 | |
67 void RenderSVGShape::updateShapeFromElement() | |
68 { | |
69 createPath(); | |
70 | |
71 m_fillBoundingBox = calculateObjectBoundingBox(); | |
72 m_strokeBoundingBox = calculateStrokeBoundingBox(); | |
73 } | |
74 | |
75 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) | |
76 { | |
77 ASSERT(m_path); | |
78 StrokeData strokeData; | |
79 SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, styleRef(), *this
); | |
80 | |
81 if (hasNonScalingStroke()) { | |
82 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); | |
83 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); | |
84 | |
85 return usePath->strokeContains(nonScalingTransform.mapPoint(point), stro
keData); | |
86 } | |
87 | |
88 return m_path->strokeContains(point, strokeData); | |
89 } | |
90 | |
91 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const W
indRule fillRule) const | |
92 { | |
93 return path().contains(point, fillRule); | |
94 } | |
95 | |
96 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, co
nst WindRule fillRule) | |
97 { | |
98 if (!m_fillBoundingBox.contains(point)) | |
99 return false; | |
100 | |
101 if (requiresFill && !SVGPaintServer::existsForRenderer(*this, styleRef(), Ap
plyToFillMode)) | |
102 return false; | |
103 | |
104 return shapeDependentFillContains(point, fillRule); | |
105 } | |
106 | |
107 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke
) | |
108 { | |
109 if (!strokeBoundingBox().contains(point)) | |
110 return false; | |
111 | |
112 if (requiresStroke && !SVGPaintServer::existsForRenderer(*this, styleRef(),
ApplyToStrokeMode)) | |
113 return false; | |
114 | |
115 return shapeDependentStrokeContains(point); | |
116 } | |
117 | |
118 void RenderSVGShape::updateLocalTransform() | |
119 { | |
120 SVGGraphicsElement* graphicsElement = toSVGGraphicsElement(element()); | |
121 if (graphicsElement->hasAnimatedLocalTransform()) { | |
122 if (m_localTransform) | |
123 m_localTransform->setTransform(graphicsElement->calculateAnimatedLoc
alTransform()); | |
124 else | |
125 m_localTransform = adoptPtr(new AffineTransform(graphicsElement->cal
culateAnimatedLocalTransform())); | |
126 } else { | |
127 m_localTransform = 0; | |
128 } | |
129 } | |
130 | |
131 void RenderSVGShape::layout() | |
132 { | |
133 bool updateCachedBoundariesInParents = false; | |
134 | |
135 if (m_needsShapeUpdate || m_needsBoundariesUpdate) { | |
136 updateShapeFromElement(); | |
137 m_needsShapeUpdate = false; | |
138 updatePaintInvalidationBoundingBox(); | |
139 m_needsBoundariesUpdate = false; | |
140 updateCachedBoundariesInParents = true; | |
141 } | |
142 | |
143 if (m_needsTransformUpdate) { | |
144 updateLocalTransform(); | |
145 m_needsTransformUpdate = false; | |
146 updateCachedBoundariesInParents = true; | |
147 } | |
148 | |
149 // Invalidate all resources of this client if our layout changed. | |
150 if (everHadLayout() && selfNeedsLayout()) | |
151 SVGResourcesCache::clientLayoutChanged(this); | |
152 | |
153 // If our bounds changed, notify the parents. | |
154 if (updateCachedBoundariesInParents) | |
155 RenderSVGModelObject::setNeedsBoundariesUpdate(); | |
156 | |
157 clearNeedsLayout(); | |
158 } | |
159 | |
160 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransfo
rm& strokeTransform) const | |
161 { | |
162 DEFINE_STATIC_LOCAL(Path, tempPath, ()); | |
163 | |
164 tempPath = *path; | |
165 tempPath.transform(strokeTransform); | |
166 | |
167 return &tempPath; | |
168 } | |
169 | |
170 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const | |
171 { | |
172 return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::Dis
allowStyleUpdate); | |
173 } | |
174 | |
175 void RenderSVGShape::paint(const PaintInfo& paintInfo, const LayoutPoint&) | |
176 { | |
177 SVGShapePainter(*this).paint(paintInfo); | |
178 } | |
179 | |
180 // This method is called from inside paintOutline() since we call paintOutline() | |
181 // while transformed to our coord system, return local coords | |
182 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPo
int&) const | |
183 { | |
184 LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates()); | |
185 if (!rect.isEmpty()) | |
186 rects.append(rect); | |
187 } | |
188 | |
189 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResu
lt& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) | |
190 { | |
191 // We only draw in the foreground phase, so we only hit-test then. | |
192 if (hitTestAction != HitTestForeground) | |
193 return false; | |
194 | |
195 FloatPoint localPoint; | |
196 if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(this, localToPar
entTransform(), pointInParent, localPoint)) | |
197 return false; | |
198 | |
199 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTIN
G, request, style()->pointerEvents()); | |
200 if (nodeAtFloatPointInternal(request, localPoint, hitRules)) { | |
201 updateHitTestResult(result, roundedLayoutPoint(localPoint)); | |
202 return true; | |
203 } | |
204 | |
205 return false; | |
206 } | |
207 | |
208 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, con
st FloatPoint& localPoint, PointerEventsHitRules hitRules) | |
209 { | |
210 bool isVisible = (style()->visibility() == VISIBLE); | |
211 if (isVisible || !hitRules.requireVisible) { | |
212 const SVGLayoutStyle& svgStyle = style()->svgStyle(); | |
213 WindRule fillRule = svgStyle.fillRule(); | |
214 if (request.svgClipContent()) | |
215 fillRule = svgStyle.clipRule(); | |
216 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoi
nt)) | |
217 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requ
ireStroke) && strokeContains(localPoint, hitRules.requireStroke)) | |
218 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireF
ill) && fillContains(localPoint, hitRules.requireFill, fillRule))) | |
219 return true; | |
220 } | |
221 return false; | |
222 } | |
223 | |
224 FloatRect RenderSVGShape::calculateObjectBoundingBox() const | |
225 { | |
226 return path().boundingRect(); | |
227 } | |
228 | |
229 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const | |
230 { | |
231 ASSERT(m_path); | |
232 FloatRect strokeBoundingBox = m_fillBoundingBox; | |
233 | |
234 if (style()->svgStyle().hasStroke()) { | |
235 StrokeData strokeData; | |
236 SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, styleRef(), *
this); | |
237 if (hasNonScalingStroke()) { | |
238 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); | |
239 if (nonScalingTransform.isInvertible()) { | |
240 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTra
nsform); | |
241 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strok
eData); | |
242 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strok
eBoundingRect); | |
243 strokeBoundingBox.unite(strokeBoundingRect); | |
244 } | |
245 } else { | |
246 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData)); | |
247 } | |
248 } | |
249 | |
250 return strokeBoundingBox; | |
251 } | |
252 | |
253 void RenderSVGShape::updatePaintInvalidationBoundingBox() | |
254 { | |
255 m_paintInvalidationBoundingBox = strokeBoundingBox(); | |
256 if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty()) | |
257 m_paintInvalidationBoundingBox.inflate(1); | |
258 SVGLayoutSupport::intersectPaintInvalidationRectWithResources(this, m_paintI
nvalidationBoundingBox); | |
259 } | |
260 | |
261 float RenderSVGShape::strokeWidth() const | |
262 { | |
263 SVGLengthContext lengthContext(element()); | |
264 return style()->svgStyle().strokeWidth()->value(lengthContext); | |
265 } | |
266 | |
267 } | |
OLD | NEW |