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