OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> |
3 * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org> |
4 * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org> | 4 * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org> |
5 * Copyright (C) 2009 Google, Inc. | 5 * Copyright (C) 2009 Google, Inc. |
6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> | 6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> |
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
8 * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com> | 8 * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com> |
9 * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> | 9 * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> |
10 * Copyright (C) 2011 University of Szeged | 10 * Copyright (C) 2011 University of Szeged |
(...skipping 10 matching lines...) Expand all Loading... |
21 * | 21 * |
22 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
25 * Boston, MA 02110-1301, USA. | 25 * Boston, MA 02110-1301, USA. |
26 */ | 26 */ |
27 | 27 |
28 #include "config.h" | 28 #include "config.h" |
29 #include "core/rendering/svg/RenderSVGShape.h" | 29 #include "core/rendering/svg/RenderSVGShape.h" |
30 | 30 |
31 #include "core/paint/SVGMarkerPainter.h" | 31 #include "core/paint/SVGShapePainter.h" |
32 #include "core/rendering/GraphicsContextAnnotator.h" | |
33 #include "core/rendering/HitTestRequest.h" | 32 #include "core/rendering/HitTestRequest.h" |
34 #include "core/rendering/PointerEventsHitRules.h" | 33 #include "core/rendering/PointerEventsHitRules.h" |
35 #include "core/rendering/svg/RenderSVGResourceMarker.h" | |
36 #include "core/rendering/svg/SVGPathData.h" | 34 #include "core/rendering/svg/SVGPathData.h" |
37 #include "core/rendering/svg/SVGRenderSupport.h" | 35 #include "core/rendering/svg/SVGRenderSupport.h" |
38 #include "core/rendering/svg/SVGRenderingContext.h" | 36 #include "core/rendering/svg/SVGRenderingContext.h" |
39 #include "core/rendering/svg/SVGResources.h" | 37 #include "core/rendering/svg/SVGResources.h" |
40 #include "core/rendering/svg/SVGResourcesCache.h" | 38 #include "core/rendering/svg/SVGResourcesCache.h" |
41 #include "core/svg/SVGGraphicsElement.h" | 39 #include "core/svg/SVGGraphicsElement.h" |
42 #include "platform/geometry/FloatPoint.h" | 40 #include "platform/geometry/FloatPoint.h" |
43 #include "platform/graphics/GraphicsContextStateSaver.h" | |
44 #include "wtf/MathExtras.h" | 41 #include "wtf/MathExtras.h" |
45 | 42 |
46 namespace blink { | 43 namespace blink { |
47 | 44 |
48 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node) | 45 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node) |
49 : RenderSVGModelObject(node) | 46 : RenderSVGModelObject(node) |
50 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are e
mpty from the beginning. | 47 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are e
mpty from the beginning. |
51 , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once
from SVGGraphicsElement. | 48 , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once
from SVGGraphicsElement. |
52 , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransf
orm object once from SVGGraphicsElement. | 49 , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransf
orm object once from SVGGraphicsElement. |
53 { | 50 { |
54 } | 51 } |
55 | 52 |
56 RenderSVGShape::~RenderSVGShape() | 53 RenderSVGShape::~RenderSVGShape() |
57 { | 54 { |
58 } | 55 } |
59 | 56 |
60 void RenderSVGShape::updateShapeFromElement() | 57 void RenderSVGShape::updateShapeFromElement() |
61 { | 58 { |
62 m_path.clear(); | 59 m_path.clear(); |
63 m_path = adoptPtr(new Path); | 60 m_path = adoptPtr(new Path); |
64 ASSERT(RenderSVGShape::isShapeEmpty()); | 61 ASSERT(RenderSVGShape::isShapeEmpty()); |
65 | 62 |
66 updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path()); | 63 updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path()); |
67 processMarkerPositions(); | 64 processMarkerPositions(); |
68 | 65 |
69 m_fillBoundingBox = calculateObjectBoundingBox(); | 66 m_fillBoundingBox = calculateObjectBoundingBox(); |
70 m_strokeBoundingBox = calculateStrokeBoundingBox(); | 67 m_strokeBoundingBox = calculateStrokeBoundingBox(); |
71 } | 68 } |
72 | 69 |
73 void RenderSVGShape::fillShape(GraphicsContext* context) const | |
74 { | |
75 context->fillPath(path()); | |
76 } | |
77 | |
78 void RenderSVGShape::strokeShape(GraphicsContext* context) const | |
79 { | |
80 ASSERT(m_path); | |
81 Path* usePath = m_path.get(); | |
82 | |
83 if (hasNonScalingStroke()) | |
84 usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform()); | |
85 | |
86 context->strokePath(*usePath); | |
87 } | |
88 | |
89 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) | 70 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) |
90 { | 71 { |
91 ASSERT(m_path); | 72 ASSERT(m_path); |
92 StrokeData strokeData; | 73 StrokeData strokeData; |
93 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this); | 74 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this); |
94 | 75 |
95 if (hasNonScalingStroke()) { | 76 if (hasNonScalingStroke()) { |
96 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); | 77 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); |
97 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); | 78 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); |
98 | 79 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransfo
rm& strokeTransform) const | 142 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransfo
rm& strokeTransform) const |
162 { | 143 { |
163 DEFINE_STATIC_LOCAL(Path, tempPath, ()); | 144 DEFINE_STATIC_LOCAL(Path, tempPath, ()); |
164 | 145 |
165 tempPath = *path; | 146 tempPath = *path; |
166 tempPath.transform(strokeTransform); | 147 tempPath.transform(strokeTransform); |
167 | 148 |
168 return &tempPath; | 149 return &tempPath; |
169 } | 150 } |
170 | 151 |
171 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransfo
rm, GraphicsContextStateSaver& stateSaver) | |
172 { | |
173 if (!strokeTransform.isInvertible()) | |
174 return false; | |
175 | |
176 stateSaver.save(); | |
177 stateSaver.context()->concatCTM(strokeTransform.inverse()); | |
178 return true; | |
179 } | |
180 | |
181 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const | 152 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const |
182 { | 153 { |
183 return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::Dis
allowStyleUpdate); | 154 return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::Dis
allowStyleUpdate); |
184 } | 155 } |
185 | 156 |
186 bool RenderSVGShape::shouldGenerateMarkerPositions() const | |
187 { | |
188 if (!style()->svgStyle().hasMarkers()) | |
189 return false; | |
190 | |
191 if (!SVGResources::supportsMarkers(*toSVGGraphicsElement(element()))) | |
192 return false; | |
193 | |
194 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(
this); | |
195 if (!resources) | |
196 return false; | |
197 | |
198 return resources->markerStart() || resources->markerMid() || resources->mark
erEnd(); | |
199 } | |
200 | |
201 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&) | 157 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&) |
202 { | 158 { |
203 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); | 159 SVGShapePainter(*this).paint(paintInfo); |
204 if (paintInfo.phase != PaintPhaseForeground | |
205 || style()->visibility() == HIDDEN | |
206 || isShapeEmpty()) | |
207 return; | |
208 | |
209 FloatRect boundingBox = paintInvalidationRectInLocalCoordinates(); | |
210 if (!SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(boundingBox,
m_localTransform, paintInfo)) | |
211 return; | |
212 | |
213 PaintInfo childPaintInfo(paintInfo); | |
214 | |
215 GraphicsContextStateSaver stateSaver(*childPaintInfo.context); | |
216 childPaintInfo.applyTransform(m_localTransform); | |
217 | |
218 SVGRenderingContext renderingContext(this, childPaintInfo); | |
219 | |
220 if (renderingContext.isRenderingPrepared()) { | |
221 const SVGRenderStyle& svgStyle = style()->svgStyle(); | |
222 if (svgStyle.shapeRendering() == SR_CRISPEDGES) | |
223 childPaintInfo.context->setShouldAntialias(false); | |
224 | |
225 for (int i = 0; i < 3; i++) { | |
226 switch (svgStyle.paintOrderType(i)) { | |
227 case PT_FILL: { | |
228 GraphicsContextStateSaver stateSaver(*childPaintInfo.context, fa
lse); | |
229 if (!SVGRenderSupport::updateGraphicsContext(stateSaver, style()
, *this, ApplyToFillMode)) | |
230 break; | |
231 fillShape(childPaintInfo.context); | |
232 break; | |
233 } | |
234 case PT_STROKE: | |
235 if (svgStyle.hasVisibleStroke()) { | |
236 GraphicsContextStateSaver stateSaver(*childPaintInfo.context
, false); | |
237 AffineTransform nonScalingTransform; | |
238 const AffineTransform* additionalPaintServerTransform = 0; | |
239 | |
240 if (hasNonScalingStroke()) { | |
241 nonScalingTransform = nonScalingStrokeTransform(); | |
242 if (!setupNonScalingStrokeContext(nonScalingTransform, s
tateSaver)) | |
243 return; | |
244 | |
245 // Non-scaling stroke needs to reset the transform back
to the host transform. | |
246 additionalPaintServerTransform = &nonScalingTransform; | |
247 } | |
248 | |
249 if (!SVGRenderSupport::updateGraphicsContext(stateSaver, sty
le(), *this, ApplyToStrokeMode, additionalPaintServerTransform)) | |
250 break; | |
251 strokeShape(childPaintInfo.context); | |
252 } | |
253 break; | |
254 case PT_MARKERS: | |
255 if (!m_markerPositions.isEmpty()) | |
256 paintMarkers(childPaintInfo); | |
257 break; | |
258 default: | |
259 ASSERT_NOT_REACHED(); | |
260 break; | |
261 } | |
262 } | |
263 } | |
264 | |
265 if (style()->outlineWidth()) | |
266 paintOutline(childPaintInfo, IntRect(boundingBox)); | |
267 } | 160 } |
268 | 161 |
269 // This method is called from inside paintOutline() since we call paintOutline() | 162 // This method is called from inside paintOutline() since we call paintOutline() |
270 // while transformed to our coord system, return local coords | 163 // while transformed to our coord system, return local coords |
271 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPo
int&, const RenderLayerModelObject*) const | 164 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPo
int&, const RenderLayerModelObject*) const |
272 { | 165 { |
273 LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates()); | 166 LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates()); |
274 if (!rect.isEmpty()) | 167 if (!rect.isEmpty()) |
275 rects.append(rect); | 168 rects.append(rect); |
276 } | 169 } |
(...skipping 26 matching lines...) Expand all Loading... |
303 if (request.svgClipContent()) | 196 if (request.svgClipContent()) |
304 fillRule = svgStyle.clipRule(); | 197 fillRule = svgStyle.clipRule(); |
305 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoi
nt)) | 198 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoi
nt)) |
306 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requ
ireStroke) && strokeContains(localPoint, hitRules.requireStroke)) | 199 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requ
ireStroke) && strokeContains(localPoint, hitRules.requireStroke)) |
307 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireF
ill) && fillContains(localPoint, hitRules.requireFill, fillRule))) | 200 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireF
ill) && fillContains(localPoint, hitRules.requireFill, fillRule))) |
308 return true; | 201 return true; |
309 } | 202 } |
310 return false; | 203 return false; |
311 } | 204 } |
312 | 205 |
313 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderS
VGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGReso
urceMarker* markerEnd) | |
314 { | |
315 switch (type) { | |
316 case StartMarker: | |
317 return markerStart; | |
318 case MidMarker: | |
319 return markerMid; | |
320 case EndMarker: | |
321 return markerEnd; | |
322 } | |
323 | |
324 ASSERT_NOT_REACHED(); | |
325 return 0; | |
326 } | |
327 | |
328 FloatRect RenderSVGShape::markerRect(float strokeWidth) const | |
329 { | |
330 ASSERT(!m_markerPositions.isEmpty()); | |
331 | |
332 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(
this); | |
333 ASSERT(resources); | |
334 | |
335 RenderSVGResourceMarker* markerStart = resources->markerStart(); | |
336 RenderSVGResourceMarker* markerMid = resources->markerMid(); | |
337 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); | |
338 ASSERT(markerStart || markerMid || markerEnd); | |
339 | |
340 FloatRect boundaries; | |
341 unsigned size = m_markerPositions.size(); | |
342 for (unsigned i = 0; i < size; ++i) { | |
343 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i]
.type, markerStart, markerMid, markerEnd)) | |
344 boundaries.unite(marker->markerBoundaries(marker->markerTransformati
on(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth))); | |
345 } | |
346 return boundaries; | |
347 } | |
348 | |
349 FloatRect RenderSVGShape::calculateObjectBoundingBox() const | 206 FloatRect RenderSVGShape::calculateObjectBoundingBox() const |
350 { | 207 { |
351 return path().boundingRect(); | 208 return path().boundingRect(); |
352 } | 209 } |
353 | 210 |
354 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const | 211 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const |
355 { | 212 { |
356 ASSERT(m_path); | 213 ASSERT(m_path); |
357 FloatRect strokeBoundingBox = m_fillBoundingBox; | 214 FloatRect strokeBoundingBox = m_fillBoundingBox; |
358 | 215 |
359 if (style()->svgStyle().hasStroke()) { | 216 if (style()->svgStyle().hasStroke()) { |
360 StrokeData strokeData; | 217 StrokeData strokeData; |
361 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), thi
s); | 218 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), thi
s); |
362 if (hasNonScalingStroke()) { | 219 if (hasNonScalingStroke()) { |
363 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); | 220 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); |
364 if (nonScalingTransform.isInvertible()) { | 221 if (nonScalingTransform.isInvertible()) { |
365 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTra
nsform); | 222 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTra
nsform); |
366 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strok
eData); | 223 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strok
eData); |
367 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strok
eBoundingRect); | 224 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strok
eBoundingRect); |
368 strokeBoundingBox.unite(strokeBoundingRect); | 225 strokeBoundingBox.unite(strokeBoundingRect); |
369 } | 226 } |
370 } else { | 227 } else { |
371 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData)); | 228 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData)); |
372 } | 229 } |
373 } | 230 } |
374 | 231 |
375 if (!m_markerPositions.isEmpty()) | |
376 strokeBoundingBox.unite(markerRect(strokeWidth())); | |
377 | |
378 return strokeBoundingBox; | 232 return strokeBoundingBox; |
379 } | 233 } |
380 | 234 |
381 void RenderSVGShape::updatePaintInvalidationBoundingBox() | 235 void RenderSVGShape::updatePaintInvalidationBoundingBox() |
382 { | 236 { |
383 m_paintInvalidationBoundingBox = strokeBoundingBox(); | 237 m_paintInvalidationBoundingBox = strokeBoundingBox(); |
384 if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty()) | 238 if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty()) |
385 m_paintInvalidationBoundingBox.inflate(1); | 239 m_paintInvalidationBoundingBox.inflate(1); |
386 SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintI
nvalidationBoundingBox); | 240 SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintI
nvalidationBoundingBox); |
387 } | 241 } |
388 | 242 |
389 float RenderSVGShape::strokeWidth() const | 243 float RenderSVGShape::strokeWidth() const |
390 { | 244 { |
391 SVGLengthContext lengthContext(element()); | 245 SVGLengthContext lengthContext(element()); |
392 return style()->svgStyle().strokeWidth()->value(lengthContext); | 246 return style()->svgStyle().strokeWidth()->value(lengthContext); |
393 } | 247 } |
394 | 248 |
395 bool RenderSVGShape::hasSmoothStroke() const | 249 bool RenderSVGShape::hasSmoothStroke() const |
396 { | 250 { |
397 const SVGRenderStyle& svgStyle = style()->svgStyle(); | 251 const SVGRenderStyle& svgStyle = style()->svgStyle(); |
398 return svgStyle.strokeDashArray()->isEmpty() | 252 return svgStyle.strokeDashArray()->isEmpty() |
399 && svgStyle.strokeMiterLimit() == SVGRenderStyle::initialStrokeMiterLimi
t() | 253 && svgStyle.strokeMiterLimit() == SVGRenderStyle::initialStrokeMiterLimi
t() |
400 && svgStyle.joinStyle() == SVGRenderStyle::initialJoinStyle() | 254 && svgStyle.joinStyle() == SVGRenderStyle::initialJoinStyle() |
401 && svgStyle.capStyle() == SVGRenderStyle::initialCapStyle(); | 255 && svgStyle.capStyle() == SVGRenderStyle::initialCapStyle(); |
402 } | 256 } |
403 | 257 |
404 void RenderSVGShape::paintMarkers(PaintInfo& paintInfo) | |
405 { | |
406 ASSERT(!m_markerPositions.isEmpty()); | |
407 | |
408 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(
this); | |
409 if (!resources) | |
410 return; | |
411 | |
412 RenderSVGResourceMarker* markerStart = resources->markerStart(); | |
413 RenderSVGResourceMarker* markerMid = resources->markerMid(); | |
414 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); | |
415 if (!markerStart && !markerMid && !markerEnd) | |
416 return; | |
417 | |
418 float strokeWidth = this->strokeWidth(); | |
419 unsigned size = m_markerPositions.size(); | |
420 for (unsigned i = 0; i < size; ++i) { | |
421 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i]
.type, markerStart, markerMid, markerEnd)) | |
422 SVGMarkerPainter(*marker).paint(paintInfo, m_markerPositions[i], str
okeWidth); | |
423 } | |
424 } | 258 } |
425 | |
426 void RenderSVGShape::processMarkerPositions() | |
427 { | |
428 m_markerPositions.clear(); | |
429 | |
430 if (!shouldGenerateMarkerPositions()) | |
431 return; | |
432 | |
433 ASSERT(m_path); | |
434 | |
435 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(
this); | |
436 ASSERT(resources); | |
437 | |
438 RenderSVGResourceMarker* markerStart = resources->markerStart(); | |
439 | |
440 SVGMarkerData markerData(m_markerPositions, markerStart ? markerStart->orien
tType() == SVGMarkerOrientAutoStartReverse : false); | |
441 m_path->apply(&markerData, SVGMarkerData::updateFromPathElement); | |
442 markerData.pathIsDone(); | |
443 } | |
444 | |
445 } | |
OLD | NEW |