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