• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/rendering/GraphicsContextAnnotator.h"
32 #include "core/rendering/HitTestRequest.h"
33 #include "core/rendering/LayoutRepainter.h"
34 #include "core/rendering/PointerEventsHitRules.h"
35 #include "core/rendering/svg/RenderSVGResourceMarker.h"
36 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
37 #include "core/rendering/svg/SVGPathData.h"
38 #include "core/rendering/svg/SVGRenderSupport.h"
39 #include "core/rendering/svg/SVGRenderingContext.h"
40 #include "core/rendering/svg/SVGResources.h"
41 #include "core/rendering/svg/SVGResourcesCache.h"
42 #include "core/svg/SVGGraphicsElement.h"
43 #include "platform/geometry/FloatPoint.h"
44 #include "platform/graphics/GraphicsContextStateSaver.h"
45 #include "wtf/MathExtras.h"
46 
47 namespace WebCore {
48 
RenderSVGShape(SVGGraphicsElement * node)49 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
50     : RenderSVGModelObject(node)
51     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
52     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
53     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
54 {
55 }
56 
~RenderSVGShape()57 RenderSVGShape::~RenderSVGShape()
58 {
59 }
60 
updateShapeFromElement()61 void RenderSVGShape::updateShapeFromElement()
62 {
63     m_path.clear();
64     m_path = adoptPtr(new Path);
65     ASSERT(RenderSVGShape::isShapeEmpty());
66 
67     updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
68     processMarkerPositions();
69 
70     m_fillBoundingBox = calculateObjectBoundingBox();
71     m_strokeBoundingBox = calculateStrokeBoundingBox();
72 }
73 
fillShape(GraphicsContext * context) const74 void RenderSVGShape::fillShape(GraphicsContext* context) const
75 {
76     context->fillPath(path());
77 }
78 
strokeShape(GraphicsContext * context) const79 void RenderSVGShape::strokeShape(GraphicsContext* context) const
80 {
81     ASSERT(m_path);
82     Path* usePath = m_path.get();
83 
84     if (hasNonScalingStroke())
85         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
86 
87     context->strokePath(*usePath);
88 }
89 
shapeDependentStrokeContains(const FloatPoint & point)90 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
91 {
92     ASSERT(m_path);
93     StrokeData strokeData;
94     SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
95 
96     if (hasNonScalingStroke()) {
97         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
98         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
99 
100         return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
101     }
102 
103     return m_path->strokeContains(point, strokeData);
104 }
105 
shapeDependentFillContains(const FloatPoint & point,const WindRule fillRule) const106 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
107 {
108     return path().contains(point, fillRule);
109 }
110 
fillContains(const FloatPoint & point,bool requiresFill,const WindRule fillRule)111 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
112 {
113     if (!m_fillBoundingBox.contains(point))
114         return false;
115 
116     bool hasFallback;
117     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), hasFallback))
118         return false;
119 
120     return shapeDependentFillContains(point, fillRule);
121 }
122 
strokeContains(const FloatPoint & point,bool requiresStroke)123 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
124 {
125     if (!strokeBoundingBox().contains(point))
126         return false;
127 
128     bool hasFallback;
129     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), hasFallback))
130         return false;
131 
132     return shapeDependentStrokeContains(point);
133 }
134 
layout()135 void RenderSVGShape::layout()
136 {
137     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
138 
139     bool updateCachedBoundariesInParents = false;
140 
141     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
142         updateShapeFromElement();
143         m_needsShapeUpdate = false;
144         updateRepaintBoundingBox();
145         m_needsBoundariesUpdate = false;
146         updateCachedBoundariesInParents = true;
147     }
148 
149     if (m_needsTransformUpdate) {
150         m_localTransform =  toSVGGraphicsElement(element())->animatedLocalTransform();
151         m_needsTransformUpdate = false;
152         updateCachedBoundariesInParents = true;
153     }
154 
155     // Invalidate all resources of this client if our layout changed.
156     if (everHadLayout() && selfNeedsLayout())
157         SVGResourcesCache::clientLayoutChanged(this);
158 
159     // If our bounds changed, notify the parents.
160     if (updateCachedBoundariesInParents)
161         RenderSVGModelObject::setNeedsBoundariesUpdate();
162 
163     repainter.repaintAfterLayout();
164     clearNeedsLayout();
165 }
166 
nonScalingStrokePath(const Path * path,const AffineTransform & strokeTransform) const167 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
168 {
169     DEFINE_STATIC_LOCAL(Path, tempPath, ());
170 
171     tempPath = *path;
172     tempPath.transform(strokeTransform);
173 
174     return &tempPath;
175 }
176 
setupNonScalingStrokeContext(AffineTransform & strokeTransform,GraphicsContextStateSaver & stateSaver)177 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
178 {
179     if (!strokeTransform.isInvertible())
180         return false;
181 
182     stateSaver.save();
183     stateSaver.context()->concatCTM(strokeTransform.inverse());
184     return true;
185 }
186 
nonScalingStrokeTransform() const187 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
188 {
189     return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
190 }
191 
shouldGenerateMarkerPositions() const192 bool RenderSVGShape::shouldGenerateMarkerPositions() const
193 {
194     if (!style()->svgStyle()->hasMarkers())
195         return false;
196 
197     if (!SVGResources::supportsMarkers(*toSVGGraphicsElement(element())))
198         return false;
199 
200     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
201     if (!resources)
202         return false;
203 
204     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
205 }
206 
fillShape(RenderStyle * style,GraphicsContext * context)207 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
208 {
209     bool hasFallback;
210     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, hasFallback)) {
211         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode)) {
212             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
213         } else if (hasFallback) {
214             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
215             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
216                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
217         }
218     }
219 }
220 
strokeShape(RenderStyle * style,GraphicsContext * context)221 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
222 {
223     bool hasFallback;
224     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, hasFallback)) {
225         if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode)) {
226             strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
227         } else if (hasFallback) {
228             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
229             if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
230                 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
231         }
232     }
233 }
234 
paint(PaintInfo & paintInfo,const LayoutPoint &)235 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
236 {
237     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
238     if (paintInfo.context->paintingDisabled()
239         || paintInfo.phase != PaintPhaseForeground
240         || style()->visibility() == HIDDEN
241         || isShapeEmpty())
242         return;
243 
244     FloatRect boundingBox = paintInvalidationRectInLocalCoordinates();
245     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
246         return;
247 
248     PaintInfo childPaintInfo(paintInfo);
249 
250     GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
251     childPaintInfo.applyTransform(m_localTransform);
252 
253     SVGRenderingContext renderingContext(this, childPaintInfo);
254 
255     if (renderingContext.isRenderingPrepared()) {
256         const SVGRenderStyle* svgStyle = style()->svgStyle();
257         if (svgStyle->shapeRendering() == SR_CRISPEDGES)
258             childPaintInfo.context->setShouldAntialias(false);
259 
260         for (int i = 0; i < 3; i++) {
261             switch (svgStyle->paintOrderType(i)) {
262             case PT_FILL:
263                 fillShape(this->style(), childPaintInfo.context);
264                 break;
265             case PT_STROKE:
266                 if (svgStyle->hasVisibleStroke()) {
267                     GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
268                     AffineTransform nonScalingTransform;
269 
270                     if (hasNonScalingStroke()) {
271                         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
272                         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
273                             return;
274                     }
275 
276                     strokeShape(this->style(), childPaintInfo.context);
277                 }
278                 break;
279             case PT_MARKERS:
280                 if (!m_markerPositions.isEmpty())
281                     drawMarkers(childPaintInfo);
282                 break;
283             default:
284                 ASSERT_NOT_REACHED();
285                 break;
286             }
287         }
288     }
289 
290     if (style()->outlineWidth())
291         paintOutline(childPaintInfo, IntRect(boundingBox));
292 }
293 
294 // This method is called from inside paintOutline() since we call paintOutline()
295 // while transformed to our coord system, return local coords
addFocusRingRects(Vector<IntRect> & rects,const LayoutPoint &,const RenderLayerModelObject *)296 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
297 {
298     IntRect rect = enclosingIntRect(paintInvalidationRectInLocalCoordinates());
299     if (!rect.isEmpty())
300         rects.append(rect);
301 }
302 
nodeAtFloatPoint(const HitTestRequest & request,HitTestResult & result,const FloatPoint & pointInParent,HitTestAction hitTestAction)303 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
304 {
305     // We only draw in the foreground phase, so we only hit-test then.
306     if (hitTestAction != HitTestForeground)
307         return false;
308 
309     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
310 
311     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
312         return false;
313 
314     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, style()->pointerEvents());
315     if (nodeAtFloatPointInternal(request, localPoint, hitRules)) {
316         updateHitTestResult(result, roundedLayoutPoint(localPoint));
317         return true;
318     }
319 
320     return false;
321 }
322 
nodeAtFloatPointInternal(const HitTestRequest & request,const FloatPoint & localPoint,PointerEventsHitRules hitRules)323 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
324 {
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)))
334             return true;
335     }
336     return false;
337 }
338 
markerForType(SVGMarkerType type,RenderSVGResourceMarker * markerStart,RenderSVGResourceMarker * markerMid,RenderSVGResourceMarker * markerEnd)339 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
340 {
341     switch (type) {
342     case StartMarker:
343         return markerStart;
344     case MidMarker:
345         return markerMid;
346     case EndMarker:
347         return markerEnd;
348     }
349 
350     ASSERT_NOT_REACHED();
351     return 0;
352 }
353 
markerRect(float strokeWidth) const354 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
355 {
356     ASSERT(!m_markerPositions.isEmpty());
357 
358     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
359     ASSERT(resources);
360 
361     RenderSVGResourceMarker* markerStart = resources->markerStart();
362     RenderSVGResourceMarker* markerMid = resources->markerMid();
363     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
364     ASSERT(markerStart || markerMid || markerEnd);
365 
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)));
371     }
372     return boundaries;
373 }
374 
calculateObjectBoundingBox() const375 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
376 {
377     return path().boundingRect();
378 }
379 
calculateStrokeBoundingBox() const380 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
381 {
382     ASSERT(m_path);
383     FloatRect strokeBoundingBox = m_fillBoundingBox;
384 
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);
395             }
396         } else {
397             strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
398         }
399     }
400 
401     if (!m_markerPositions.isEmpty())
402         strokeBoundingBox.unite(markerRect(strokeWidth()));
403 
404     return strokeBoundingBox;
405 }
406 
updateRepaintBoundingBox()407 void RenderSVGShape::updateRepaintBoundingBox()
408 {
409     m_repaintBoundingBox = strokeBoundingBox();
410     if (strokeWidth() < 1.0f && !m_repaintBoundingBox.isEmpty())
411         m_repaintBoundingBox.inflate(1);
412     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
413 }
414 
strokeWidth() const415 float RenderSVGShape::strokeWidth() const
416 {
417     SVGLengthContext lengthContext(element());
418     return style()->svgStyle()->strokeWidth()->value(lengthContext);
419 }
420 
hasSmoothStroke() const421 bool RenderSVGShape::hasSmoothStroke() const
422 {
423     const SVGRenderStyle* svgStyle = style()->svgStyle();
424     return svgStyle->strokeDashArray()->isEmpty()
425         && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit()
426         && svgStyle->joinStyle() == svgStyle->initialJoinStyle()
427         && svgStyle->capStyle() == svgStyle->initialCapStyle();
428 }
429 
drawMarkers(PaintInfo & paintInfo)430 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
431 {
432     ASSERT(!m_markerPositions.isEmpty());
433 
434     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
435     if (!resources)
436         return;
437 
438     RenderSVGResourceMarker* markerStart = resources->markerStart();
439     RenderSVGResourceMarker* markerMid = resources->markerMid();
440     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
441     if (!markerStart && !markerMid && !markerEnd)
442         return;
443 
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));
449     }
450 }
451 
processMarkerPositions()452 void RenderSVGShape::processMarkerPositions()
453 {
454     m_markerPositions.clear();
455 
456     if (!shouldGenerateMarkerPositions())
457         return;
458 
459     ASSERT(m_path);
460 
461     SVGMarkerData markerData(m_markerPositions);
462     m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
463     markerData.pathIsDone();
464 }
465 
466 }
467