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