blob: e4f3131d32052ff622d6822f4f88e01fbc1092e3 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010030#include "core/rendering/svg/RenderSVGShape.h"
31
32#include "core/platform/graphics/FloatPoint.h"
Torne (Richard Coles)81a51572013-05-13 16:52:28 +010033#include "core/platform/graphics/GraphicsContextStateSaver.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010034#include "core/rendering/HitTestRequest.h"
35#include "core/rendering/LayoutRepainter.h"
36#include "core/rendering/PointerEventsHitRules.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010037#include "core/rendering/svg/RenderSVGResourceMarker.h"
38#include "core/rendering/svg/RenderSVGResourceSolidColor.h"
39#include "core/rendering/svg/SVGPathData.h"
40#include "core/rendering/svg/SVGRenderingContext.h"
41#include "core/rendering/svg/SVGResources.h"
42#include "core/rendering/svg/SVGResourcesCache.h"
Ben Murdoch591b9582013-07-10 11:41:44 +010043#include "core/svg/SVGGraphicsElement.h"
Ben Murdoch02772c62013-07-26 10:21:05 +010044#include "wtf/MathExtras.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010045
46namespace WebCore {
47
Ben Murdoch591b9582013-07-10 11:41:44 +010048RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010049 : RenderSVGModelObject(node)
50 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
Ben Murdoch591b9582013-07-10 11:41:44 +010051 , 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.
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010053{
54}
55
56RenderSVGShape::~RenderSVGShape()
57{
58}
59
60void RenderSVGShape::updateShapeFromElement()
61{
62 m_path.clear();
63 m_path = adoptPtr(new Path);
64 ASSERT(RenderSVGShape::isEmpty());
65
Ben Murdoch591b9582013-07-10 11:41:44 +010066 SVGGraphicsElement* element = toSVGGraphicsElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010067 updatePathFromGraphicsElement(element, path());
68 processMarkerPositions();
69
70 m_fillBoundingBox = calculateObjectBoundingBox();
71 m_strokeBoundingBox = calculateStrokeBoundingBox();
72}
73
74bool RenderSVGShape::isEmpty() const
75{
76 return path().isEmpty();
77}
78
79void RenderSVGShape::fillShape(GraphicsContext* context) const
80{
81 context->fillPath(path());
82}
83
84void RenderSVGShape::strokeShape(GraphicsContext* context) const
85{
86 ASSERT(m_path);
87 Path* usePath = m_path.get();
88
89 if (hasNonScalingStroke())
90 usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
91
92 context->strokePath(*usePath);
93}
94
95bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
96{
97 ASSERT(m_path);
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +010098 StrokeData strokeData;
99 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100100
101 if (hasNonScalingStroke()) {
102 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
103 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
104
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100105 return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100106 }
107
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100108 return m_path->strokeContains(point, strokeData);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100109}
110
111bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
112{
113 return path().contains(point, fillRule);
114}
115
116bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
117{
118 if (!m_fillBoundingBox.contains(point))
119 return false;
120
Ben Murdoch83750172013-07-24 10:36:59 +0100121 StyleColor fallbackColor;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100122 if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
123 return false;
124
125 return shapeDependentFillContains(point, fillRule);
126}
127
128bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
129{
130 if (!strokeBoundingBox().contains(point))
131 return false;
132
Ben Murdoch83750172013-07-24 10:36:59 +0100133 StyleColor fallbackColor;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100134 if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
135 return false;
136
137 return shapeDependentStrokeContains(point);
138}
139
140void RenderSVGShape::layout()
141{
142 StackStats::LayoutCheckPoint layoutCheckPoint;
143 LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
Ben Murdoch591b9582013-07-10 11:41:44 +0100144 SVGGraphicsElement* element = toSVGGraphicsElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100145
146 bool updateCachedBoundariesInParents = false;
147
148 if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
149 updateShapeFromElement();
150 m_needsShapeUpdate = false;
151 updateRepaintBoundingBox();
152 m_needsBoundariesUpdate = false;
153 updateCachedBoundariesInParents = true;
154 }
155
156 if (m_needsTransformUpdate) {
157 m_localTransform = element->animatedLocalTransform();
158 m_needsTransformUpdate = false;
159 updateCachedBoundariesInParents = true;
160 }
161
162 // Invalidate all resources of this client if our layout changed.
163 if (everHadLayout() && selfNeedsLayout())
164 SVGResourcesCache::clientLayoutChanged(this);
165
166 // If our bounds changed, notify the parents.
167 if (updateCachedBoundariesInParents)
168 RenderSVGModelObject::setNeedsBoundariesUpdate();
169
170 repainter.repaintAfterLayout();
171 setNeedsLayout(false);
172}
173
174Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
175{
176 DEFINE_STATIC_LOCAL(Path, tempPath, ());
177
178 tempPath = *path;
179 tempPath.transform(strokeTransform);
180
181 return &tempPath;
182}
183
184bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
185{
186 if (!strokeTransform.isInvertible())
187 return false;
188
189 stateSaver.save();
190 stateSaver.context()->concatCTM(strokeTransform.inverse());
191 return true;
192}
193
194AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
195{
Ben Murdoch591b9582013-07-10 11:41:44 +0100196 SVGGraphicsElement* element = toSVGGraphicsElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100197 return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
198}
199
200bool RenderSVGShape::shouldGenerateMarkerPositions() const
201{
202 if (!style()->svgStyle()->hasMarkers())
203 return false;
204
Ben Murdoch591b9582013-07-10 11:41:44 +0100205 SVGGraphicsElement* element = toSVGGraphicsElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100206 if (!element->supportsMarkers())
207 return false;
208
209 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
210 if (!resources)
211 return false;
212
213 return resources->markerStart() || resources->markerMid() || resources->markerEnd();
214}
215
216void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
217{
Ben Murdoch83750172013-07-24 10:36:59 +0100218 StyleColor fallbackColor;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100219 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
220 if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
221 fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
222 else if (fallbackColor.isValid()) {
223 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
Ben Murdoch83750172013-07-24 10:36:59 +0100224 fallbackResource->setColor(fallbackColor.color());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100225 if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
226 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
227 }
228 }
229}
230
231void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
232{
Ben Murdoch83750172013-07-24 10:36:59 +0100233 StyleColor fallbackColor;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100234 if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor)) {
235 if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
236 strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
237 else if (fallbackColor.isValid()) {
238 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
Ben Murdoch83750172013-07-24 10:36:59 +0100239 fallbackResource->setColor(fallbackColor.color());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100240 if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
241 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
242 }
243 }
244}
245
246void RenderSVGShape::fillAndStrokeShape(GraphicsContext* context)
247{
248 RenderStyle* style = this->style();
249
250 fillShape(style, context);
251
252 if (!style->svgStyle()->hasVisibleStroke())
253 return;
254
255 GraphicsContextStateSaver stateSaver(*context, false);
256
257 if (hasNonScalingStroke()) {
258 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
259 if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
260 return;
261 }
262
263 strokeShape(style, context);
264}
265
266void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
267{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100268 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
269
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100270 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || isEmpty())
271 return;
272 FloatRect boundingBox = repaintRectInLocalCoordinates();
273 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
274 return;
275
276 PaintInfo childPaintInfo(paintInfo);
277 bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
278 if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
279 GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
280 childPaintInfo.applyTransform(m_localTransform);
281
282 if (childPaintInfo.phase == PaintPhaseForeground) {
283 SVGRenderingContext renderingContext(this, childPaintInfo);
284
285 if (renderingContext.isRenderingPrepared()) {
286 const SVGRenderStyle* svgStyle = style()->svgStyle();
287 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
288 childPaintInfo.context->setShouldAntialias(false);
289
290 fillAndStrokeShape(childPaintInfo.context);
291 if (!m_markerPositions.isEmpty())
292 drawMarkers(childPaintInfo);
293 }
294 }
295
296 if (drawsOutline)
297 paintOutline(childPaintInfo, IntRect(boundingBox));
298 }
299}
300
301// This method is called from inside paintOutline() since we call paintOutline()
302// while transformed to our coord system, return local coords
303void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
304{
305 IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
306 if (!rect.isEmpty())
307 rects.append(rect);
308}
309
310bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
311{
312 // We only draw in the forground phase, so we only hit-test then.
313 if (hitTestAction != HitTestForeground)
314 return false;
315
316 FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
317
318 if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
319 return false;
320
321 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
322 bool isVisible = (style()->visibility() == VISIBLE);
323 if (isVisible || !hitRules.requireVisible) {
324 const SVGRenderStyle* svgStyle = style()->svgStyle();
325 WindRule fillRule = svgStyle->fillRule();
326 if (request.svgClipContent())
327 fillRule = svgStyle->clipRule();
328 if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
329 || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
330 updateHitTestResult(result, roundedLayoutPoint(localPoint));
331 return true;
332 }
333 }
334 return false;
335}
336
337static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
338{
339 switch (type) {
340 case StartMarker:
341 return markerStart;
342 case MidMarker:
343 return markerMid;
344 case EndMarker:
345 return markerEnd;
346 }
347
348 ASSERT_NOT_REACHED();
349 return 0;
350}
351
352FloatRect RenderSVGShape::markerRect(float strokeWidth) const
353{
354 ASSERT(!m_markerPositions.isEmpty());
355
356 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
357 ASSERT(resources);
358
359 RenderSVGResourceMarker* markerStart = resources->markerStart();
360 RenderSVGResourceMarker* markerMid = resources->markerMid();
361 RenderSVGResourceMarker* markerEnd = resources->markerEnd();
362 ASSERT(markerStart || markerMid || markerEnd);
363
364 FloatRect boundaries;
365 unsigned size = m_markerPositions.size();
366 for (unsigned i = 0; i < size; ++i) {
367 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
368 boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
369 }
370 return boundaries;
371}
372
373FloatRect RenderSVGShape::calculateObjectBoundingBox() const
374{
375 return path().boundingRect();
376}
377
378FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
379{
380 ASSERT(m_path);
381 FloatRect strokeBoundingBox = m_fillBoundingBox;
382
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100383 if (style()->svgStyle()->hasStroke()) {
384 StrokeData strokeData;
385 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100386 if (hasNonScalingStroke()) {
387 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
388 if (nonScalingTransform.isInvertible()) {
389 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100390 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100391 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
392 strokeBoundingBox.unite(strokeBoundingRect);
393 }
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100394 } else {
395 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
396 }
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100397 }
398
399 if (!m_markerPositions.isEmpty())
400 strokeBoundingBox.unite(markerRect(strokeWidth()));
401
402 return strokeBoundingBox;
403}
404
405void RenderSVGShape::updateRepaintBoundingBox()
406{
407 m_repaintBoundingBoxExcludingShadow = strokeBoundingBox();
408 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBoxExcludingShadow);
409
410 m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow;
411 SVGRenderSupport::intersectRepaintRectWithShadows(this, m_repaintBoundingBox);
412}
413
414float RenderSVGShape::strokeWidth() const
415{
416 SVGElement* svgElement = toSVGElement(node());
417 SVGLengthContext lengthContext(svgElement);
418 return style()->svgStyle()->strokeWidth().value(lengthContext);
419}
420
421bool 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
430void 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
452void 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}