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/rendering/GraphicsContextAnnotator.h"
32 #include "core/rendering/HitTestRequest.h"
33 #include "core/rendering/PointerEventsHitRules.h"
34 #include "core/rendering/svg/RenderSVGResourceMarker.h"
35 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
36 #include "core/rendering/svg/SVGPathData.h"
37 #include "core/rendering/svg/SVGRenderSupport.h"
38 #include "core/rendering/svg/SVGRenderingContext.h"
39 #include "core/rendering/svg/SVGResources.h"
40 #include "core/rendering/svg/SVGResourcesCache.h"
41 #include "core/svg/SVGGraphicsElement.h"
42 #include "platform/geometry/FloatPoint.h"
43 #include "platform/graphics/GraphicsContextStateSaver.h"
44 #include "wtf/MathExtras.h"
48 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
49 : RenderSVGModelObject(node)
50 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
51 , 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 AffineTransform object once from SVGGraphicsElement.
56 RenderSVGShape::~RenderSVGShape()
60 void RenderSVGShape::updateShapeFromElement()
63 m_path = adoptPtr(new Path);
64 ASSERT(RenderSVGShape::isShapeEmpty());
66 updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
67 processMarkerPositions();
69 m_fillBoundingBox = calculateObjectBoundingBox();
70 m_strokeBoundingBox = calculateStrokeBoundingBox();
73 void RenderSVGShape::fillShape(GraphicsContext* context) const
75 context->fillPath(path());
78 void RenderSVGShape::strokeShape(GraphicsContext* context) const
81 Path* usePath = m_path.get();
83 if (hasNonScalingStroke())
84 usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
86 context->strokePath(*usePath);
89 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
92 StrokeData strokeData;
93 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
95 if (hasNonScalingStroke()) {
96 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
97 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
99 return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
102 return m_path->strokeContains(point, strokeData);
105 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
107 return path().contains(point, fillRule);
110 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
112 if (!m_fillBoundingBox.contains(point))
116 if (requiresFill && !RenderSVGResource::requestPaintingResource(ApplyToFillMode, this, style(), hasFallback))
119 return shapeDependentFillContains(point, fillRule);
122 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
124 if (!strokeBoundingBox().contains(point))
128 if (requiresStroke && !RenderSVGResource::requestPaintingResource(ApplyToStrokeMode, this, style(), hasFallback))
131 return shapeDependentStrokeContains(point);
134 void RenderSVGShape::layout()
136 bool updateCachedBoundariesInParents = false;
138 if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
139 updateShapeFromElement();
140 m_needsShapeUpdate = false;
141 updatePaintInvalidationBoundingBox();
142 m_needsBoundariesUpdate = false;
143 updateCachedBoundariesInParents = true;
146 if (m_needsTransformUpdate) {
147 m_localTransform = toSVGGraphicsElement(element())->animatedLocalTransform();
148 m_needsTransformUpdate = false;
149 updateCachedBoundariesInParents = true;
152 // Invalidate all resources of this client if our layout changed.
153 if (everHadLayout() && selfNeedsLayout())
154 SVGResourcesCache::clientLayoutChanged(this);
156 // If our bounds changed, notify the parents.
157 if (updateCachedBoundariesInParents)
158 RenderSVGModelObject::setNeedsBoundariesUpdate();
163 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
165 DEFINE_STATIC_LOCAL(Path, tempPath, ());
168 tempPath.transform(strokeTransform);
173 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
175 if (!strokeTransform.isInvertible())
179 stateSaver.context()->concatCTM(strokeTransform.inverse());
183 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
185 return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
188 bool RenderSVGShape::shouldGenerateMarkerPositions() const
190 if (!style()->svgStyle().hasMarkers())
193 if (!SVGResources::supportsMarkers(*toSVGGraphicsElement(element())))
196 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
200 return resources->markerStart() || resources->markerMid() || resources->markerEnd();
203 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
206 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::requestPaintingResource(ApplyToFillMode, this, style, hasFallback)) {
207 if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode)) {
209 fillPaintingResource->postApplyResource(this, context);
210 } else if (hasFallback) {
211 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
212 if (fallbackResource->applyResource(this, style, context, ApplyToFillMode)) {
214 fallbackResource->postApplyResource(this, context);
220 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
223 if (RenderSVGResource* strokePaintingResource = RenderSVGResource::requestPaintingResource(ApplyToStrokeMode, this, style, hasFallback)) {
224 if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode)) {
225 strokeShape(context);
226 strokePaintingResource->postApplyResource(this, context);
227 } else if (hasFallback) {
228 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
229 if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode)) {
230 strokeShape(context);
231 fallbackResource->postApplyResource(this, context);
237 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
239 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
240 if (paintInfo.phase != PaintPhaseForeground
241 || style()->visibility() == HIDDEN
245 FloatRect boundingBox = paintInvalidationRectInLocalCoordinates();
246 if (!SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(boundingBox, m_localTransform, paintInfo))
249 PaintInfo childPaintInfo(paintInfo);
251 GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
252 childPaintInfo.applyTransform(m_localTransform);
254 SVGRenderingContext renderingContext(this, childPaintInfo);
256 if (renderingContext.isRenderingPrepared()) {
257 const SVGRenderStyle& svgStyle = style()->svgStyle();
258 if (svgStyle.shapeRendering() == SR_CRISPEDGES)
259 childPaintInfo.context->setShouldAntialias(false);
261 for (int i = 0; i < 3; i++) {
262 switch (svgStyle.paintOrderType(i)) {
264 fillShape(this->style(), childPaintInfo.context);
267 if (svgStyle.hasVisibleStroke()) {
268 GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
269 AffineTransform nonScalingTransform;
271 if (hasNonScalingStroke()) {
272 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
273 if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
277 strokeShape(this->style(), childPaintInfo.context);
281 if (!m_markerPositions.isEmpty())
282 drawMarkers(childPaintInfo);
285 ASSERT_NOT_REACHED();
291 if (style()->outlineWidth())
292 paintOutline(childPaintInfo, IntRect(boundingBox));
295 // This method is called from inside paintOutline() since we call paintOutline()
296 // while transformed to our coord system, return local coords
297 void RenderSVGShape::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) const
299 LayoutRect rect = LayoutRect(paintInvalidationRectInLocalCoordinates());
304 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
306 // We only draw in the foreground phase, so we only hit-test then.
307 if (hitTestAction != HitTestForeground)
310 FloatPoint localPoint;
311 if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, m_localTransform, pointInParent, localPoint))
314 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, style()->pointerEvents());
315 if (nodeAtFloatPointInternal(request, localPoint, hitRules)) {
316 updateHitTestResult(result, roundedLayoutPoint(localPoint));
323 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
325 bool isVisible = (style()->visibility() == VISIBLE);
326 if (isVisible || !hitRules.requireVisible) {
327 const SVGRenderStyle& svgStyle = style()->svgStyle();
328 WindRule fillRule = svgStyle.fillRule();
329 if (request.svgClipContent())
330 fillRule = svgStyle.clipRule();
331 if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoint))
332 || (hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
333 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule)))
339 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
350 ASSERT_NOT_REACHED();
354 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
356 ASSERT(!m_markerPositions.isEmpty());
358 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
361 RenderSVGResourceMarker* markerStart = resources->markerStart();
362 RenderSVGResourceMarker* markerMid = resources->markerMid();
363 RenderSVGResourceMarker* markerEnd = resources->markerEnd();
364 ASSERT(markerStart || markerMid || markerEnd);
366 FloatRect boundaries;
367 unsigned size = m_markerPositions.size();
368 for (unsigned i = 0; i < size; ++i) {
369 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
370 boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
375 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
377 return path().boundingRect();
380 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
383 FloatRect strokeBoundingBox = m_fillBoundingBox;
385 if (style()->svgStyle().hasStroke()) {
386 StrokeData strokeData;
387 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
388 if (hasNonScalingStroke()) {
389 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
390 if (nonScalingTransform.isInvertible()) {
391 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
392 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
393 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
394 strokeBoundingBox.unite(strokeBoundingRect);
397 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
401 if (!m_markerPositions.isEmpty())
402 strokeBoundingBox.unite(markerRect(strokeWidth()));
404 return strokeBoundingBox;
407 void RenderSVGShape::updatePaintInvalidationBoundingBox()
409 m_paintInvalidationBoundingBox = strokeBoundingBox();
410 if (strokeWidth() < 1.0f && !m_paintInvalidationBoundingBox.isEmpty())
411 m_paintInvalidationBoundingBox.inflate(1);
412 SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintInvalidationBoundingBox);
415 float RenderSVGShape::strokeWidth() const
417 SVGLengthContext lengthContext(element());
418 return style()->svgStyle().strokeWidth()->value(lengthContext);
421 bool RenderSVGShape::hasSmoothStroke() const
423 const SVGRenderStyle& svgStyle = style()->svgStyle();
424 return svgStyle.strokeDashArray()->isEmpty()
425 && svgStyle.strokeMiterLimit() == SVGRenderStyle::initialStrokeMiterLimit()
426 && svgStyle.joinStyle() == SVGRenderStyle::initialJoinStyle()
427 && svgStyle.capStyle() == SVGRenderStyle::initialCapStyle();
430 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
432 ASSERT(!m_markerPositions.isEmpty());
434 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
438 RenderSVGResourceMarker* markerStart = resources->markerStart();
439 RenderSVGResourceMarker* markerMid = resources->markerMid();
440 RenderSVGResourceMarker* markerEnd = resources->markerEnd();
441 if (!markerStart && !markerMid && !markerEnd)
444 float strokeWidth = this->strokeWidth();
445 unsigned size = m_markerPositions.size();
446 for (unsigned i = 0; i < size; ++i) {
447 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
448 marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
452 void RenderSVGShape::processMarkerPositions()
454 m_markerPositions.clear();
456 if (!shouldGenerateMarkerPositions())
461 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
464 RenderSVGResourceMarker* markerStart = resources->markerStart();
466 SVGMarkerData markerData(m_markerPositions, markerStart ? markerStart->orientType() == SVGMarkerOrientAutoStartReverse : false);
467 m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
468 markerData.pathIsDone();