WebCore/rendering/SVGRenderSupport.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
       
     3  *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
       
     4  *           (C) 2007 Eric Seidel <eric@webkit.org>
       
     5  *           (C) 2009 Google, Inc.  All rights reserved.
       
     6  *           (C) 2009 Dirk Schulze <krit@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 
       
    26 #include "config.h"
       
    27 
       
    28 #if ENABLE(SVG)
       
    29 #include "SVGRenderSupport.h"
       
    30 
       
    31 #include "FrameView.h"
       
    32 #include "ImageBuffer.h"
       
    33 #include "NodeRenderStyle.h"
       
    34 #include "RenderLayer.h"
       
    35 #include "RenderPath.h"
       
    36 #include "RenderSVGContainer.h"
       
    37 #include "RenderSVGResource.h"
       
    38 #include "RenderSVGResourceClipper.h"
       
    39 #include "RenderSVGResourceFilter.h"
       
    40 #include "RenderSVGResourceMarker.h"
       
    41 #include "RenderSVGResourceMasker.h"
       
    42 #include "RenderSVGRoot.h"
       
    43 #include "SVGStyledElement.h"
       
    44 #include "TransformState.h"
       
    45 #include <wtf/UnusedParam.h>
       
    46 
       
    47 namespace WebCore {
       
    48 
       
    49 IntRect SVGRenderSupport::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 SVGRenderSupport::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 SVGRenderSupport::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 SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
       
    80 {
       
    81     ASSERT(object);
       
    82     SVGElement* svgElement = static_cast<SVGElement*>(object->node());
       
    83     ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
       
    84 
       
    85     SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
       
    86     RenderStyle* style = object->style();
       
    87     ASSERT(style);
       
    88 
       
    89     const SVGRenderStyle* svgStyle = style->svgStyle();
       
    90     ASSERT(svgStyle);
       
    91 
       
    92     FloatRect repaintRect;
       
    93 
       
    94     // Setup transparency layers before setting up SVG resources!
       
    95     float opacity = style->opacity();
       
    96     if (opacity < 1) {
       
    97         repaintRect = object->repaintRectInLocalCoordinates();
       
    98         paintInfo.context->clip(repaintRect);
       
    99         paintInfo.context->beginTransparencyLayer(opacity);
       
   100     }
       
   101 
       
   102     if (const ShadowData* shadow = svgStyle->shadow()) {
       
   103         // Eventually compute repaint rect, if not done so far.
       
   104         if (opacity >= 1)
       
   105             repaintRect = object->repaintRectInLocalCoordinates();
       
   106 
       
   107         paintInfo.context->clip(repaintRect);
       
   108         paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
       
   109         paintInfo.context->beginTransparencyLayer(1);
       
   110     }
       
   111 
       
   112     Document* document = object->document();
       
   113 
       
   114     if (svgStyle->hasMasker()) {
       
   115         AtomicString maskerId(svgStyle->maskerResource());
       
   116         if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(document, maskerId)) {
       
   117             if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
       
   118                 return false;
       
   119         } else
       
   120             document->accessSVGExtensions()->addPendingResource(maskerId, styledElement);
       
   121     }
       
   122 
       
   123     if (svgStyle->hasClipper()) {
       
   124         AtomicString clipperId(svgStyle->clipperResource());
       
   125         if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, clipperId))
       
   126             clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode);
       
   127         else
       
   128             document->accessSVGExtensions()->addPendingResource(clipperId, styledElement);
       
   129     }
       
   130 
       
   131 #if ENABLE(FILTERS)
       
   132     if (svgStyle->hasFilter()) {
       
   133         AtomicString filterId(svgStyle->filterResource());
       
   134         if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(document, filterId)) { 
       
   135             if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
       
   136                 return false;
       
   137         } else
       
   138             document->accessSVGExtensions()->addPendingResource(filterId, styledElement);
       
   139     }
       
   140 #endif
       
   141 
       
   142     return true;
       
   143 }
       
   144 
       
   145 void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
       
   146 {
       
   147 #if !ENABLE(FILTERS)
       
   148     UNUSED_PARAM(savedContext);
       
   149 #endif
       
   150 
       
   151     ASSERT(object);
       
   152 
       
   153     const RenderStyle* style = object->style();
       
   154     ASSERT(style);
       
   155 
       
   156     const SVGRenderStyle* svgStyle = style->svgStyle();
       
   157     ASSERT(svgStyle);
       
   158 
       
   159 #if ENABLE(FILTERS)
       
   160     if (svgStyle->hasFilter()) {
       
   161         AtomicString filterId(svgStyle->filterResource());
       
   162         if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object->document(), filterId)) { 
       
   163             filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode);
       
   164             paintInfo.context = savedContext;
       
   165         }
       
   166     }
       
   167 #endif
       
   168 
       
   169     float opacity = style->opacity();    
       
   170     if (opacity < 1)
       
   171         paintInfo.context->endTransparencyLayer();
       
   172 
       
   173     // This needs to be done separately from opacity, because if both properties are set,
       
   174     // then the transparency layers are nested. 
       
   175     if (svgStyle->shadow())
       
   176         paintInfo.context->endTransparencyLayer();
       
   177 }
       
   178 
       
   179 void SVGRenderSupport::renderSubtreeToImage(ImageBuffer* image, RenderObject* item)
       
   180 {
       
   181     ASSERT(item);
       
   182     ASSERT(image);
       
   183     ASSERT(image->context());
       
   184 
       
   185     // FIXME: This sets the rect to the viewable area of the current frame. This
       
   186     // is used to support text drawings to the ImageBuffer. See bug 30399.
       
   187     IntRect rect;
       
   188     FrameView* frameView = item->document()->view();
       
   189     if (frameView)
       
   190         rect = IntRect(0, 0, frameView->visibleWidth(), frameView->visibleHeight());
       
   191     PaintInfo info(image->context(), rect, PaintPhaseForeground, 0, 0, 0);
       
   192 
       
   193     // FIXME: isSVGContainer returns true for RenderSVGViewportContainer, so if this is ever
       
   194     // called with one of those, we will read from the wrong offset in an object due to a bad cast.
       
   195     RenderSVGContainer* svgContainer = 0;
       
   196     if (item && item->isSVGContainer())
       
   197         svgContainer = toRenderSVGContainer(item);
       
   198 
       
   199     bool drawsContents = svgContainer ? svgContainer->drawsContents() : false;
       
   200     if (svgContainer && !drawsContents)
       
   201         svgContainer->setDrawsContents(true);
       
   202 
       
   203     item->layoutIfNeeded();
       
   204     item->paint(info, 0, 0);
       
   205 
       
   206     if (svgContainer && !drawsContents)
       
   207         svgContainer->setDrawsContents(false);
       
   208 }
       
   209 
       
   210 FloatRect SVGRenderSupport::computeContainerBoundingBox(const RenderObject* container, ContainerBoundingBoxMode mode)
       
   211 {
       
   212     FloatRect boundingBox;
       
   213 
       
   214     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
       
   215         FloatRect childBoundingBox;
       
   216 
       
   217         switch (mode) {
       
   218         case ObjectBoundingBox:
       
   219             childBoundingBox = current->objectBoundingBox();
       
   220             break;
       
   221         case StrokeBoundingBox:
       
   222             childBoundingBox = current->strokeBoundingBox();
       
   223             break;
       
   224         case RepaintBoundingBox:
       
   225             childBoundingBox = current->repaintRectInLocalCoordinates();
       
   226             break;
       
   227         }
       
   228 
       
   229         boundingBox.unite(current->localToParentTransform().mapRect(childBoundingBox));
       
   230     }
       
   231 
       
   232     return boundingBox;
       
   233 }
       
   234 
       
   235 static inline RenderSVGRoot* svgRootTreeObject(RenderObject* start)
       
   236 {
       
   237     while (start && !start->isSVGRoot())
       
   238         start = start->parent();
       
   239 
       
   240     ASSERT(start);
       
   241     ASSERT(start->isSVGRoot());
       
   242     return toRenderSVGRoot(start);
       
   243 }
       
   244 
       
   245 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
       
   246 {
       
   247     bool layoutSizeChanged = svgRootTreeObject(start)->isLayoutSizeChanged();
       
   248 
       
   249     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
       
   250         bool needsLayout = selfNeedsLayout;
       
   251 
       
   252         if (layoutSizeChanged) {
       
   253             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
       
   254             if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
       
   255                 if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
       
   256                     // When the layout size changed and when using relative values tell the RenderPath to update its Path object
       
   257                     if (child->isRenderPath())
       
   258                         toRenderPath(child)->setNeedsPathUpdate();
       
   259 
       
   260                     needsLayout = true;
       
   261                 }
       
   262             }
       
   263         }
       
   264 
       
   265         if (needsLayout) {
       
   266             child->setNeedsLayout(true, false);
       
   267             child->layout();
       
   268         } else
       
   269             child->layoutIfNeeded();
       
   270 
       
   271         ASSERT(!child->needsLayout());
       
   272     }
       
   273 }
       
   274 
       
   275 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
       
   276 {
       
   277     // SVG doesn't support independent x/y overflow
       
   278     ASSERT(object->style()->overflowX() == object->style()->overflowY());
       
   279 
       
   280     // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
       
   281     ASSERT(object->style()->overflowX() != OSCROLL);
       
   282 
       
   283     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
       
   284     ASSERT(!object->isRoot());
       
   285 
       
   286     return object->style()->overflowX() == OHIDDEN;
       
   287 }
       
   288 
       
   289 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
       
   290 {
       
   291     ASSERT(object);
       
   292     ASSERT(object->style());
       
   293     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
       
   294     if (!svgStyle)
       
   295         return;
       
   296         
       
   297     RenderObject* renderer = const_cast<RenderObject*>(object);
       
   298 #if ENABLE(FILTERS)
       
   299     if (svgStyle->hasFilter()) {
       
   300         if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object->document(), svgStyle->filterResource()))
       
   301             repaintRect = filter->resourceBoundingBox(renderer);
       
   302     }
       
   303 #endif
       
   304 
       
   305     if (svgStyle->hasClipper()) {
       
   306         if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object->document(), svgStyle->clipperResource()))
       
   307             repaintRect.intersect(clipper->resourceBoundingBox(renderer));
       
   308     }
       
   309     
       
   310     if (svgStyle->hasMasker()) {
       
   311         if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object->document(), svgStyle->maskerResource()))
       
   312             repaintRect.intersect(masker->resourceBoundingBox(renderer));
       
   313     }
       
   314     
       
   315     svgStyle->inflateForShadow(repaintRect);
       
   316 }
       
   317 
       
   318 bool SVGRenderSupport::pointInClippingArea(const RenderObject* object, const FloatPoint& point)
       
   319 {
       
   320     ASSERT(object);
       
   321     ASSERT(object->style());
       
   322 
       
   323     Document* document = object->document();
       
   324     ASSERT(document);
       
   325 
       
   326     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
       
   327     ASSERT(svgStyle);
       
   328 
       
   329     // We just take clippers into account to determine if a point is on the node. The Specification may
       
   330     // change later and we also need to check maskers.
       
   331     if (svgStyle->hasClipper()) {
       
   332         if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, svgStyle->clipperResource()))
       
   333             return clipper->hitTestClipContent(object->objectBoundingBox(), point);
       
   334     }
       
   335 
       
   336     return true;
       
   337 }
       
   338 
       
   339 DashArray SVGRenderSupport::dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle)
       
   340 {
       
   341     DashArray array;
       
   342     
       
   343     CSSValueList* dashes = style->svgStyle()->strokeDashArray();
       
   344     if (!dashes)
       
   345         return array;
       
   346 
       
   347     CSSPrimitiveValue* dash = 0;
       
   348     unsigned long len = dashes->length();
       
   349     for (unsigned long i = 0; i < len; ++i) {
       
   350         dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i));
       
   351         if (!dash)
       
   352             continue;
       
   353 
       
   354         array.append(dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle));
       
   355     }
       
   356 
       
   357     return array;
       
   358 }
       
   359 
       
   360 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
       
   361 {
       
   362     context->setStrokeThickness(SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeWidth(), 1.0f));
       
   363     context->setLineCap(style->svgStyle()->capStyle());
       
   364     context->setLineJoin(style->svgStyle()->joinStyle());
       
   365     if (style->svgStyle()->joinStyle() == MiterJoin)
       
   366         context->setMiterLimit(style->svgStyle()->strokeMiterLimit());
       
   367 
       
   368     const DashArray& dashes = dashArrayFromRenderingStyle(object->style(), object->document()->documentElement()->renderStyle());
       
   369     float dashOffset = SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeDashOffset(), 0.0f);
       
   370     if (dashes.isEmpty())
       
   371         context->setStrokeStyle(SolidStroke);
       
   372     else
       
   373         context->setLineDash(dashes, dashOffset);
       
   374 }
       
   375 
       
   376 const RenderObject* SVGRenderSupport::findTextRootObject(const RenderObject* start)
       
   377 {
       
   378     while (start && !start->isSVGText())
       
   379         start = start->parent();
       
   380     ASSERT(start);
       
   381     ASSERT(start->isSVGText());
       
   382 
       
   383     return start;
       
   384 }
       
   385 
       
   386 }
       
   387 
       
   388 #endif