2 Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2008 Rob Buis <buis@kde.org>
4 2005, 2007 Eric Seidel <eric@webkit.org>
6 2009 Dirk Schulze <krit@webkit.org>
7 Copyright (C) Research In Motion Limited 2010. All rights reserved.
8 2009 Jeff Schiller <codedread@gmail.com>
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public License
21 aint with this library; see the file COPYING.LIB. If not, write to
22 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 Boston, MA 02110-1301, USA.
29 #include "RenderSVGPath.h"
31 #include "FloatPoint.h"
32 #include "FloatQuad.h"
33 #include "GraphicsContext.h"
34 #include "HitTestRequest.h"
35 #include "PointerEventsHitRules.h"
36 #include "RenderSVGContainer.h"
37 #include "RenderSVGResourceMarker.h"
38 #include "RenderSVGResourceSolidColor.h"
39 #include "SVGRenderSupport.h"
40 #include "SVGResources.h"
41 #include "SVGStyledTransformableElement.h"
42 #include "SVGTransformList.h"
43 #include "SVGURIReference.h"
44 #include "StrokeStyleApplier.h"
45 #include <wtf/MathExtras.h>
49 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
51 BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
59 void strokeStyle(GraphicsContext* gc)
61 SVGRenderSupport::applyStrokeStyleToContext(gc, m_style, m_object);
65 const RenderObject* m_object;
69 RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node)
70 : RenderSVGModelObject(node)
71 , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning
72 , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement
73 , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement
77 RenderSVGPath::~RenderSVGPath()
81 bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule)
83 if (!m_fillBoundingBox.contains(point))
87 if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
90 return m_path.contains(point, fillRule);
93 bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke)
95 if (!m_strokeAndMarkerBoundingBox.contains(point))
99 if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
102 BoundingRectStrokeStyleApplier strokeStyle(this, style());
103 return m_path.strokeContains(&strokeStyle, point);
106 void RenderSVGPath::layout()
108 LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
109 SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
111 bool updateCachedBoundariesInParents = false;
113 bool needsPathUpdate = m_needsPathUpdate;
114 if (needsPathUpdate) {
116 element->toPathData(m_path);
117 m_needsPathUpdate = false;
118 updateCachedBoundariesInParents = true;
121 if (m_needsTransformUpdate) {
122 m_localTransform = element->animatedLocalTransform();
123 m_needsTransformUpdate = false;
124 updateCachedBoundariesInParents = true;
127 if (m_needsBoundariesUpdate)
128 updateCachedBoundariesInParents = true;
130 // Invalidate all resources of this client if our layout changed.
131 if (m_everHadLayout && selfNeedsLayout())
132 SVGResourcesCache::clientLayoutChanged(this);
134 // At this point LayoutRepainter already grabbed the old bounds,
135 // recalculate them now so repaintAfterLayout() uses the new bounds.
136 if (needsPathUpdate || m_needsBoundariesUpdate) {
137 updateCachedBoundaries();
138 m_needsBoundariesUpdate = false;
141 // If our bounds changed, notify the parents.
142 if (updateCachedBoundariesInParents)
143 RenderSVGModelObject::setNeedsBoundariesUpdate();
145 repainter.repaintAfterLayout();
146 setNeedsLayout(false);
149 void RenderSVGPath::fillAndStrokePath(GraphicsContext* context)
151 RenderStyle* style = this->style();
154 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
155 if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
156 fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
157 else if (fallbackColor.isValid()) {
158 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
159 fallbackResource->setColor(fallbackColor);
160 if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
161 fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
165 fallbackColor = Color();
166 RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor);
167 if (!strokePaintingResource)
172 bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
173 bool restoreContext = false;
174 if (nonScalingStroke) {
175 SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
176 AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
177 if (!nonScalingStrokeTransform.isInvertible())
181 path.transform(nonScalingStrokeTransform);
184 context->concatCTM(nonScalingStrokeTransform.inverse());
185 restoreContext = true;
188 if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
189 strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
190 else if (fallbackColor.isValid()) {
191 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
192 fallbackResource->setColor(fallbackColor);
193 if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
194 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
201 void RenderSVGPath::paint(PaintInfo& paintInfo, int, int)
203 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
206 FloatRect boundingBox = repaintRectInLocalCoordinates();
207 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
210 PaintInfo childPaintInfo(paintInfo);
211 bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
212 if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
213 childPaintInfo.context->save();
214 childPaintInfo.applyTransform(m_localTransform);
216 if (childPaintInfo.phase == PaintPhaseForeground) {
217 PaintInfo savedInfo(childPaintInfo);
219 if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
220 const SVGRenderStyle* svgStyle = style()->svgStyle();
221 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
222 childPaintInfo.context->setShouldAntialias(false);
224 fillAndStrokePath(childPaintInfo.context);
226 if (svgStyle->hasMarkers())
227 m_markerLayoutInfo.drawMarkers(childPaintInfo);
230 SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
234 paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
235 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()));
237 childPaintInfo.context->restore();
241 // This method is called from inside paintOutline() since we call paintOutline()
242 // while transformed to our coord system, return local coords
243 void RenderSVGPath::addFocusRingRects(Vector<IntRect>& rects, int, int)
245 IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
250 bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
252 // We only draw in the forground phase, so we only hit-test then.
253 if (hitTestAction != HitTestForeground)
256 FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
258 if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
261 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
262 bool isVisible = (style()->visibility() == VISIBLE);
263 if (isVisible || !hitRules.requireVisible) {
264 const SVGRenderStyle* svgStyle = style()->svgStyle();
265 WindRule fillRule = svgStyle->fillRule();
266 if (request.svgClipContent())
267 fillRule = svgStyle->clipRule();
268 if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
269 || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
270 updateHitTestResult(result, roundedIntPoint(localPoint));
277 FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded()
279 SVGElement* svgElement = static_cast<SVGElement*>(node());
280 ASSERT(svgElement && svgElement->document());
281 if (!svgElement->isStyled())
284 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
285 if (!styledElement->supportsMarkers())
288 const SVGRenderStyle* svgStyle = style()->svgStyle();
289 ASSERT(svgStyle->hasMarkers());
291 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
295 RenderSVGResourceMarker* markerStart = resources->markerStart();
296 RenderSVGResourceMarker* markerMid = resources->markerMid();
297 RenderSVGResourceMarker* markerEnd = resources->markerEnd();
298 if (!markerStart && !markerMid && !markerEnd)
301 return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path);
304 void RenderSVGPath::updateCachedBoundaries()
306 if (m_path.isEmpty()) {
307 m_fillBoundingBox = FloatRect();
308 m_strokeAndMarkerBoundingBox = FloatRect();
309 m_repaintBoundingBox = FloatRect();
313 // Cache _unclipped_ fill bounding box, used for calculations in resources
314 m_fillBoundingBox = m_path.boundingRect();
316 // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries)
317 m_strokeAndMarkerBoundingBox = m_fillBoundingBox;
319 const SVGRenderStyle* svgStyle = style()->svgStyle();
320 if (svgStyle->hasStroke()) {
321 BoundingRectStrokeStyleApplier strokeStyle(this, style());
322 m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle));
325 if (svgStyle->hasMarkers()) {
326 FloatRect markerBounds = calculateMarkerBoundsIfNeeded();
327 if (!markerBounds.isEmpty())
328 m_strokeAndMarkerBoundingBox.unite(markerBounds);
331 // Cache smallest possible repaint rectangle
332 m_repaintBoundingBox = m_strokeAndMarkerBoundingBox;
333 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
338 #endif // ENABLE(SVG)