Home | History | Annotate | Download | only in svg
      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 #include "core/rendering/svg/SVGRenderSupport.h"
     28 
     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/RenderSVGText.h"
     38 #include "core/rendering/svg/RenderSVGViewportContainer.h"
     39 #include "core/rendering/svg/SVGResources.h"
     40 #include "core/rendering/svg/SVGResourcesCache.h"
     41 #include "core/svg/SVGElement.h"
     42 #include "platform/geometry/TransformState.h"
     43 
     44 namespace WebCore {
     45 
     46 LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer)
     47 {
     48     // Return early for any cases where we don't actually paint
     49     if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
     50         return LayoutRect();
     51 
     52     // Pass our local paint rect to computeRectForRepaint() which will
     53     // map to parent coords and recurse up the parent chain.
     54     FloatRect repaintRect = object->repaintRectInLocalCoordinates();
     55     object->computeFloatRectForRepaint(repaintContainer, repaintRect);
     56     return enclosingLayoutRect(repaintRect);
     57 }
     58 
     59 void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
     60 {
     61     repaintRect.inflate(object->style()->outlineWidth());
     62 
     63     // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent.
     64     repaintRect = object->localToParentTransform().mapRect(repaintRect);
     65     object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
     66 }
     67 
     68 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
     69 {
     70     transformState.applyTransform(object->localToParentTransform());
     71 
     72     RenderObject* parent = object->parent();
     73 
     74     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
     75     // to map an element from SVG viewport coordinates to CSS box coordinates.
     76     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
     77     if (parent->isSVGRoot())
     78         transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
     79 
     80     MapCoordinatesFlags mode = UseTransforms;
     81     parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
     82 }
     83 
     84 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
     85 {
     86     ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
     87 
     88     RenderObject* parent = object->parent();
     89 
     90     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
     91     // to map an element from SVG viewport coordinates to CSS box coordinates.
     92     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
     93     if (parent->isSVGRoot()) {
     94         TransformationMatrix matrix(object->localToParentTransform());
     95         matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
     96         geometryMap.push(object, matrix);
     97     } else
     98         geometryMap.push(object, object->localToParentTransform());
     99 
    100     return parent;
    101 }
    102 
    103 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
    104 {
    105     if (!object->checkForRepaintDuringLayout())
    106         return false;
    107     // When a parent container is transformed in SVG, all children will be painted automatically
    108     // so we are able to skip redundant repaint checks.
    109     RenderObject* parent = object->parent();
    110     return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
    111 }
    112 
    113 // Update a bounding box taking into account the validity of the other bounding box.
    114 static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
    115 {
    116     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
    117     if (!otherValid)
    118         return;
    119 
    120     if (!objectBoundingBoxValid) {
    121         objectBoundingBox = otherBoundingBox;
    122         objectBoundingBoxValid = true;
    123         return;
    124     }
    125 
    126     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
    127 }
    128 
    129 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
    130 {
    131     objectBoundingBox = FloatRect();
    132     objectBoundingBoxValid = false;
    133     strokeBoundingBox = FloatRect();
    134 
    135     // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
    136     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
    137     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
    138     for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
    139         if (current->isSVGHiddenContainer())
    140             continue;
    141 
    142         const AffineTransform& transform = current->localToParentTransform();
    143         if (transform.isIdentity()) {
    144             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox());
    145             strokeBoundingBox.unite(current->repaintRectInLocalCoordinates());
    146         } else {
    147             updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox()));
    148             strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
    149         }
    150     }
    151 
    152     repaintBoundingBox = strokeBoundingBox;
    153 }
    154 
    155 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
    156 {
    157     if (localTransform.isIdentity())
    158         return localRepaintRect.intersects(paintInfo.rect);
    159 
    160     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
    161 }
    162 
    163 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
    164 {
    165     while (start && !start->isSVGRoot())
    166         start = start->parent();
    167 
    168     ASSERT(start);
    169     ASSERT(start->isSVGRoot());
    170     return toRenderSVGRoot(start);
    171 }
    172 
    173 static inline void invalidateResourcesOfChildren(RenderObject* start)
    174 {
    175     ASSERT(!start->needsLayout());
    176     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
    177         resources->removeClientFromCache(start, false);
    178 
    179     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
    180         invalidateResourcesOfChildren(child);
    181 }
    182 
    183 static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
    184 {
    185     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
    186         start = start->parent();
    187 
    188     ASSERT(start);
    189     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
    190     if (start->isSVGViewportContainer())
    191         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
    192 
    193     return toRenderSVGRoot(start)->isLayoutSizeChanged();
    194 }
    195 
    196 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
    197 {
    198     while (ancestor && !ancestor->isSVGRoot()) {
    199         if (ancestor->isSVGTransformableContainer())
    200             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
    201         if (ancestor->isSVGViewportContainer())
    202             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
    203         ancestor = ancestor->parent();
    204     }
    205 
    206     return false;
    207 }
    208 
    209 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
    210 {
    211     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
    212     bool transformChanged = transformToRootChanged(start);
    213     HashSet<RenderObject*> notlayoutedObjects;
    214 
    215     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    216         bool needsLayout = selfNeedsLayout;
    217         bool childEverHadLayout = child->everHadLayout();
    218 
    219         if (transformChanged) {
    220             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
    221             if (child->isSVGText())
    222                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    223             needsLayout = true;
    224         }
    225 
    226         if (layoutSizeChanged) {
    227             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
    228             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
    229                 if (element->hasRelativeLengths()) {
    230                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
    231                     if (child->isSVGShape())
    232                         toRenderSVGShape(child)->setNeedsShapeUpdate();
    233                     else if (child->isSVGText()) {
    234                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    235                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
    236                     }
    237 
    238                     needsLayout = true;
    239                 }
    240             }
    241         }
    242 
    243         SubtreeLayoutScope layoutScope(child);
    244         // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
    245         // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
    246         // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
    247         // SubtreeLayoutScope (in RenderView::layout()).
    248         if (needsLayout && !child->isSVGResourceContainer())
    249             layoutScope.setNeedsLayout(child);
    250 
    251         layoutResourcesIfNeeded(child);
    252 
    253         if (child->needsLayout()) {
    254             child->layout();
    255             // Renderers are responsible for repainting themselves when changing, except
    256             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
    257             // We could handle this in the individual objects, but for now it's easier to have
    258             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
    259             if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
    260                 child->repaint();
    261         } else if (layoutSizeChanged)
    262             notlayoutedObjects.add(child);
    263     }
    264 
    265     if (!layoutSizeChanged) {
    266         ASSERT(notlayoutedObjects.isEmpty());
    267         return;
    268     }
    269 
    270     // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
    271     HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
    272     for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
    273         invalidateResourcesOfChildren(*it);
    274 }
    275 
    276 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
    277 {
    278     ASSERT(object);
    279 
    280     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    281     if (resources)
    282         resources->layoutIfNeeded();
    283 }
    284 
    285 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
    286 {
    287     // SVG doesn't support independent x/y overflow
    288     ASSERT(object->style()->overflowX() == object->style()->overflowY());
    289 
    290     // OSCROLL is never set for SVG - see StyleResolver::adjustRenderStyle
    291     ASSERT(object->style()->overflowX() != OSCROLL);
    292 
    293     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
    294     ASSERT(!object->isRoot());
    295 
    296     return object->style()->overflowX() == OHIDDEN;
    297 }
    298 
    299 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
    300 {
    301     ASSERT(object);
    302 
    303     RenderObject* renderer = const_cast<RenderObject*>(object);
    304     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
    305     if (!resources)
    306         return;
    307 
    308     if (RenderSVGResourceFilter* filter = resources->filter())
    309         repaintRect = filter->resourceBoundingBox(renderer);
    310 
    311     if (RenderSVGResourceClipper* clipper = resources->clipper())
    312         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
    313 
    314     if (RenderSVGResourceMasker* masker = resources->masker())
    315         repaintRect.intersect(masker->resourceBoundingBox(renderer));
    316 }
    317 
    318 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
    319 {
    320     // If any of this container's children need to be laid out, and a filter is applied
    321     // to the container, we need to repaint the entire container.
    322     if (!object->normalChildNeedsLayout())
    323         return false;
    324 
    325     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    326     if (!resources || !resources->filter())
    327         return false;
    328 
    329     return true;
    330 }
    331 
    332 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
    333 {
    334     ASSERT(object);
    335 
    336     // We just take clippers into account to determine if a point is on the node. The Specification may
    337     // change later and we also need to check maskers.
    338     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    339     if (!resources)
    340         return true;
    341 
    342     if (RenderSVGResourceClipper* clipper = resources->clipper())
    343         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
    344 
    345     return true;
    346 }
    347 
    348 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
    349 {
    350     ASSERT(context);
    351     ASSERT(style);
    352     ASSERT(object);
    353     ASSERT(object->node());
    354     ASSERT(object->node()->isSVGElement());
    355 
    356     const SVGRenderStyle* svgStyle = style->svgStyle();
    357     ASSERT(svgStyle);
    358 
    359     SVGLengthContext lengthContext(toSVGElement(object->node()));
    360     context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
    361     context->setLineCap(svgStyle->capStyle());
    362     context->setLineJoin(svgStyle->joinStyle());
    363     context->setMiterLimit(svgStyle->strokeMiterLimit());
    364 
    365     const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
    366     if (dashes.isEmpty())
    367         return;
    368 
    369     DashArray dashArray;
    370     const Vector<SVGLength>::const_iterator end = dashes.end();
    371     for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
    372         dashArray.append((*it).value(lengthContext));
    373 
    374     context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
    375 }
    376 
    377 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
    378 {
    379     ASSERT(strokeData);
    380     ASSERT(style);
    381     ASSERT(object);
    382     ASSERT(object->node());
    383     ASSERT(object->node()->isSVGElement());
    384 
    385     const SVGRenderStyle* svgStyle = style->svgStyle();
    386     ASSERT(svgStyle);
    387 
    388     SVGLengthContext lengthContext(toSVGElement(object->node()));
    389     strokeData->setThickness(svgStyle->strokeWidth().value(lengthContext));
    390     strokeData->setLineCap(svgStyle->capStyle());
    391     strokeData->setLineJoin(svgStyle->joinStyle());
    392     strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
    393 
    394     const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
    395     if (dashes.isEmpty())
    396         return;
    397 
    398     DashArray dashArray;
    399     const Vector<SVGLength>::const_iterator end = dashes.end();
    400     for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
    401         dashArray.append((*it).value(lengthContext));
    402 
    403     strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
    404 }
    405 
    406 bool SVGRenderSupport::isEmptySVGInlineText(const RenderObject* object)
    407 {
    408     // RenderSVGInlineText performs whitespace filtering in order to support xml:space
    409     // (http://www.w3.org/TR/SVG/struct.html#LangSpaceAttrs), and can end up with an empty string
    410     // even when its original constructor argument is non-empty.
    411     return object->isSVGInlineText() && toRenderSVGInlineText(object)->hasEmptyText();
    412 }
    413 
    414 }
    415