1 /* 2 * Copyright (C) 2007, 2008 Rob Buis <buis (at) kde.org> 3 * (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 4 * (C) 2007 Eric Seidel <eric (at) webkit.org> 5 * (C) 2009 Google, Inc. All rights reserved. 6 * (C) 2009 Dirk Schulze <krit (at) webkit.org> 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 27 #if ENABLE(SVG) 28 #include "SVGRenderSupport.h" 29 30 #include "AffineTransform.h" 31 #include "ImageBuffer.h" 32 #include "RenderObject.h" 33 #include "RenderSVGContainer.h" 34 #include "RenderView.h" 35 #include "SVGResourceClipper.h" 36 #include "SVGResourceFilter.h" 37 #include "SVGResourceMasker.h" 38 #include "SVGStyledElement.h" 39 #include "SVGURIReference.h" 40 #include "TransformState.h" 41 #include <wtf/UnusedParam.h> 42 43 namespace WebCore { 44 45 SVGRenderBase::~SVGRenderBase() 46 { 47 } 48 49 IntRect SVGRenderBase::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer) 50 { 51 // Return early for any cases where we don't actually paint 52 if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent()) 53 return IntRect(); 54 55 // Pass our local paint rect to computeRectForRepaint() which will 56 // map to parent coords and recurse up the parent chain. 57 IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates()); 58 object->computeRectForRepaint(repaintContainer, repaintRect); 59 return repaintRect; 60 } 61 62 void SVGRenderBase::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) 63 { 64 object->style()->svgStyle()->inflateForShadow(repaintRect); 65 66 // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent 67 repaintRect = object->localToParentTransform().mapRect(repaintRect); 68 object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed); 69 } 70 71 void SVGRenderBase::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) 72 { 73 ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree. 74 ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless. 75 transformState.applyTransform(object->localToParentTransform()); 76 object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 77 } 78 79 bool SVGRenderBase::prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& repaintRect, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) 80 { 81 #if !ENABLE(FILTERS) 82 UNUSED_PARAM(filter); 83 UNUSED_PARAM(rootFilter); 84 #endif 85 86 ASSERT(object); 87 SVGElement* svgElement = static_cast<SVGElement*>(object->node()); 88 ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); 89 90 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); 91 const RenderStyle* style = object->style(); 92 ASSERT(style); 93 94 const SVGRenderStyle* svgStyle = style->svgStyle(); 95 ASSERT(svgStyle); 96 97 // Setup transparency layers before setting up filters! 98 float opacity = style->opacity(); 99 if (opacity < 1.0f) { 100 paintInfo.context->clip(repaintRect); 101 paintInfo.context->beginTransparencyLayer(opacity); 102 } 103 104 if (ShadowData* shadow = svgStyle->shadow()) { 105 paintInfo.context->clip(repaintRect); 106 paintInfo.context->setShadow(IntSize(shadow->x, shadow->y), shadow->blur, shadow->color, style->colorSpace()); 107 paintInfo.context->beginTransparencyLayer(1.0f); 108 } 109 110 #if ENABLE(FILTERS) 111 AtomicString filterId(svgStyle->filter()); 112 #endif 113 114 AtomicString clipperId(svgStyle->clipPath()); 115 AtomicString maskerId(svgStyle->maskElement()); 116 117 Document* document = object->document(); 118 119 #if ENABLE(FILTERS) 120 SVGResourceFilter* newFilter = getFilterById(document, filterId, object); 121 if (newFilter == rootFilter) { 122 // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>. 123 // The filter is NOT meant to be applied twice in that case! 124 filter = 0; 125 filterId = String(); 126 } else 127 filter = newFilter; 128 #endif 129 130 SVGResourceClipper* clipper = getClipperById(document, clipperId, object); 131 SVGResourceMasker* masker = getMaskerById(document, maskerId, object); 132 133 if (masker) { 134 masker->addClient(styledElement); 135 if (!masker->applyMask(paintInfo.context, object)) 136 return false; 137 } else if (!maskerId.isEmpty()) 138 svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); 139 140 if (clipper) { 141 clipper->addClient(styledElement); 142 clipper->applyClip(paintInfo.context, object->objectBoundingBox()); 143 } else if (!clipperId.isEmpty()) 144 svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); 145 146 #if ENABLE(FILTERS) 147 if (filter) { 148 filter->addClient(styledElement); 149 if (!filter->prepareFilter(paintInfo.context, object)) 150 return false; 151 } else if (!filterId.isEmpty()) 152 svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); 153 #endif 154 155 return true; 156 } 157 158 void SVGRenderBase::finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, SVGResourceFilter*& filter, GraphicsContext* savedContext) 159 { 160 #if !ENABLE(FILTERS) 161 UNUSED_PARAM(filter); 162 UNUSED_PARAM(savedContext); 163 #endif 164 165 ASSERT(object); 166 167 const RenderStyle* style = object->style(); 168 ASSERT(style); 169 170 #if ENABLE(FILTERS) 171 if (filter) { 172 filter->applyFilter(paintInfo.context, object); 173 paintInfo.context = savedContext; 174 } 175 #endif 176 177 float opacity = style->opacity(); 178 if (opacity < 1.0f) 179 paintInfo.context->endTransparencyLayer(); 180 181 // This needs to be done separately from opacity, because if both properties are set, 182 // then the transparency layers are nested. 183 if (style->svgStyle()->shadow()) 184 paintInfo.context->endTransparencyLayer(); 185 } 186 187 void renderSubtreeToImage(ImageBuffer* image, RenderObject* item) 188 { 189 ASSERT(item); 190 ASSERT(image); 191 ASSERT(image->context()); 192 RenderObject::PaintInfo info(image->context(), IntRect(), PaintPhaseForeground, 0, 0, 0); 193 194 // FIXME: isSVGContainer returns true for RenderSVGViewportContainer, so if this is ever 195 // called with one of those, we will read from the wrong offset in an object due to a bad cast. 196 RenderSVGContainer* svgContainer = 0; 197 if (item && item->isSVGContainer()) 198 svgContainer = toRenderSVGContainer(item); 199 200 bool drawsContents = svgContainer ? svgContainer->drawsContents() : false; 201 if (svgContainer && !drawsContents) 202 svgContainer->setDrawsContents(true); 203 204 item->layoutIfNeeded(); 205 item->paint(info, 0, 0); 206 207 if (svgContainer && !drawsContents) 208 svgContainer->setDrawsContents(false); 209 } 210 211 void clampImageBufferSizeToViewport(FrameView* frameView, IntSize& size) 212 { 213 if (!frameView) 214 return; 215 216 int viewWidth = frameView->visibleWidth(); 217 int viewHeight = frameView->visibleHeight(); 218 219 if (size.width() > viewWidth) 220 size.setWidth(viewWidth); 221 222 if (size.height() > viewHeight) 223 size.setHeight(viewHeight); 224 } 225 226 FloatRect SVGRenderBase::computeContainerBoundingBox(const RenderObject* container, bool includeAllPaintedContent) 227 { 228 FloatRect boundingBox; 229 230 RenderObject* current = container->firstChild(); 231 for (; current != 0; current = current->nextSibling()) { 232 FloatRect childBBox = includeAllPaintedContent ? current->repaintRectInLocalCoordinates() : current->objectBoundingBox(); 233 FloatRect childBBoxInLocalCoords = current->localToParentTransform().mapRect(childBBox); 234 boundingBox.unite(childBBoxInLocalCoords); 235 } 236 237 return boundingBox; 238 } 239 240 void SVGRenderBase::layoutChildren(RenderObject* start, bool selfNeedsLayout) 241 { 242 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 243 // Only force our kids to layout if we're being asked to relayout as a result of a parent changing 244 // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed 245 // that's a possible future optimization using LayoutState 246 // http://bugs.webkit.org/show_bug.cgi?id=15391 247 bool needsLayout = selfNeedsLayout; 248 if (!needsLayout) { 249 if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) { 250 if (element->isStyled()) 251 needsLayout = static_cast<SVGStyledElement*>(element)->hasRelativeValues(); 252 } 253 } 254 255 if (needsLayout) 256 child->setNeedsLayout(true, false); 257 258 child->layoutIfNeeded(); 259 ASSERT(!child->needsLayout()); 260 } 261 } 262 263 bool SVGRenderBase::isOverflowHidden(const RenderObject* object) 264 { 265 // SVG doesn't support independent x/y overflow 266 ASSERT(object->style()->overflowX() == object->style()->overflowY()); 267 268 // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle 269 ASSERT(object->style()->overflowX() != OSCROLL); 270 271 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 272 ASSERT(!object->isRoot()); 273 274 return object->style()->overflowX() == OHIDDEN; 275 } 276 277 FloatRect SVGRenderBase::filterBoundingBoxForRenderer(const RenderObject* object) const 278 { 279 #if ENABLE(FILTERS) 280 SVGResourceFilter* filter = getFilterById(object->document(), object->style()->svgStyle()->filter(), object); 281 if (filter) 282 return filter->filterBoundingBox(object->objectBoundingBox()); 283 #else 284 UNUSED_PARAM(object); 285 #endif 286 return FloatRect(); 287 } 288 289 FloatRect SVGRenderBase::clipperBoundingBoxForRenderer(const RenderObject* object) const 290 { 291 SVGResourceClipper* clipper = getClipperById(object->document(), object->style()->svgStyle()->clipPath(), object); 292 if (clipper) 293 return clipper->clipperBoundingBox(object->objectBoundingBox()); 294 295 return FloatRect(); 296 } 297 298 FloatRect SVGRenderBase::maskerBoundingBoxForRenderer(const RenderObject* object) const 299 { 300 SVGResourceMasker* masker = getMaskerById(object->document(), object->style()->svgStyle()->maskElement(), object); 301 if (masker) 302 return masker->maskerBoundingBox(object->objectBoundingBox()); 303 304 return FloatRect(); 305 } 306 307 void applyTransformToPaintInfo(RenderObject::PaintInfo& paintInfo, const AffineTransform& localToAncestorTransform) 308 { 309 if (localToAncestorTransform.isIdentity()) 310 return; 311 312 paintInfo.context->concatCTM(localToAncestorTransform); 313 paintInfo.rect = localToAncestorTransform.inverse().mapRect(paintInfo.rect); 314 } 315 316 } // namespace WebCore 317 318 #endif // ENABLE(SVG) 319