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 "RenderSVGShape.h" 30 31#include "FloatPoint.h" 32#include "FloatQuad.h" 33#include "GraphicsContext.h" 34#include "HitTestRequest.h" 35#include "LayoutRepainter.h" 36#include "PointerEventsHitRules.h" 37#include "RenderSVGResourceMarker.h" 38#include "RenderSVGResourceSolidColor.h" 39#include "SVGPathData.h" 40#include "SVGRenderingContext.h" 41#include "SVGResources.h" 42#include "SVGResourcesCache.h" 43#include "SVGTransformList.h" 44#include "SVGURIReference.h" 45#include "StrokeStyleApplier.h" 46#include <wtf/StackStats.h> 47 48namespace WebCore { 49 50class BoundingRectStrokeStyleApplier final : public StrokeStyleApplier { 51public: 52 BoundingRectStrokeStyleApplier(const RenderSVGShape& renderer) 53 : m_renderer(renderer) 54 { 55 } 56 57 virtual void strokeStyle(GraphicsContext* context) override 58 { 59 SVGRenderSupport::applyStrokeStyleToContext(context, m_renderer.style(), m_renderer); 60 } 61 62private: 63 const RenderSVGShape& m_renderer; 64}; 65 66RenderSVGShape::RenderSVGShape(SVGGraphicsElement& element, PassRef<RenderStyle> style) 67 : RenderSVGModelObject(element, WTF::move(style)) 68 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning. 69 , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement. 70 , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement. 71{ 72} 73 74RenderSVGShape::~RenderSVGShape() 75{ 76} 77 78void RenderSVGShape::updateShapeFromElement() 79{ 80 m_path = std::make_unique<Path>(); 81 ASSERT(RenderSVGShape::isEmpty()); 82 83 updatePathFromGraphicsElement(&graphicsElement(), path()); 84 processMarkerPositions(); 85 86 m_fillBoundingBox = calculateObjectBoundingBox(); 87 m_strokeBoundingBox = calculateStrokeBoundingBox(); 88} 89 90bool RenderSVGShape::isEmpty() const 91{ 92 return path().isEmpty(); 93} 94 95void RenderSVGShape::fillShape(GraphicsContext* context) const 96{ 97 context->fillPath(path()); 98} 99 100void RenderSVGShape::strokeShape(GraphicsContext* context) const 101{ 102 ASSERT(m_path); 103 Path* usePath = m_path.get(); 104 105 if (hasNonScalingStroke()) 106 usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform()); 107 108 context->strokePath(*usePath); 109} 110 111bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) 112{ 113 ASSERT(m_path); 114 BoundingRectStrokeStyleApplier applier(*this); 115 116 if (hasNonScalingStroke()) { 117 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); 118 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); 119 120 return usePath->strokeContains(&applier, nonScalingTransform.mapPoint(point)); 121 } 122 123 return m_path->strokeContains(&applier, point); 124} 125 126bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const 127{ 128 return path().contains(point, fillRule); 129} 130 131bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule) 132{ 133 if (!m_fillBoundingBox.contains(point)) 134 return false; 135 136 Color fallbackColor; 137 if (requiresFill && !RenderSVGResource::fillPaintingResource(*this, style(), fallbackColor)) 138 return false; 139 140 return shapeDependentFillContains(point, fillRule); 141} 142 143bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke) 144{ 145 if (!strokeBoundingBox().contains(point)) 146 return false; 147 148 Color fallbackColor; 149 if (requiresStroke && !RenderSVGResource::strokePaintingResource(*this, style(), fallbackColor)) 150 return false; 151 152 return shapeDependentStrokeContains(point); 153} 154 155void RenderSVGShape::layout() 156{ 157 StackStats::LayoutCheckPoint layoutCheckPoint; 158 LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this) && selfNeedsLayout()); 159 160 bool updateCachedBoundariesInParents = false; 161 162 if (m_needsShapeUpdate || m_needsBoundariesUpdate) { 163 updateShapeFromElement(); 164 m_needsShapeUpdate = false; 165 updateRepaintBoundingBox(); 166 m_needsBoundariesUpdate = false; 167 updateCachedBoundariesInParents = true; 168 } 169 170 if (m_needsTransformUpdate) { 171 m_localTransform = graphicsElement().animatedLocalTransform(); 172 m_needsTransformUpdate = false; 173 updateCachedBoundariesInParents = true; 174 } 175 176 // Invalidate all resources of this client if our layout changed. 177 if (everHadLayout() && selfNeedsLayout()) 178 SVGResourcesCache::clientLayoutChanged(*this); 179 180 // If our bounds changed, notify the parents. 181 if (updateCachedBoundariesInParents) 182 RenderSVGModelObject::setNeedsBoundariesUpdate(); 183 184 repainter.repaintAfterLayout(); 185 clearNeedsLayout(); 186} 187 188Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const 189{ 190 DEPRECATED_DEFINE_STATIC_LOCAL(Path, tempPath, ()); 191 192 tempPath = *path; 193 tempPath.transform(strokeTransform); 194 195 return &tempPath; 196} 197 198bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver) 199{ 200 if (!strokeTransform.isInvertible()) 201 return false; 202 203 stateSaver.save(); 204 stateSaver.context()->concatCTM(strokeTransform.inverse()); 205 return true; 206} 207 208AffineTransform RenderSVGShape::nonScalingStrokeTransform() const 209{ 210 return graphicsElement().getScreenCTM(SVGLocatable::DisallowStyleUpdate); 211} 212 213bool RenderSVGShape::shouldGenerateMarkerPositions() const 214{ 215 if (!style().svgStyle().hasMarkers()) 216 return false; 217 218 if (!graphicsElement().supportsMarkers()) 219 return false; 220 221 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*this); 222 if (!resources) 223 return false; 224 225 return resources->markerStart() || resources->markerMid() || resources->markerEnd(); 226} 227 228void RenderSVGShape::fillShape(const RenderStyle& style, GraphicsContext* context) 229{ 230 Color fallbackColor; 231 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(*this, style, fallbackColor)) { 232 if (fillPaintingResource->applyResource(*this, style, context, ApplyToFillMode)) 233 fillPaintingResource->postApplyResource(*this, context, ApplyToFillMode, 0, this); 234 else if (fallbackColor.isValid()) { 235 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); 236 fallbackResource->setColor(fallbackColor); 237 if (fallbackResource->applyResource(*this, style, context, ApplyToFillMode)) 238 fallbackResource->postApplyResource(*this, context, ApplyToFillMode, 0, this); 239 } 240 } 241} 242 243void RenderSVGShape::strokeShape(const RenderStyle& style, GraphicsContext* context) 244{ 245 Color fallbackColor; 246 if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(*this, style, fallbackColor)) { 247 if (strokePaintingResource->applyResource(*this, style, context, ApplyToStrokeMode)) 248 strokePaintingResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this); 249 else if (fallbackColor.isValid()) { 250 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); 251 fallbackResource->setColor(fallbackColor); 252 if (fallbackResource->applyResource(*this, style, context, ApplyToStrokeMode)) 253 fallbackResource->postApplyResource(*this, context, ApplyToStrokeMode, 0, this); 254 } 255 } 256} 257 258void RenderSVGShape::strokeShape(GraphicsContext* context) 259{ 260 if (!style().svgStyle().hasVisibleStroke()) 261 return; 262 263 GraphicsContextStateSaver stateSaver(*context, false); 264 if (hasNonScalingStroke()) { 265 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); 266 if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver)) 267 return; 268 } 269 strokeShape(style(), context); 270} 271 272void RenderSVGShape::fillStrokeMarkers(PaintInfo& childPaintInfo) 273{ 274 Vector<PaintType> paintOrder = style().svgStyle().paintTypesForPaintOrder(); 275 for (unsigned i = 0; i < paintOrder.size(); ++i) { 276 switch (paintOrder.at(i)) { 277 case PaintTypeFill: 278 fillShape(style(), childPaintInfo.context); 279 break; 280 case PaintTypeStroke: 281 strokeShape(childPaintInfo.context); 282 break; 283 case PaintTypeMarkers: 284 if (!m_markerPositions.isEmpty()) 285 drawMarkers(childPaintInfo); 286 break; 287 } 288 } 289} 290 291void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&) 292{ 293 if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground 294 || style().visibility() == HIDDEN || isEmpty()) 295 return; 296 FloatRect boundingBox = repaintRectInLocalCoordinates(); 297 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo)) 298 return; 299 300 PaintInfo childPaintInfo(paintInfo); 301 GraphicsContextStateSaver stateSaver(*childPaintInfo.context); 302 childPaintInfo.applyTransform(m_localTransform); 303 304 if (childPaintInfo.phase == PaintPhaseForeground) { 305 SVGRenderingContext renderingContext(*this, childPaintInfo); 306 307 if (renderingContext.isRenderingPrepared()) { 308 const SVGRenderStyle& svgStyle = style().svgStyle(); 309 if (svgStyle.shapeRendering() == SR_CRISPEDGES) 310 childPaintInfo.context->setShouldAntialias(false); 311 312 fillStrokeMarkers(childPaintInfo); 313 } 314 } 315 316 if (style().outlineWidth()) 317 paintOutline(childPaintInfo, IntRect(boundingBox)); 318} 319 320// This method is called from inside paintOutline() since we call paintOutline() 321// while transformed to our coord system, return local coords 322void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) 323{ 324 IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); 325 if (!rect.isEmpty()) 326 rects.append(rect); 327} 328 329bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) 330{ 331 // We only draw in the forground phase, so we only hit-test then. 332 if (hitTestAction != HitTestForeground) 333 return false; 334 335 FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent); 336 337 if (!SVGRenderSupport::pointInClippingArea(*this, localPoint)) 338 return false; 339 340 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style().pointerEvents()); 341 bool isVisible = (style().visibility() == VISIBLE); 342 if (isVisible || !hitRules.requireVisible) { 343 const SVGRenderStyle& svgStyle = style().svgStyle(); 344 WindRule fillRule = svgStyle.fillRule(); 345 if (request.svgClipContent()) 346 fillRule = svgStyle.clipRule(); 347 if ((hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke)) 348 || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) { 349 updateHitTestResult(result, roundedLayoutPoint(localPoint)); 350 return true; 351 } 352 } 353 return false; 354} 355 356static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd) 357{ 358 switch (type) { 359 case StartMarker: 360 return markerStart; 361 case MidMarker: 362 return markerMid; 363 case EndMarker: 364 return markerEnd; 365 } 366 367 ASSERT_NOT_REACHED(); 368 return 0; 369} 370 371FloatRect RenderSVGShape::markerRect(float strokeWidth) const 372{ 373 ASSERT(!m_markerPositions.isEmpty()); 374 375 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*this); 376 ASSERT(resources); 377 378 RenderSVGResourceMarker* markerStart = resources->markerStart(); 379 RenderSVGResourceMarker* markerMid = resources->markerMid(); 380 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); 381 ASSERT(markerStart || markerMid || markerEnd); 382 383 FloatRect boundaries; 384 unsigned size = m_markerPositions.size(); 385 for (unsigned i = 0; i < size; ++i) { 386 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd)) 387 boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth))); 388 } 389 return boundaries; 390} 391 392FloatRect RenderSVGShape::calculateObjectBoundingBox() const 393{ 394 return path().fastBoundingRect(); 395} 396 397FloatRect RenderSVGShape::calculateStrokeBoundingBox() const 398{ 399 ASSERT(m_path); 400 FloatRect strokeBoundingBox = m_fillBoundingBox; 401 402 const SVGRenderStyle& svgStyle = style().svgStyle(); 403 if (svgStyle.hasStroke()) { 404 BoundingRectStrokeStyleApplier strokeStyle(*this); 405 if (hasNonScalingStroke()) { 406 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); 407 if (nonScalingTransform.isInvertible()) { 408 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); 409 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle); 410 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect); 411 strokeBoundingBox.unite(strokeBoundingRect); 412 } 413 } else 414 strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle)); 415 } 416 417 if (!m_markerPositions.isEmpty()) 418 strokeBoundingBox.unite(markerRect(strokeWidth())); 419 420 return strokeBoundingBox; 421} 422 423void RenderSVGShape::updateRepaintBoundingBox() 424{ 425 m_repaintBoundingBoxExcludingShadow = strokeBoundingBox(); 426 SVGRenderSupport::intersectRepaintRectWithResources(*this, m_repaintBoundingBoxExcludingShadow); 427 428 m_repaintBoundingBox = m_repaintBoundingBoxExcludingShadow; 429 SVGRenderSupport::intersectRepaintRectWithShadows(*this, m_repaintBoundingBox); 430} 431 432float RenderSVGShape::strokeWidth() const 433{ 434 SVGLengthContext lengthContext(&graphicsElement()); 435 return style().svgStyle().strokeWidth().value(lengthContext); 436} 437 438bool RenderSVGShape::hasSmoothStroke() const 439{ 440 const SVGRenderStyle& svgStyle = style().svgStyle(); 441 return svgStyle.strokeDashArray().isEmpty() 442 && svgStyle.strokeMiterLimit() == svgStyle.initialStrokeMiterLimit() 443 && svgStyle.joinStyle() == svgStyle.initialJoinStyle() 444 && svgStyle.capStyle() == svgStyle.initialCapStyle(); 445} 446 447void RenderSVGShape::drawMarkers(PaintInfo& paintInfo) 448{ 449 ASSERT(!m_markerPositions.isEmpty()); 450 451 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*this); 452 if (!resources) 453 return; 454 455 RenderSVGResourceMarker* markerStart = resources->markerStart(); 456 RenderSVGResourceMarker* markerMid = resources->markerMid(); 457 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); 458 if (!markerStart && !markerMid && !markerEnd) 459 return; 460 461 float strokeWidth = this->strokeWidth(); 462 unsigned size = m_markerPositions.size(); 463 for (unsigned i = 0; i < size; ++i) { 464 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd)) 465 marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)); 466 } 467} 468 469void RenderSVGShape::processMarkerPositions() 470{ 471 m_markerPositions.clear(); 472 473 if (!shouldGenerateMarkerPositions()) 474 return; 475 476 ASSERT(m_path); 477 478 SVGMarkerData markerData(m_markerPositions); 479 m_path->apply(&markerData, SVGMarkerData::updateFromPathElement); 480 markerData.pathIsDone(); 481} 482 483} 484