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