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 #if ENABLE(SVG) 28 #include "SVGRenderSupport.h" 29 30 #include "FrameView.h" 31 #include "ImageBuffer.h" 32 #include "NodeRenderStyle.h" 33 #include "RenderLayer.h" 34 #include "RenderSVGPath.h" 35 #include "RenderSVGResource.h" 36 #include "RenderSVGResourceClipper.h" 37 #include "RenderSVGResourceFilter.h" 38 #include "RenderSVGResourceMarker.h" 39 #include "RenderSVGResourceMasker.h" 40 #include "RenderSVGRoot.h" 41 #include "SVGResources.h" 42 #include "SVGStyledElement.h" 43 #include "TransformState.h" 44 #include <wtf/UnusedParam.h> 45 46 namespace WebCore { 47 48 IntRect SVGRenderSupport::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer) 49 { 50 // Return early for any cases where we don't actually paint 51 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent()) 52 return IntRect(); 53 54 // Pass our local paint rect to computeRectForRepaint() which will 55 // map to parent coords and recurse up the parent chain. 56 IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates()); 57 object->computeRectForRepaint(repaintContainer, repaintRect); 58 return repaintRect; 59 } 60 61 void SVGRenderSupport::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) 62 { 63 const SVGRenderStyle* svgStyle = object->style()->svgStyle(); 64 if (const ShadowData* shadow = svgStyle->shadow()) 65 shadow->adjustRectForShadow(repaintRect); 66 67 // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent 68 repaintRect = object->localToParentTransform().mapRect(repaintRect); 69 object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed); 70 } 71 72 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) 73 { 74 ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree. 75 ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless. 76 transformState.applyTransform(object->localToParentTransform()); 77 object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 78 } 79 80 bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo) 81 { 82 ASSERT(object); 83 84 RenderStyle* style = object->style(); 85 ASSERT(style); 86 87 const SVGRenderStyle* svgStyle = style->svgStyle(); 88 ASSERT(svgStyle); 89 90 // Setup transparency layers before setting up SVG resources! 91 float opacity = style->opacity(); 92 const ShadowData* shadow = svgStyle->shadow(); 93 if (opacity < 1 || shadow) { 94 FloatRect repaintRect = object->repaintRectInLocalCoordinates(); 95 96 if (opacity < 1) { 97 paintInfo.context->clip(repaintRect); 98 paintInfo.context->beginTransparencyLayer(opacity); 99 } 100 101 if (shadow) { 102 paintInfo.context->clip(repaintRect); 103 paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace()); 104 paintInfo.context->beginTransparencyLayer(1); 105 } 106 } 107 108 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 109 if (!resources) 110 return true; 111 112 if (RenderSVGResourceMasker* masker = resources->masker()) { 113 if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) 114 return false; 115 } 116 117 if (RenderSVGResourceClipper* clipper = resources->clipper()) { 118 if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) 119 return false; 120 } 121 122 #if ENABLE(FILTERS) 123 if (RenderSVGResourceFilter* filter = resources->filter()) { 124 if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) 125 return false; 126 } 127 #endif 128 129 return true; 130 } 131 132 void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext) 133 { 134 #if !ENABLE(FILTERS) 135 UNUSED_PARAM(savedContext); 136 #endif 137 138 ASSERT(object); 139 140 const RenderStyle* style = object->style(); 141 ASSERT(style); 142 143 const SVGRenderStyle* svgStyle = style->svgStyle(); 144 ASSERT(svgStyle); 145 146 #if ENABLE(FILTERS) 147 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 148 if (resources) { 149 if (RenderSVGResourceFilter* filter = resources->filter()) { 150 filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode, /* path */0); 151 paintInfo.context = savedContext; 152 } 153 } 154 #endif 155 156 if (style->opacity() < 1) 157 paintInfo.context->endTransparencyLayer(); 158 159 if (svgStyle->shadow()) 160 paintInfo.context->endTransparencyLayer(); 161 } 162 163 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) 164 { 165 for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) { 166 if (current->isSVGHiddenContainer()) 167 continue; 168 169 const AffineTransform& transform = current->localToParentTransform(); 170 if (transform.isIdentity()) { 171 objectBoundingBox.unite(current->objectBoundingBox()); 172 strokeBoundingBox.unite(current->strokeBoundingBox()); 173 repaintBoundingBox.unite(current->repaintRectInLocalCoordinates()); 174 } else { 175 objectBoundingBox.unite(transform.mapRect(current->objectBoundingBox())); 176 strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox())); 177 repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); 178 } 179 } 180 } 181 182 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) 183 { 184 if (localTransform.isIdentity()) 185 return localRepaintRect.intersects(paintInfo.rect); 186 187 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); 188 } 189 190 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start) 191 { 192 while (start && !start->isSVGRoot()) 193 start = start->parent(); 194 195 ASSERT(start); 196 ASSERT(start->isSVGRoot()); 197 return toRenderSVGRoot(start); 198 } 199 200 static inline void invalidateResourcesOfChildren(RenderObject* start) 201 { 202 ASSERT(!start->needsLayout()); 203 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start)) 204 resources->removeClientFromCache(start, false); 205 206 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) 207 invalidateResourcesOfChildren(child); 208 } 209 210 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout) 211 { 212 bool layoutSizeChanged = findTreeRootObject(start)->isLayoutSizeChanged(); 213 HashSet<RenderObject*> notlayoutedObjects; 214 215 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 216 bool needsLayout = selfNeedsLayout; 217 218 if (layoutSizeChanged) { 219 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths 220 if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) { 221 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) { 222 // When the layout size changed and when using relative values tell the RenderSVGPath to update its Path object 223 if (child->isSVGPath()) 224 toRenderSVGPath(child)->setNeedsPathUpdate(); 225 226 needsLayout = true; 227 } 228 } 229 } 230 231 if (needsLayout) { 232 child->setNeedsLayout(true, false); 233 child->layout(); 234 } else { 235 if (child->needsLayout()) 236 child->layout(); 237 else if (layoutSizeChanged) 238 notlayoutedObjects.add(child); 239 } 240 241 ASSERT(!child->needsLayout()); 242 } 243 244 if (!layoutSizeChanged) { 245 ASSERT(notlayoutedObjects.isEmpty()); 246 return; 247 } 248 249 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. 250 HashSet<RenderObject*>::iterator end = notlayoutedObjects.end(); 251 for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it) 252 invalidateResourcesOfChildren(*it); 253 } 254 255 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object) 256 { 257 // SVG doesn't support independent x/y overflow 258 ASSERT(object->style()->overflowX() == object->style()->overflowY()); 259 260 // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle 261 ASSERT(object->style()->overflowX() != OSCROLL); 262 263 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 264 ASSERT(!object->isRoot()); 265 266 return object->style()->overflowX() == OHIDDEN; 267 } 268 269 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect) 270 { 271 ASSERT(object); 272 273 RenderStyle* style = object->style(); 274 ASSERT(style); 275 276 const SVGRenderStyle* svgStyle = style->svgStyle(); 277 ASSERT(svgStyle); 278 279 RenderObject* renderer = const_cast<RenderObject*>(object); 280 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 281 if (!resources) { 282 if (const ShadowData* shadow = svgStyle->shadow()) 283 shadow->adjustRectForShadow(repaintRect); 284 return; 285 } 286 287 #if ENABLE(FILTERS) 288 if (RenderSVGResourceFilter* filter = resources->filter()) 289 repaintRect = filter->resourceBoundingBox(renderer); 290 #endif 291 292 if (RenderSVGResourceClipper* clipper = resources->clipper()) 293 repaintRect.intersect(clipper->resourceBoundingBox(renderer)); 294 295 if (RenderSVGResourceMasker* masker = resources->masker()) 296 repaintRect.intersect(masker->resourceBoundingBox(renderer)); 297 298 if (const ShadowData* shadow = svgStyle->shadow()) 299 shadow->adjustRectForShadow(repaintRect); 300 } 301 302 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point) 303 { 304 ASSERT(object); 305 306 // We just take clippers into account to determine if a point is on the node. The Specification may 307 // change later and we also need to check maskers. 308 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 309 if (!resources) 310 return true; 311 312 if (RenderSVGResourceClipper* clipper = resources->clipper()) 313 return clipper->hitTestClipContent(object->objectBoundingBox(), point); 314 315 return true; 316 } 317 318 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object) 319 { 320 ASSERT(context); 321 ASSERT(style); 322 ASSERT(object); 323 ASSERT(object->node()); 324 ASSERT(object->node()->isSVGElement()); 325 326 const SVGRenderStyle* svgStyle = style->svgStyle(); 327 ASSERT(svgStyle); 328 329 SVGElement* lengthContext = static_cast<SVGElement*>(object->node()); 330 context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext)); 331 context->setLineCap(svgStyle->capStyle()); 332 context->setLineJoin(svgStyle->joinStyle()); 333 if (svgStyle->joinStyle() == MiterJoin) 334 context->setMiterLimit(svgStyle->strokeMiterLimit()); 335 336 const Vector<SVGLength>& dashes = svgStyle->strokeDashArray(); 337 if (dashes.isEmpty()) 338 context->setStrokeStyle(SolidStroke); 339 else { 340 DashArray dashArray; 341 const Vector<SVGLength>::const_iterator end = dashes.end(); 342 for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it) 343 dashArray.append((*it).value(lengthContext)); 344 345 context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext)); 346 } 347 } 348 349 } 350 351 #endif 352