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 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Library General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Library General Public License for more details. 19 * 20 * You should have received a copy of the GNU Library General Public License 21 * along with this library; see the file COPYING.LIB. If not, write to 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 * Boston, MA 02110-1301, USA. 24 */ 25 26 #include "config.h" 27 28 #if ENABLE(SVG) 29 #include "RenderSVGPath.h" 30 31 #include "FloatPoint.h" 32 #include "FloatQuad.h" 33 #include "GraphicsContext.h" 34 #include "HitTestRequest.h" 35 #include "PointerEventsHitRules.h" 36 #include "RenderSVGContainer.h" 37 #include "RenderSVGResourceMarker.h" 38 #include "RenderSVGResourceSolidColor.h" 39 #include "SVGRenderSupport.h" 40 #include "SVGResources.h" 41 #include "SVGStyledTransformableElement.h" 42 #include "SVGTransformList.h" 43 #include "SVGURIReference.h" 44 #include "StrokeStyleApplier.h" 45 #include <wtf/MathExtras.h> 46 47 namespace WebCore { 48 49 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier { 50 public: 51 BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style) 52 : m_object(object) 53 , m_style(style) 54 { 55 ASSERT(style); 56 ASSERT(object); 57 } 58 59 void strokeStyle(GraphicsContext* gc) 60 { 61 SVGRenderSupport::applyStrokeStyleToContext(gc, m_style, m_object); 62 } 63 64 private: 65 const RenderObject* m_object; 66 RenderStyle* m_style; 67 }; 68 69 RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node) 70 : RenderSVGModelObject(node) 71 , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning 72 , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement 73 , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement 74 { 75 } 76 77 RenderSVGPath::~RenderSVGPath() 78 { 79 } 80 81 bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule) 82 { 83 if (!m_fillBoundingBox.contains(point)) 84 return false; 85 86 Color fallbackColor; 87 if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor)) 88 return false; 89 90 return m_path.contains(point, fillRule); 91 } 92 93 bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke) 94 { 95 if (!m_strokeAndMarkerBoundingBox.contains(point)) 96 return false; 97 98 Color fallbackColor; 99 if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor)) 100 return false; 101 102 BoundingRectStrokeStyleApplier strokeStyle(this, style()); 103 return m_path.strokeContains(&strokeStyle, point); 104 } 105 106 void RenderSVGPath::layout() 107 { 108 LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout()); 109 SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); 110 111 bool updateCachedBoundariesInParents = false; 112 113 bool needsPathUpdate = m_needsPathUpdate; 114 if (needsPathUpdate) { 115 m_path.clear(); 116 element->toPathData(m_path); 117 m_needsPathUpdate = false; 118 updateCachedBoundariesInParents = true; 119 } 120 121 if (m_needsTransformUpdate) { 122 m_localTransform = element->animatedLocalTransform(); 123 m_needsTransformUpdate = false; 124 updateCachedBoundariesInParents = true; 125 } 126 127 if (m_needsBoundariesUpdate) 128 updateCachedBoundariesInParents = true; 129 130 // Invalidate all resources of this client if our layout changed. 131 if (m_everHadLayout && selfNeedsLayout()) { 132 SVGResourcesCache::clientLayoutChanged(this); 133 m_markerLayoutInfo.clear(); 134 } 135 136 // At this point LayoutRepainter already grabbed the old bounds, 137 // recalculate them now so repaintAfterLayout() uses the new bounds. 138 if (needsPathUpdate || m_needsBoundariesUpdate) { 139 updateCachedBoundaries(); 140 m_needsBoundariesUpdate = false; 141 } 142 143 // If our bounds changed, notify the parents. 144 if (updateCachedBoundariesInParents) 145 RenderSVGModelObject::setNeedsBoundariesUpdate(); 146 147 repainter.repaintAfterLayout(); 148 setNeedsLayout(false); 149 } 150 151 void RenderSVGPath::fillAndStrokePath(GraphicsContext* context) 152 { 153 RenderStyle* style = this->style(); 154 155 Color fallbackColor; 156 if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) { 157 if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode)) 158 fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path); 159 else if (fallbackColor.isValid()) { 160 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); 161 fallbackResource->setColor(fallbackColor); 162 if (fallbackResource->applyResource(this, style, context, ApplyToFillMode)) 163 fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path); 164 } 165 } 166 167 fallbackColor = Color(); 168 RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor); 169 if (!strokePaintingResource) 170 return; 171 172 Path path; 173 174 bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE; 175 bool restoreContext = false; 176 if (nonScalingStroke) { 177 SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node()); 178 AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate); 179 if (!nonScalingStrokeTransform.isInvertible()) 180 return; 181 182 path = m_path; 183 path.transform(nonScalingStrokeTransform); 184 185 context->save(); 186 context->concatCTM(nonScalingStrokeTransform.inverse()); 187 restoreContext = true; 188 } 189 190 if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode)) 191 strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path); 192 else if (fallbackColor.isValid()) { 193 RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource(); 194 fallbackResource->setColor(fallbackColor); 195 if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode)) 196 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path); 197 } 198 199 if (restoreContext) 200 context->restore(); 201 } 202 203 void RenderSVGPath::paint(PaintInfo& paintInfo, int, int) 204 { 205 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) 206 return; 207 208 FloatRect boundingBox = repaintRectInLocalCoordinates(); 209 if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo)) 210 return; 211 212 PaintInfo childPaintInfo(paintInfo); 213 bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline); 214 if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) { 215 childPaintInfo.context->save(); 216 childPaintInfo.applyTransform(m_localTransform); 217 218 if (childPaintInfo.phase == PaintPhaseForeground) { 219 PaintInfo savedInfo(childPaintInfo); 220 221 if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) { 222 const SVGRenderStyle* svgStyle = style()->svgStyle(); 223 if (svgStyle->shapeRendering() == SR_CRISPEDGES) 224 childPaintInfo.context->setShouldAntialias(false); 225 226 fillAndStrokePath(childPaintInfo.context); 227 228 if (svgStyle->hasMarkers()) 229 m_markerLayoutInfo.drawMarkers(childPaintInfo); 230 } 231 232 SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context); 233 } 234 235 if (drawsOutline) 236 paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), 237 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height())); 238 239 childPaintInfo.context->restore(); 240 } 241 } 242 243 // This method is called from inside paintOutline() since we call paintOutline() 244 // while transformed to our coord system, return local coords 245 void RenderSVGPath::addFocusRingRects(Vector<IntRect>& rects, int, int) 246 { 247 IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates()); 248 if (!rect.isEmpty()) 249 rects.append(rect); 250 } 251 252 bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) 253 { 254 // We only draw in the forground phase, so we only hit-test then. 255 if (hitTestAction != HitTestForeground) 256 return false; 257 258 FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent); 259 260 if (!SVGRenderSupport::pointInClippingArea(this, localPoint)) 261 return false; 262 263 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents()); 264 bool isVisible = (style()->visibility() == VISIBLE); 265 if (isVisible || !hitRules.requireVisible) { 266 const SVGRenderStyle* svgStyle = style()->svgStyle(); 267 WindRule fillRule = svgStyle->fillRule(); 268 if (request.svgClipContent()) 269 fillRule = svgStyle->clipRule(); 270 if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke)) 271 || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) { 272 updateHitTestResult(result, roundedIntPoint(localPoint)); 273 return true; 274 } 275 } 276 return false; 277 } 278 279 FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded() 280 { 281 SVGElement* svgElement = static_cast<SVGElement*>(node()); 282 ASSERT(svgElement && svgElement->document()); 283 if (!svgElement->isStyled()) 284 return FloatRect(); 285 286 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); 287 if (!styledElement->supportsMarkers()) 288 return FloatRect(); 289 290 const SVGRenderStyle* svgStyle = style()->svgStyle(); 291 ASSERT(svgStyle->hasMarkers()); 292 293 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); 294 if (!resources) 295 return FloatRect(); 296 297 RenderSVGResourceMarker* markerStart = resources->markerStart(); 298 RenderSVGResourceMarker* markerMid = resources->markerMid(); 299 RenderSVGResourceMarker* markerEnd = resources->markerEnd(); 300 if (!markerStart && !markerMid && !markerEnd) 301 return FloatRect(); 302 303 return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path); 304 } 305 306 void RenderSVGPath::updateCachedBoundaries() 307 { 308 if (m_path.isEmpty()) { 309 m_fillBoundingBox = FloatRect(); 310 m_strokeAndMarkerBoundingBox = FloatRect(); 311 m_repaintBoundingBox = FloatRect(); 312 return; 313 } 314 315 // Cache _unclipped_ fill bounding box, used for calculations in resources 316 m_fillBoundingBox = m_path.boundingRect(); 317 318 // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries) 319 m_strokeAndMarkerBoundingBox = m_fillBoundingBox; 320 321 const SVGRenderStyle* svgStyle = style()->svgStyle(); 322 if (svgStyle->hasStroke()) { 323 BoundingRectStrokeStyleApplier strokeStyle(this, style()); 324 m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle)); 325 } 326 327 if (svgStyle->hasMarkers()) { 328 FloatRect markerBounds = calculateMarkerBoundsIfNeeded(); 329 if (!markerBounds.isEmpty()) 330 m_strokeAndMarkerBoundingBox.unite(markerBounds); 331 } 332 333 // Cache smallest possible repaint rectangle 334 m_repaintBoundingBox = m_strokeAndMarkerBoundingBox; 335 SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox); 336 } 337 338 } 339 340 #endif // ENABLE(SVG) 341