1/* 2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25#include "config.h" 26 27#if ENABLE(SVG) 28#include "SVGRenderSupport.h" 29 30#include "NodeRenderStyle.h" 31#include "RenderGeometryMap.h" 32#include "RenderLayer.h" 33#include "RenderSVGResource.h" 34#include "RenderSVGResourceClipper.h" 35#include "RenderSVGResourceFilter.h" 36#include "RenderSVGResourceMarker.h" 37#include "RenderSVGResourceMasker.h" 38#include "RenderSVGRoot.h" 39#include "RenderSVGText.h" 40#include "RenderSVGViewportContainer.h" 41#include "SVGResources.h" 42#include "SVGResourcesCache.h" 43#include "SVGStyledElement.h" 44#include "TransformState.h" 45 46namespace WebCore { 47 48FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderObject* object) 49{ 50 // FIXME: Add support for RenderSVGBlock. 51 52 // FIXME: This should use a safer cast such as toRenderSVGModelObject(). 53 if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) 54 return static_cast<const RenderSVGModelObject*>(object)->repaintRectInLocalCoordinatesExcludingSVGShadow(); 55 56 return object->repaintRectInLocalCoordinates(); 57} 58 59LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer) 60{ 61 // Return early for any cases where we don't actually paint 62 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent()) 63 return LayoutRect(); 64 65 // Pass our local paint rect to computeRectForRepaint() which will 66 // map to parent coords and recurse up the parent chain. 67 FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(object); 68 const SVGRenderStyle* svgStyle = object->style()->svgStyle(); 69 if (const ShadowData* shadow = svgStyle->shadow()) 70 shadow->adjustRectForShadow(repaintRect); 71 object->computeFloatRectForRepaint(repaintContainer, repaintRect); 72 return enclosingLayoutRect(repaintRect); 73} 74 75void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) 76{ 77 const SVGRenderStyle* svgStyle = object->style()->svgStyle(); 78 if (const ShadowData* shadow = svgStyle->shadow()) 79 shadow->adjustRectForShadow(repaintRect); 80 repaintRect.inflate(object->style()->outlineWidth()); 81 82 // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent. 83 repaintRect = object->localToParentTransform().mapRect(repaintRect); 84 object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed); 85} 86 87void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed) 88{ 89 transformState.applyTransform(object->localToParentTransform()); 90 91 RenderObject* parent = object->parent(); 92 93 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 94 // to map an element from SVG viewport coordinates to CSS box coordinates. 95 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 96 if (parent->isSVGRoot()) 97 transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform()); 98 99 MapCoordinatesFlags mode = UseTransforms; 100 parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); 101} 102 103const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) 104{ 105 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object); 106 107 RenderObject* parent = object->parent(); 108 109 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 110 // to map an element from SVG viewport coordinates to CSS box coordinates. 111 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 112 if (parent->isSVGRoot()) { 113 TransformationMatrix matrix(object->localToParentTransform()); 114 matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform()); 115 geometryMap.push(object, matrix); 116 } else 117 geometryMap.push(object, object->localToParentTransform()); 118 119 return parent; 120} 121 122bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object) 123{ 124 if (!object->checkForRepaintDuringLayout()) 125 return false; 126 // When a parent container is transformed in SVG, all children will be painted automatically 127 // so we are able to skip redundant repaint checks. 128 RenderObject* parent = object->parent(); 129 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate()); 130} 131 132// Update a bounding box taking into account the validity of the other bounding box. 133static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) 134{ 135 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; 136 if (!otherValid) 137 return; 138 139 if (!objectBoundingBoxValid) { 140 objectBoundingBox = otherBoundingBox; 141 objectBoundingBoxValid = true; 142 return; 143 } 144 145 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); 146} 147 148void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) 149{ 150 objectBoundingBox = FloatRect(); 151 objectBoundingBoxValid = false; 152 strokeBoundingBox = FloatRect(); 153 154 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes 155 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound 156 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. 157 for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) { 158 if (current->isSVGHiddenContainer()) 159 continue; 160 161 const AffineTransform& transform = current->localToParentTransform(); 162 if (transform.isIdentity()) { 163 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox()); 164 strokeBoundingBox.unite(current->repaintRectInLocalCoordinates()); 165 } else { 166 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); 167 strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); 168 } 169 } 170 171 repaintBoundingBox = strokeBoundingBox; 172} 173 174bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) 175{ 176 if (localTransform.isIdentity()) 177 return localRepaintRect.intersects(paintInfo.rect); 178 179 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); 180} 181 182const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start) 183{ 184 while (start && !start->isSVGRoot()) 185 start = start->parent(); 186 187 ASSERT(start); 188 ASSERT(start->isSVGRoot()); 189 return toRenderSVGRoot(start); 190} 191 192static inline void invalidateResourcesOfChildren(RenderObject* start) 193{ 194 ASSERT(!start->needsLayout()); 195 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) 196 resources->removeClientFromCache(start, false); 197 198 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) 199 invalidateResourcesOfChildren(child); 200} 201 202static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start) 203{ 204 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) 205 start = start->parent(); 206 207 ASSERT(start); 208 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); 209 if (start->isSVGViewportContainer()) 210 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); 211 212 return toRenderSVGRoot(start)->isLayoutSizeChanged(); 213} 214 215bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor) 216{ 217 while (ancestor && !ancestor->isSVGRoot()) { 218 if (ancestor->isSVGTransformableContainer()) 219 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate(); 220 if (ancestor->isSVGViewportContainer()) 221 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate(); 222 ancestor = ancestor->parent(); 223 } 224 225 return false; 226} 227 228void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) 229{ 230 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); 231 bool transformChanged = transformToRootChanged(start); 232 bool hasSVGShadow = rendererHasSVGShadow(start); 233 bool needsBoundariesUpdate = start->needsBoundariesUpdate(); 234 HashSet<RenderObject*> notlayoutedObjects; 235 236 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 237 bool needsLayout = selfNeedsLayout; 238 bool childEverHadLayout = child->everHadLayout(); 239 240 if (needsBoundariesUpdate && hasSVGShadow) { 241 // If we have a shadow, our shadow is baked into our children's cached boundaries, 242 // so they need to update. 243 child->setNeedsBoundariesUpdate(); 244 needsLayout = true; 245 } 246 247 if (transformChanged) { 248 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). 249 if (child->isSVGText()) 250 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 251 needsLayout = true; 252 } 253 254 if (layoutSizeChanged) { 255 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths 256 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { 257 if (element->isSVGStyledElement() && toSVGStyledElement(element)->hasRelativeLengths()) { 258 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object 259 if (child->isSVGShape()) 260 toRenderSVGShape(child)->setNeedsShapeUpdate(); 261 else if (child->isSVGText()) { 262 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 263 toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); 264 } 265 266 needsLayout = true; 267 } 268 } 269 } 270 271 if (needsLayout) 272 child->setNeedsLayout(true, MarkOnlyThis); 273 274 if (child->needsLayout()) { 275 child->layout(); 276 // Renderers are responsible for repainting themselves when changing, except 277 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. 278 // We could handle this in the individual objects, but for now it's easier to have 279 // parent containers call repaint(). (RenderBlock::layout* has similar logic.) 280 if (!childEverHadLayout) 281 child->repaint(); 282 } else if (layoutSizeChanged) 283 notlayoutedObjects.add(child); 284 285 ASSERT(!child->needsLayout()); 286 } 287 288 if (!layoutSizeChanged) { 289 ASSERT(notlayoutedObjects.isEmpty()); 290 return; 291 } 292 293 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. 294 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); 295 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) 296 invalidateResourcesOfChildren(*it); 297} 298 299bool SVGRenderSupport::isOverflowHidden(const RenderObject* object) 300{ 301 // SVG doesn't support independent x/y overflow 302 ASSERT(object->style()->overflowX() == object->style()->overflowY()); 303 304 // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle 305 ASSERT(object->style()->overflowX() != OSCROLL); 306 307 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 308 ASSERT(!object->isRoot()); 309 310 return object->style()->overflowX() == OHIDDEN; 311} 312 313bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject* object) 314{ 315 // FIXME: Add support for RenderSVGBlock. 316 317 // FIXME: This should use a safer cast such as toRenderSVGModelObject(). 318 if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) 319 return static_cast<const RenderSVGModelObject*>(object)->hasSVGShadow(); 320 321 if (object->isSVGRoot()) 322 return toRenderSVGRoot(object)->hasSVGShadow(); 323 324 return false; 325} 326 327void SVGRenderSupport::setRendererHasSVGShadow(RenderObject* object, bool hasShadow) 328{ 329 // FIXME: Add support for RenderSVGBlock. 330 331 // FIXME: This should use a safer cast such as toRenderSVGModelObject(). 332 if (object->isSVGShape() || object->isSVGImage() || object->isSVGContainer()) 333 return static_cast<RenderSVGModelObject*>(object)->setHasSVGShadow(hasShadow); 334 335 if (object->isSVGRoot()) 336 return static_cast<RenderSVGRoot*>(object)->setHasSVGShadow(hasShadow); 337} 338 339void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderObject* object, FloatRect& repaintRect) 340{ 341 // Since -webkit-svg-shadow enables shadow drawing for its children, but its children 342 // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for 343 // shadows in order to correctly compute our repaint rect. 344 345 ASSERT(object); 346 347 const RenderObject* currentObject = object; 348 349 AffineTransform localToRootTransform; 350 351 while (currentObject && rendererHasSVGShadow(currentObject)) { 352 RenderStyle* style = currentObject->style(); 353 if (style) { 354 const SVGRenderStyle* svgStyle = style->svgStyle(); 355 if (svgStyle) { 356 if (const ShadowData* shadow = svgStyle->shadow()) 357 shadow->adjustRectForShadow(repaintRect); 358 } 359 } 360 361 repaintRect = currentObject->localToParentTransform().mapRect(repaintRect); 362 localToRootTransform *= currentObject->localToParentTransform(); 363 364 currentObject = currentObject->parent(); 365 }; 366 367 if (localToRootTransform.isIdentity()) 368 return; 369 370 AffineTransform rootToLocalTransform = localToRootTransform.inverse(); 371 repaintRect = rootToLocalTransform.mapRect(repaintRect); 372} 373 374void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect) 375{ 376 ASSERT(object); 377 378 RenderObject* renderer = const_cast<RenderObject*>(object); 379 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 380 if (!resources) 381 return; 382 383#if ENABLE(FILTERS) 384 if (RenderSVGResourceFilter* filter = resources->filter()) 385 repaintRect = filter->resourceBoundingBox(renderer); 386#endif 387 388 if (RenderSVGResourceClipper* clipper = resources->clipper()) 389 repaintRect.intersect(clipper->resourceBoundingBox(renderer)); 390 391 if (RenderSVGResourceMasker* masker = resources->masker()) 392 repaintRect.intersect(masker->resourceBoundingBox(renderer)); 393} 394 395bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object) 396{ 397 // If any of this container's children need to be laid out, and a filter is applied 398 // to the container, we need to repaint the entire container. 399 if (!object->normalChildNeedsLayout()) 400 return false; 401 402 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 403 if (!resources || !resources->filter()) 404 return false; 405 406 return true; 407} 408 409bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point) 410{ 411 ASSERT(object); 412 413 // We just take clippers into account to determine if a point is on the node. The Specification may 414 // change later and we also need to check maskers. 415 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 416 if (!resources) 417 return true; 418 419 if (RenderSVGResourceClipper* clipper = resources->clipper()) 420 return clipper->hitTestClipContent(object->objectBoundingBox(), point); 421 422 return true; 423} 424 425void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) 426{ 427 ASSERT(context); 428 ASSERT(style); 429 ASSERT(object); 430 ASSERT(object->node()); 431 ASSERT(object->node()->isSVGElement()); 432 433 const SVGRenderStyle* svgStyle = style->svgStyle(); 434 ASSERT(svgStyle); 435 436 SVGLengthContext lengthContext(toSVGElement(object->node())); 437 context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); 438 context->setLineCap(svgStyle->capStyle()); 439 context->setLineJoin(svgStyle->joinStyle()); 440 if (svgStyle->joinStyle() == MiterJoin) 441 context->setMiterLimit(svgStyle->strokeMiterLimit()); 442 443 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); 444 if (dashes.isEmpty()) 445 context->setStrokeStyle(SolidStroke); 446 else { 447 DashArray dashArray; 448 const Vector<SVGLength>::const_iterator end = dashes.end(); 449 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) 450 dashArray.append((*it).value(lengthContext)); 451 452 context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); 453 } 454} 455 456void SVGRenderSupport::childAdded(RenderObject* parent, RenderObject* child) 457{ 458 SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child)); 459} 460 461void SVGRenderSupport::styleChanged(RenderObject* object) 462{ 463 RenderObject* parent = object->parent(); 464 SVGRenderSupport::setRendererHasSVGShadow(object, (parent && SVGRenderSupport::rendererHasSVGShadow(parent)) || (object->style()->svgStyle() && object->style()->svgStyle()->shadow())); 465} 466 467} 468 469#endif 470