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 27 #include "core/rendering/svg/SVGRenderSupport.h" 28 29 #include "core/platform/graphics/transforms/TransformState.h" 30 #include "core/rendering/RenderGeometryMap.h" 31 #include "core/rendering/RenderLayer.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 "wtf/UnusedParam.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->repaintRectInLocalCoordinates(); 55 object->computeFloatRectForRepaint(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 computeFloatRectForRepaint() on our parent. 64 repaintRect = object->localToParentTransform().mapRect(repaintRect); 65 object->parent()->computeFloatRectForRepaint(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::checkForSVGRepaintDuringLayout(RenderObject* object) 104 { 105 if (!object->checkForRepaintDuringLayout()) 106 return false; 107 // When a parent container is transformed in SVG, all children will be painted automatically 108 // so we are able to skip redundant repaint checks. 109 RenderObject* parent = object->parent(); 110 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate()); 111 } 112 113 // Update a bounding box taking into account the validity of the other bounding box. 114 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) 115 { 116 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; 117 if (!otherValid) 118 return; 119 120 if (!objectBoundingBoxValid) { 121 objectBoundingBox = otherBoundingBox; 122 objectBoundingBoxValid = true; 123 return; 124 } 125 126 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); 127 } 128 129 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) 130 { 131 objectBoundingBox = FloatRect(); 132 objectBoundingBoxValid = false; 133 strokeBoundingBox = FloatRect(); 134 135 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes 136 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound 137 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. 138 for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) { 139 if (current->isSVGHiddenContainer()) 140 continue; 141 142 const AffineTransform& transform = current->localToParentTransform(); 143 if (transform.isIdentity()) { 144 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox()); 145 strokeBoundingBox.unite(current->repaintRectInLocalCoordinates()); 146 } else { 147 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); 148 strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); 149 } 150 } 151 152 repaintBoundingBox = strokeBoundingBox; 153 } 154 155 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) 156 { 157 if (localTransform.isIdentity()) 158 return localRepaintRect.intersects(paintInfo.rect); 159 160 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); 161 } 162 163 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start) 164 { 165 while (start && !start->isSVGRoot()) 166 start = start->parent(); 167 168 ASSERT(start); 169 ASSERT(start->isSVGRoot()); 170 return toRenderSVGRoot(start); 171 } 172 173 static inline void invalidateResourcesOfChildren(RenderObject* start) 174 { 175 ASSERT(!start->needsLayout()); 176 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) 177 resources->removeClientFromCache(start, false); 178 179 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) 180 invalidateResourcesOfChildren(child); 181 } 182 183 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start) 184 { 185 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) 186 start = start->parent(); 187 188 ASSERT(start); 189 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); 190 if (start->isSVGViewportContainer()) 191 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); 192 193 return toRenderSVGRoot(start)->isLayoutSizeChanged(); 194 } 195 196 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor) 197 { 198 while (ancestor && !ancestor->isSVGRoot()) { 199 if (ancestor->isSVGTransformableContainer()) 200 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate(); 201 if (ancestor->isSVGViewportContainer()) 202 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate(); 203 ancestor = ancestor->parent(); 204 } 205 206 return false; 207 } 208 209 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) 210 { 211 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); 212 bool transformChanged = transformToRootChanged(start); 213 HashSet<RenderObject*> notlayoutedObjects; 214 215 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 216 bool needsLayout = selfNeedsLayout; 217 bool childEverHadLayout = child->everHadLayout(); 218 219 if (transformChanged) { 220 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). 221 if (child->isSVGText()) 222 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 223 needsLayout = true; 224 } 225 226 if (layoutSizeChanged) { 227 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths 228 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { 229 if (element->hasRelativeLengths()) { 230 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object 231 if (child->isSVGShape()) 232 toRenderSVGShape(child)->setNeedsShapeUpdate(); 233 else if (child->isSVGText()) { 234 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 235 toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); 236 } 237 238 needsLayout = true; 239 } 240 } 241 } 242 243 if (needsLayout) 244 child->setNeedsLayout(MarkOnlyThis); 245 246 if (child->needsLayout()) { 247 child->layout(); 248 // Renderers are responsible for repainting themselves when changing, except 249 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. 250 // We could handle this in the individual objects, but for now it's easier to have 251 // parent containers call repaint(). (RenderBlock::layout* has similar logic.) 252 if (!childEverHadLayout) 253 child->repaint(); 254 } else if (layoutSizeChanged) 255 notlayoutedObjects.add(child); 256 257 ASSERT(!child->needsLayout()); 258 } 259 260 if (!layoutSizeChanged) { 261 ASSERT(notlayoutedObjects.isEmpty()); 262 return; 263 } 264 265 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. 266 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); 267 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) 268 invalidateResourcesOfChildren(*it); 269 } 270 271 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object) 272 { 273 // SVG doesn't support independent x/y overflow 274 ASSERT(object->style()->overflowX() == object->style()->overflowY()); 275 276 // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle 277 ASSERT(object->style()->overflowX() != OSCROLL); 278 279 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 280 ASSERT(!object->isRoot()); 281 282 return object->style()->overflowX() == OHIDDEN; 283 } 284 285 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect) 286 { 287 ASSERT(object); 288 289 RenderObject* renderer = const_cast<RenderObject*>(object); 290 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 291 if (!resources) 292 return; 293 294 if (RenderSVGResourceFilter* filter = resources->filter()) 295 repaintRect = filter->resourceBoundingBox(renderer); 296 297 if (RenderSVGResourceClipper* clipper = resources->clipper()) 298 repaintRect.intersect(clipper->resourceBoundingBox(renderer)); 299 300 if (RenderSVGResourceMasker* masker = resources->masker()) 301 repaintRect.intersect(masker->resourceBoundingBox(renderer)); 302 } 303 304 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object) 305 { 306 // If any of this container's children need to be laid out, and a filter is applied 307 // to the container, we need to repaint the entire container. 308 if (!object->normalChildNeedsLayout()) 309 return false; 310 311 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 312 if (!resources || !resources->filter()) 313 return false; 314 315 return true; 316 } 317 318 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point) 319 { 320 ASSERT(object); 321 322 // We just take clippers into account to determine if a point is on the node. The Specification may 323 // change later and we also need to check maskers. 324 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 325 if (!resources) 326 return true; 327 328 if (RenderSVGResourceClipper* clipper = resources->clipper()) 329 return clipper->hitTestClipContent(object->objectBoundingBox(), point); 330 331 return true; 332 } 333 334 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) 335 { 336 ASSERT(context); 337 ASSERT(style); 338 ASSERT(object); 339 ASSERT(object->node()); 340 ASSERT(object->node()->isSVGElement()); 341 342 const SVGRenderStyle* svgStyle = style->svgStyle(); 343 ASSERT(svgStyle); 344 345 SVGLengthContext lengthContext(toSVGElement(object->node())); 346 context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); 347 context->setLineCap(svgStyle->capStyle()); 348 context->setLineJoin(svgStyle->joinStyle()); 349 context->setMiterLimit(svgStyle->strokeMiterLimit()); 350 351 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); 352 if (dashes.isEmpty()) 353 return; 354 355 DashArray dashArray; 356 const Vector<SVGLength>::const_iterator end = dashes.end(); 357 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) 358 dashArray.append((*it).value(lengthContext)); 359 360 context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); 361 } 362 363 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object) 364 { 365 ASSERT(strokeData); 366 ASSERT(style); 367 ASSERT(object); 368 ASSERT(object->node()); 369 ASSERT(object->node()->isSVGElement()); 370 371 const SVGRenderStyle* svgStyle = style->svgStyle(); 372 ASSERT(svgStyle); 373 374 SVGLengthContext lengthContext(toSVGElement(object->node())); 375 strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext)); 376 strokeData->setLineCap(svgStyle->capStyle()); 377 strokeData->setLineJoin(svgStyle->joinStyle()); 378 strokeData->setMiterLimit(svgStyle->strokeMiterLimit()); 379 380 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); 381 if (dashes.isEmpty()) 382 return; 383 384 DashArray dashArray; 385 const Vector<SVGLength>::const_iterator end = dashes.end(); 386 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) 387 dashArray.append((*it).value(lengthContext)); 388 389 strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); 390 } 391 392 bool SVGRenderSupport::isEmptySVGInlineText(const RenderObject* object) 393 { 394 // RenderSVGInlineText performs whitespace filtering in order to support xml:space 395 // (http://www.w3.org/TR/SVG/struct.html#LangSpaceAttrs), and can end up with an empty string 396 // even when its original constructor argument is non-empty. 397 return object->isSVGInlineText() && toRenderSVGInlineText(object)->hasEmptyText(); 398 } 399 400 } 401