• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
5                   2009 Google, Inc.
6                   2009 Dirk Schulze <krit@webkit.org>
7 
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12 
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17 
18     You should have received a copy of the GNU Library General Public License
19     aint with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23 
24 #include "config.h"
25 
26 #if ENABLE(SVG)
27 #include "RenderPath.h"
28 
29 #include "FloatPoint.h"
30 #include "FloatQuad.h"
31 #include "GraphicsContext.h"
32 #include "PointerEventsHitRules.h"
33 #include "RenderSVGContainer.h"
34 #include "StrokeStyleApplier.h"
35 #include "SVGPaintServer.h"
36 #include "SVGRenderSupport.h"
37 #include "SVGResourceFilter.h"
38 #include "SVGResourceMarker.h"
39 #include "SVGResourceMasker.h"
40 #include "SVGStyledTransformableElement.h"
41 #include "SVGTransformList.h"
42 #include "SVGURIReference.h"
43 #include <wtf/MathExtras.h>
44 
45 namespace WebCore {
46 
47 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
48 public:
BoundingRectStrokeStyleApplier(const RenderObject * object,RenderStyle * style)49     BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
50         : m_object(object)
51         , m_style(style)
52     {
53         ASSERT(style);
54         ASSERT(object);
55     }
56 
strokeStyle(GraphicsContext * gc)57     void strokeStyle(GraphicsContext* gc)
58     {
59         applyStrokeStyleToContext(gc, m_style, m_object);
60     }
61 
62 private:
63     const RenderObject* m_object;
64     RenderStyle* m_style;
65 };
66 
RenderPath(SVGStyledTransformableElement * node)67 RenderPath::RenderPath(SVGStyledTransformableElement* node)
68     : RenderSVGModelObject(node)
69 {
70 }
71 
localToParentTransform() const72 const AffineTransform& RenderPath::localToParentTransform() const
73 {
74     return m_localTransform;
75 }
76 
localTransform() const77 AffineTransform RenderPath::localTransform() const
78 {
79     return m_localTransform;
80 }
81 
fillContains(const FloatPoint & point,bool requiresFill) const82 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
83 {
84     if (m_path.isEmpty())
85         return false;
86 
87     if (requiresFill && !SVGPaintServer::fillPaintServer(style(), this))
88         return false;
89 
90     return m_path.contains(point, style()->svgStyle()->fillRule());
91 }
92 
strokeContains(const FloatPoint & point,bool requiresStroke) const93 bool RenderPath::strokeContains(const FloatPoint& point, bool requiresStroke) const
94 {
95     if (m_path.isEmpty())
96         return false;
97 
98     if (requiresStroke && !SVGPaintServer::strokePaintServer(style(), this))
99         return false;
100 
101     BoundingRectStrokeStyleApplier strokeStyle(this, style());
102     return m_path.strokeContains(&strokeStyle, point);
103 }
104 
objectBoundingBox() const105 FloatRect RenderPath::objectBoundingBox() const
106 {
107     if (m_path.isEmpty())
108         return FloatRect();
109 
110     if (m_cachedLocalFillBBox.isEmpty())
111         m_cachedLocalFillBBox = m_path.boundingRect();
112 
113     return m_cachedLocalFillBBox;
114 }
115 
strokeBoundingBox() const116 FloatRect RenderPath::strokeBoundingBox() const
117 {
118     if (m_path.isEmpty())
119         return FloatRect();
120 
121     if (!m_cachedLocalStrokeBBox.isEmpty())
122         return m_cachedLocalStrokeBBox;
123 
124     m_cachedLocalStrokeBBox = objectBoundingBox();
125     if (style()->svgStyle()->hasStroke()) {
126         BoundingRectStrokeStyleApplier strokeStyle(this, style());
127         m_cachedLocalStrokeBBox.unite(m_path.strokeBoundingRect(&strokeStyle));
128     }
129 
130     return m_cachedLocalStrokeBBox;
131 }
132 
markerBoundingBox() const133 FloatRect RenderPath::markerBoundingBox() const
134 {
135     if (m_path.isEmpty())
136         return FloatRect();
137 
138     if (m_cachedLocalMarkerBBox.isEmpty())
139         calculateMarkerBoundsIfNeeded();
140 
141     return m_cachedLocalMarkerBBox;
142 }
143 
repaintRectInLocalCoordinates() const144 FloatRect RenderPath::repaintRectInLocalCoordinates() const
145 {
146     if (m_path.isEmpty())
147         return FloatRect();
148 
149     // If we already have a cached repaint rect, return that
150     if (!m_cachedLocalRepaintRect.isEmpty())
151         return m_cachedLocalRepaintRect;
152 
153     // FIXME: We need to be careful here. We assume that there is no filter,
154     // clipper, marker or masker if the rects are empty.
155     FloatRect rect = filterBoundingBoxForRenderer(this);
156     if (!rect.isEmpty())
157         m_cachedLocalRepaintRect = rect;
158     else {
159         m_cachedLocalRepaintRect = strokeBoundingBox();
160         m_cachedLocalRepaintRect.unite(markerBoundingBox());
161     }
162 
163     rect = clipperBoundingBoxForRenderer(this);
164     if (!rect.isEmpty())
165         m_cachedLocalRepaintRect.intersect(rect);
166 
167     rect = maskerBoundingBoxForRenderer(this);
168     if (!rect.isEmpty())
169         m_cachedLocalRepaintRect.intersect(rect);
170 
171     style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect);
172 
173     return m_cachedLocalRepaintRect;
174 }
175 
setPath(const Path & newPath)176 void RenderPath::setPath(const Path& newPath)
177 {
178     m_path = newPath;
179     m_cachedLocalRepaintRect = FloatRect();
180     m_cachedLocalStrokeBBox = FloatRect();
181     m_cachedLocalFillBBox = FloatRect();
182     m_cachedLocalMarkerBBox = FloatRect();
183 }
184 
layout()185 void RenderPath::layout()
186 {
187     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
188 
189     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
190     m_localTransform = element->animatedLocalTransform();
191     setPath(element->toPathData());
192 
193     repainter.repaintAfterLayout();
194     setNeedsLayout(false);
195 }
196 
fillAndStrokePath(const Path & path,GraphicsContext * context,RenderStyle * style,RenderPath * object)197 static inline void fillAndStrokePath(const Path& path, GraphicsContext* context, RenderStyle* style, RenderPath* object)
198 {
199     context->beginPath();
200 
201     SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, object);
202     if (fillPaintServer) {
203         context->addPath(path);
204         fillPaintServer->draw(context, object, ApplyToFillTargetType);
205     }
206 
207     SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, object);
208     if (strokePaintServer) {
209         context->addPath(path); // path is cleared when filled.
210         strokePaintServer->draw(context, object, ApplyToStrokeTargetType);
211     }
212 }
213 
paint(PaintInfo & paintInfo,int,int)214 void RenderPath::paint(PaintInfo& paintInfo, int, int)
215 {
216     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
217         return;
218 
219     FloatRect boundingBox = repaintRectInLocalCoordinates();
220     FloatRect nonLocalBoundingBox = m_localTransform.mapRect(boundingBox);
221     // FIXME: The empty rect check is to deal with incorrect initial clip in renderSubtreeToImage
222     // unfortunately fixing that problem is fairly complex unless we were willing to just futz the
223     // rect to something "close enough"
224     if (!nonLocalBoundingBox.intersects(paintInfo.rect) && !paintInfo.rect.isEmpty())
225         return;
226 
227     PaintInfo childPaintInfo(paintInfo);
228     childPaintInfo.context->save();
229     applyTransformToPaintInfo(childPaintInfo, m_localTransform);
230     SVGResourceFilter* filter = 0;
231 
232     if (childPaintInfo.phase == PaintPhaseForeground) {
233         PaintInfo savedInfo(childPaintInfo);
234 
235         if (prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter)) {
236             if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES)
237                 childPaintInfo.context->setShouldAntialias(false);
238             fillAndStrokePath(m_path, childPaintInfo.context, style(), this);
239 
240             if (static_cast<SVGStyledElement*>(node())->supportsMarkers())
241                 m_markerLayoutInfo.drawMarkers(childPaintInfo);
242         }
243         finishRenderSVGContent(this, childPaintInfo, filter, savedInfo.context);
244     }
245 
246     if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
247         paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
248             static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style());
249 
250     childPaintInfo.context->restore();
251 }
252 
253 // This method is called from inside paintOutline() since we call paintOutline()
254 // while transformed to our coord system, return local coords
addFocusRingRects(Vector<IntRect> & rects,int,int)255 void RenderPath::addFocusRingRects(Vector<IntRect>& rects, int, int)
256 {
257     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
258     if (!rect.isEmpty())
259         rects.append(rect);
260 }
261 
nodeAtFloatPoint(const HitTestRequest &,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)262 bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
263 {
264     // We only draw in the forground phase, so we only hit-test then.
265     if (hitTestAction != HitTestForeground)
266         return false;
267 
268     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
269 
270     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents());
271 
272     bool isVisible = (style()->visibility() == VISIBLE);
273     if (isVisible || !hitRules.requireVisible) {
274         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
275             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill))) {
276             updateHitTestResult(result, roundedIntPoint(localPoint));
277             return true;
278         }
279     }
280 
281     return false;
282 }
283 
calculateMarkerBoundsIfNeeded() const284 void RenderPath::calculateMarkerBoundsIfNeeded() const
285 {
286     Document* doc = document();
287 
288     SVGElement* svgElement = static_cast<SVGElement*>(node());
289     ASSERT(svgElement && svgElement->document());
290     if (!svgElement->isStyled())
291         return;
292 
293     SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
294     if (!styledElement->supportsMarkers())
295         return;
296 
297     const SVGRenderStyle* svgStyle = style()->svgStyle();
298     AtomicString startMarkerId(svgStyle->startMarker());
299     AtomicString midMarkerId(svgStyle->midMarker());
300     AtomicString endMarkerId(svgStyle->endMarker());
301 
302     SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId, this);
303     SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId, this);
304     SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId, this);
305 
306     if (!startMarker && !startMarkerId.isEmpty())
307         svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement);
308     else if (startMarker)
309         startMarker->addClient(styledElement);
310 
311     if (!midMarker && !midMarkerId.isEmpty())
312         svgElement->document()->accessSVGExtensions()->addPendingResource(midMarkerId, styledElement);
313     else if (midMarker)
314         midMarker->addClient(styledElement);
315 
316     if (!endMarker && !endMarkerId.isEmpty())
317         svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement);
318     else if (endMarker)
319         endMarker->addClient(styledElement);
320 
321     if (!startMarker && !midMarker && !endMarker)
322         return;
323 
324     float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f);
325     m_cachedLocalMarkerBBox = m_markerLayoutInfo.calculateBoundaries(startMarker, midMarker, endMarker, strokeWidth, m_path);
326 }
327 
328 }
329 
330 #endif // ENABLE(SVG)
331