1 /* 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2008 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2005, 2007 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Google, Inc. 6 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 8 * Copyright (C) 2009 Jeff Schiller <codedread (at) gmail.com> 9 * Copyright (C) 2011 Renata Hodovan <reni (at) 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 #include "core/rendering/svg/RenderSVGShape.h" 31 32 #include "core/platform/graphics/FloatPoint.h" 33 #include "core/platform/graphics/GraphicsContextStateSaver.h" 34 #include "core/rendering/HitTestRequest.h" 35 #include "core/rendering/LayoutRepainter.h" 36 #include "core/rendering/PointerEventsHitRules.h" 37 #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" 43 #include "core/svg/SVGGraphicsElement.h" 44 #include "wtf/MathExtras.h" 45 46 namespace WebCore { 47 48 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node) 49 : RenderSVGModelObject(node) 50 , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning. 51 , 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. 53 { 54 } 55 56 RenderSVGShape::~RenderSVGShape() 57 { 58 } 59 60 void RenderSVGShape::updateShapeFromElement() 61 { 62 m_path.clear(); 63 m_path = adoptPtr(new Path); 64 ASSERT(RenderSVGShape::isEmpty()); 65 66 SVGGraphicsElement* element = toSVGGraphicsElement(node()); 67 updatePathFromGraphicsElement(element, path()); 68 processMarkerPositions(); 69 70 m_fillBoundingBox = calculateObjectBoundingBox(); 71 m_strokeBoundingBox = calculateStrokeBoundingBox(); 72 } 73 74 bool RenderSVGShape::isEmpty() const 75 { 76 return path().isEmpty(); 77 } 78 79 void RenderSVGShape::fillShape(GraphicsContext* context) const 80 { 81 context->fillPath(path()); 82 } 83 84 void 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 95 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point) 96 { 97 ASSERT(m_path); 98 StrokeData strokeData; 99 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this); 100 101 if (hasNonScalingStroke()) { 102 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); 103 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); 104 105 return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData); 106 } 107 108 return m_path->strokeContains(point, strokeData); 109 } 110 111 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const 112 { 113 return path().contains(point, fillRule); 114 } 115 116 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule) 117 { 118 if (!m_fillBoundingBox.contains(point)) 119 return false; 120 121 StyleColor fallbackColor; 122 if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor)) 123 return false; 124 125 return shapeDependentFillContains(point, fillRule); 126 } 127 128 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke) 129 { 130 if (!strokeBoundingBox().contains(point)) 131 return false; 132 133 StyleColor fallbackColor; 134 if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor)) 135 return false; 136 137 return shapeDependentStrokeContains(point); 138 } 139 140 void RenderSVGShape::layout() 141 { 142 StackStats::LayoutCheckPoint layoutCheckPoint; 143 LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout()); 144 SVGGraphicsElement* element = toSVGGraphicsElement(node()); 145 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 clearNeedsLayout(); 172 } 173 174 Path* 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 184 bool 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 194 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const 195 { 196 SVGGraphicsElement* element = toSVGGraphicsElement(node()); 197 return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate); 198 } 199 200 bool RenderSVGShape::shouldGenerateMarkerPositions() const 201 { 202 if (!style()->svgStyle()->hasMarkers()) 203 return false; 204 205 SVGGraphicsElement* element = toSVGGraphicsElement(node()); 206 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 216 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context) 217 { 218 StyleColor fallbackColor; 219 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(); 224 fallbackResource->setColor(fallbackColor.color()); 225 if (fallbackResource->applyResource(this, style, context, ApplyToFillMode)) 226 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this); 227 } 228 } 229 } 230 231 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context) 232 { 233 StyleColor fallbackColor; 234 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(); 239 fallbackResource->setColor(fallbackColor.color()); 240 if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode)) 241 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this); 242 } 243 } 244 } 245 246 void 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 266 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&) 267 { 268 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); 269 270 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 303 void 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 310 bool 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 337 static 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 352 FloatRect 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 373 FloatRect RenderSVGShape::calculateObjectBoundingBox() const 374 { 375 return path().boundingRect(); 376 } 377 378 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const 379 { 380 ASSERT(m_path); 381 FloatRect strokeBoundingBox = m_fillBoundingBox; 382 383 if (style()->svgStyle()->hasStroke()) { 384 StrokeData strokeData; 385 SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this); 386 if (hasNonScalingStroke()) { 387 AffineTransform nonScalingTransform = nonScalingStrokeTransform(); 388 if (nonScalingTransform.isInvertible()) { 389 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); 390 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData); 391 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect); 392 strokeBoundingBox.unite(strokeBoundingRect); 393 } 394 } else { 395 strokeBoundingBox.unite(path().strokeBoundingRect(strokeData)); 396 } 397 } 398 399 if (!m_markerPositions.isEmpty()) 400 strokeBoundingBox.unite(markerRect(strokeWidth())); 401 402 return strokeBoundingBox; 403 } 404 405 void RenderSVGShape::updateRepaintBoundingBox() 406 { 407 m_repaintBoundingBox = strokeBoundingBox(); 408 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox); 409 } 410 411 float RenderSVGShape::strokeWidth() const 412 { 413 SVGElement* svgElement = toSVGElement(node()); 414 SVGLengthContext lengthContext(svgElement); 415 return style()->svgStyle()->strokeWidth().value(lengthContext); 416 } 417 418 bool RenderSVGShape::hasSmoothStroke() const 419 { 420 const SVGRenderStyle* svgStyle = style()->svgStyle(); 421 return svgStyle->strokeDashArray().isEmpty() 422 && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit() 423 && svgStyle->joinStyle() == svgStyle->initialJoinStyle() 424 && svgStyle->capStyle() == svgStyle->initialCapStyle(); 425 } 426 427 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo) 428 { 429 ASSERT(!m_markerPositions.isEmpty()); 430 431 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); 432 if (!resources) 433 return; 434 435 RenderSVGResourceMarker* markerStart = resources->markerStart(); 436 RenderSVGResourceMarker* markerMid = resources->markerMid(); 437 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); 438 if (!markerStart && !markerMid && !markerEnd) 439 return; 440 441 float strokeWidth = this->strokeWidth(); 442 unsigned size = m_markerPositions.size(); 443 for (unsigned i = 0; i < size; ++i) { 444 if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd)) 445 marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)); 446 } 447 } 448 449 void RenderSVGShape::processMarkerPositions() 450 { 451 m_markerPositions.clear(); 452 453 if (!shouldGenerateMarkerPositions()) 454 return; 455 456 ASSERT(m_path); 457 458 SVGMarkerData markerData(m_markerPositions); 459 m_path->apply(&markerData, SVGMarkerData::updateFromPathElement); 460 markerData.pathIsDone(); 461 } 462 463 } 464