Home | History | Annotate | Download | only in rendering
      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