1 /* 2 * Copyright (C) 2007, 2008 Rob Buis <buis (at) kde.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2009 Dirk Schulze <krit (at) 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 #include "core/rendering/svg/SVGRenderSupport.h" 27 28 #include "core/rendering/PaintInfo.h" 29 #include "core/rendering/RenderGeometryMap.h" 30 #include "core/rendering/RenderLayer.h" 31 #include "core/rendering/SubtreeLayoutScope.h" 32 #include "core/rendering/svg/RenderSVGInlineText.h" 33 #include "core/rendering/svg/RenderSVGResourceClipper.h" 34 #include "core/rendering/svg/RenderSVGResourceFilter.h" 35 #include "core/rendering/svg/RenderSVGResourceMasker.h" 36 #include "core/rendering/svg/RenderSVGRoot.h" 37 #include "core/rendering/svg/RenderSVGText.h" 38 #include "core/rendering/svg/RenderSVGViewportContainer.h" 39 #include "core/rendering/svg/SVGResources.h" 40 #include "core/rendering/svg/SVGResourcesCache.h" 41 #include "core/svg/SVGElement.h" 42 #include "platform/geometry/TransformState.h" 43 44 namespace WebCore { 45 46 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer) 47 { 48 // Return early for any cases where we don't actually paint 49 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent()) 50 return LayoutRect(); 51 52 // Pass our local paint rect to computeRectForRepaint() which will 53 // map to parent coords and recurse up the parent chain. 54 FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates(); 55 object->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect); 56 return enclosingLayoutRect(repaintRect); 57 } 58 59 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) 60 { 61 repaintRect.inflate(object->style()->outlineWidth()); 62 63 // Translate to coords in our parent renderer, and then call computeFloatRectForPaintInvalidation() on our parent. 64 repaintRect = object->localToParentTransform().mapRect(repaintRect); 65 object->parent()->computeFloatRectForPaintInvalidation(repaintContainer, repaintRect, fixed); 66 } 67 68 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed) 69 { 70 transformState.applyTransform(object->localToParentTransform()); 71 72 RenderObject* parent = object->parent(); 73 74 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 75 // to map an element from SVG viewport coordinates to CSS box coordinates. 76 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 77 if (parent->isSVGRoot()) 78 transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform()); 79 80 MapCoordinatesFlags mode = UseTransforms; 81 parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); 82 } 83 84 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) 85 { 86 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object); 87 88 RenderObject* parent = object->parent(); 89 90 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 91 // to map an element from SVG viewport coordinates to CSS box coordinates. 92 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 93 if (parent->isSVGRoot()) { 94 TransformationMatrix matrix(object->localToParentTransform()); 95 matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform()); 96 geometryMap.push(object, matrix); 97 } else 98 geometryMap.push(object, object->localToParentTransform()); 99 100 return parent; 101 } 102 103 bool SVGRenderSupport::parentTransformDidChange(RenderObject* object) 104 { 105 // When a parent container is transformed in SVG, all children will be painted automatically 106 // so we are able to skip redundant repaint checks. 107 RenderObject* parent = object->parent(); 108 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate()); 109 } 110 111 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object) 112 { 113 if (!object->checkForPaintInvalidationDuringLayout()) 114 return false; 115 116 return parentTransformDidChange(object); 117 } 118 119 // Update a bounding box taking into account the validity of the other bounding box. 120 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) 121 { 122 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; 123 if (!otherValid) 124 return; 125 126 if (!objectBoundingBoxValid) { 127 objectBoundingBox = otherBoundingBox; 128 objectBoundingBoxValid = true; 129 return; 130 } 131 132 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); 133 } 134 135 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) 136 { 137 objectBoundingBox = FloatRect(); 138 objectBoundingBoxValid = false; 139 strokeBoundingBox = FloatRect(); 140 141 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes 142 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound 143 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. 144 for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) { 145 if (current->isSVGHiddenContainer()) 146 continue; 147 148 const AffineTransform& transform = current->localToParentTransform(); 149 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, 150 transform.mapRect(current->objectBoundingBox())); 151 strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates())); 152 } 153 154 repaintBoundingBox = strokeBoundingBox; 155 } 156 157 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) 158 { 159 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); 160 } 161 162 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start) 163 { 164 while (start && !start->isSVGRoot()) 165 start = start->parent(); 166 167 ASSERT(start); 168 ASSERT(start->isSVGRoot()); 169 return toRenderSVGRoot(start); 170 } 171 172 inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start) 173 { 174 ASSERT(!start->needsLayout()); 175 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) 176 resources->removeClientFromCache(start, false); 177 178 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) 179 invalidateResourcesOfChildren(child); 180 } 181 182 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start) 183 { 184 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) 185 start = start->parent(); 186 187 ASSERT(start); 188 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); 189 if (start->isSVGViewportContainer()) 190 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); 191 192 return toRenderSVGRoot(start)->isLayoutSizeChanged(); 193 } 194 195 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor) 196 { 197 while (ancestor && !ancestor->isSVGRoot()) { 198 if (ancestor->isSVGTransformableContainer()) 199 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate(); 200 if (ancestor->isSVGViewportContainer()) 201 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate(); 202 ancestor = ancestor->parent(); 203 } 204 205 return false; 206 } 207 208 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) 209 { 210 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); 211 bool transformChanged = transformToRootChanged(start); 212 HashSet<RenderObject*> notlayoutedObjects; 213 214 for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) { 215 bool needsLayout = selfNeedsLayout; 216 bool childEverHadLayout = child->everHadLayout(); 217 218 if (transformChanged) { 219 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). 220 if (child->isSVGText()) 221 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 222 needsLayout = true; 223 } 224 225 if (layoutSizeChanged) { 226 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths 227 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { 228 if (element->hasRelativeLengths()) { 229 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object 230 if (child->isSVGShape()) { 231 toRenderSVGShape(child)->setNeedsShapeUpdate(); 232 } else if (child->isSVGText()) { 233 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 234 toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); 235 } 236 237 needsLayout = true; 238 } 239 } 240 } 241 242 SubtreeLayoutScope layoutScope(*child); 243 // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope. 244 // Since they only care about viewport size changes (to resolve their relative lengths), we trigger 245 // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher 246 // SubtreeLayoutScope (in RenderView::layout()). 247 if (needsLayout && !child->isSVGResourceContainer()) 248 layoutScope.setNeedsLayout(child); 249 250 layoutResourcesIfNeeded(child); 251 252 if (child->needsLayout()) { 253 child->layout(); 254 // Renderers are responsible for repainting themselves when changing, except 255 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. 256 // We could handle this in the individual objects, but for now it's easier to have 257 // parent containers call repaint(). (RenderBlock::layout* has similar logic.) 258 if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) 259 child->paintInvalidationForWholeRenderer(); 260 } else if (layoutSizeChanged) { 261 notlayoutedObjects.add(child); 262 } 263 } 264 265 if (!layoutSizeChanged) { 266 ASSERT(notlayoutedObjects.isEmpty()); 267 return; 268 } 269 270 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. 271 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); 272 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) 273 invalidateResourcesOfChildren(*it); 274 } 275 276 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object) 277 { 278 ASSERT(object); 279 280 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 281 if (resources) 282 resources->layoutIfNeeded(); 283 } 284 285 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object) 286 { 287 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 288 ASSERT(!object->isDocumentElement()); 289 290 return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL; 291 } 292 293 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect) 294 { 295 ASSERT(renderer); 296 297 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 298 if (!resources) 299 return; 300 301 if (RenderSVGResourceFilter* filter = resources->filter()) 302 repaintRect = filter->resourceBoundingBox(renderer); 303 304 if (RenderSVGResourceClipper* clipper = resources->clipper()) 305 repaintRect.intersect(clipper->resourceBoundingBox(renderer)); 306 307 if (RenderSVGResourceMasker* masker = resources->masker()) 308 repaintRect.intersect(masker->resourceBoundingBox(renderer)); 309 } 310 311 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object) 312 { 313 // If any of this container's children need to be laid out, and a filter is applied 314 // to the container, we need to repaint the entire container. 315 if (!object->normalChildNeedsLayout()) 316 return false; 317 318 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 319 if (!resources || !resources->filter()) 320 return false; 321 322 return true; 323 } 324 325 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point) 326 { 327 ASSERT(object); 328 329 // We just take clippers into account to determine if a point is on the node. The Specification may 330 // change later and we also need to check maskers. 331 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 332 if (!resources) 333 return true; 334 335 if (RenderSVGResourceClipper* clipper = resources->clipper()) 336 return clipper->hitTestClipContent(object->objectBoundingBox(), point); 337 338 return true; 339 } 340 341 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) 342 { 343 ASSERT(context); 344 ASSERT(style); 345 ASSERT(object); 346 ASSERT(object->node()); 347 ASSERT(object->node()->isSVGElement()); 348 349 const SVGRenderStyle* svgStyle = style->svgStyle(); 350 ASSERT(svgStyle); 351 352 SVGLengthContext lengthContext(toSVGElement(object->node())); 353 context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext)); 354 context->setLineCap(svgStyle->capStyle()); 355 context->setLineJoin(svgStyle->joinStyle()); 356 context->setMiterLimit(svgStyle->strokeMiterLimit()); 357 358 RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); 359 if (dashes->isEmpty()) 360 return; 361 362 DashArray dashArray; 363 SVGLengthList::ConstIterator it = dashes->begin(); 364 SVGLengthList::ConstIterator itEnd = dashes->end(); 365 for (; it != itEnd; ++it) 366 dashArray.append(it->value(lengthContext)); 367 368 context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); 369 } 370 371 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) 372 { 373 ASSERT(strokeData); 374 ASSERT(style); 375 ASSERT(object); 376 ASSERT(object->node()); 377 ASSERT(object->node()->isSVGElement()); 378 379 const SVGRenderStyle* svgStyle = style->svgStyle(); 380 ASSERT(svgStyle); 381 382 SVGLengthContext lengthContext(toSVGElement(object->node())); 383 strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext)); 384 strokeData->setLineCap(svgStyle->capStyle()); 385 strokeData->setLineJoin(svgStyle->joinStyle()); 386 strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); 387 388 RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray(); 389 if (dashes->isEmpty()) 390 return; 391 392 DashArray dashArray; 393 size_t length = dashes->length(); 394 for (size_t i = 0; i < length; ++i) 395 dashArray.append(dashes->at(i)->value(lengthContext)); 396 397 strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext)); 398 } 399 400 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object) 401 { 402 ASSERT(object->isText()); 403 // <br> is marked as text, but is not handled by the SVG rendering code-path. 404 return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText(); 405 } 406 407 } 408