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 #include "core/rendering/svg/SVGRenderSupport.h"
     27 
     28 #include "core/rendering/PaintInfo.h"
     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->paintInvalidationRectInLocalCoordinates();
     55     object->computeFloatRectForPaintInvalidation(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 computeFloatRectForPaintInvalidation() on our parent.
     64     repaintRect = object->localToParentTransform().mapRect(repaintRect);
     65     object->parent()->computeFloatRectForPaintInvalidation(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::parentTransformDidChange(RenderObject* object)
    104 {
    105     // When a parent container is transformed in SVG, all children will be painted automatically
    106     // so we are able to skip redundant repaint checks.
    107     RenderObject* parent = object->parent();
    108     return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
    109 }
    110 
    111 bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
    112 {
    113     if (!object->checkForPaintInvalidationDuringLayout())
    114         return false;
    115 
    116     return parentTransformDidChange(object);
    117 }
    118 
    119 // Update a bounding box taking into account the validity of the other bounding box.
    120 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
    121 {
    122     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
    123     if (!otherValid)
    124         return;
    125 
    126     if (!objectBoundingBoxValid) {
    127         objectBoundingBox = otherBoundingBox;
    128         objectBoundingBoxValid = true;
    129         return;
    130     }
    131 
    132     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
    133 }
    134 
    135 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
    136 {
    137     objectBoundingBox = FloatRect();
    138     objectBoundingBoxValid = false;
    139     strokeBoundingBox = FloatRect();
    140 
    141     // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes
    142     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
    143     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
    144     for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) {
    145         if (current->isSVGHiddenContainer())
    146             continue;
    147 
    148         const AffineTransform& transform = current->localToParentTransform();
    149         updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
    150             transform.mapRect(current->objectBoundingBox()));
    151         strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates()));
    152     }
    153 
    154     repaintBoundingBox = strokeBoundingBox;
    155 }
    156 
    157 bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
    158 {
    159     return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
    160 }
    161 
    162 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
    163 {
    164     while (start && !start->isSVGRoot())
    165         start = start->parent();
    166 
    167     ASSERT(start);
    168     ASSERT(start->isSVGRoot());
    169     return toRenderSVGRoot(start);
    170 }
    171 
    172 inline void SVGRenderSupport::invalidateResourcesOfChildren(RenderObject* start)
    173 {
    174     ASSERT(!start->needsLayout());
    175     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
    176         resources->removeClientFromCache(start, false);
    177 
    178     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling())
    179         invalidateResourcesOfChildren(child);
    180 }
    181 
    182 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start)
    183 {
    184     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
    185         start = start->parent();
    186 
    187     ASSERT(start);
    188     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
    189     if (start->isSVGViewportContainer())
    190         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
    191 
    192     return toRenderSVGRoot(start)->isLayoutSizeChanged();
    193 }
    194 
    195 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
    196 {
    197     while (ancestor && !ancestor->isSVGRoot()) {
    198         if (ancestor->isSVGTransformableContainer())
    199             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
    200         if (ancestor->isSVGViewportContainer())
    201             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
    202         ancestor = ancestor->parent();
    203     }
    204 
    205     return false;
    206 }
    207 
    208 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
    209 {
    210     bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
    211     bool transformChanged = transformToRootChanged(start);
    212     HashSet<RenderObject*> notlayoutedObjects;
    213 
    214     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
    215         bool needsLayout = selfNeedsLayout;
    216         bool childEverHadLayout = child->everHadLayout();
    217 
    218         if (transformChanged) {
    219             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
    220             if (child->isSVGText())
    221                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    222             needsLayout = true;
    223         }
    224 
    225         if (layoutSizeChanged) {
    226             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
    227             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
    228                 if (element->hasRelativeLengths()) {
    229                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
    230                     if (child->isSVGShape()) {
    231                         toRenderSVGShape(child)->setNeedsShapeUpdate();
    232                     } else if (child->isSVGText()) {
    233                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    234                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
    235                     }
    236 
    237                     needsLayout = true;
    238                 }
    239             }
    240         }
    241 
    242         SubtreeLayoutScope layoutScope(*child);
    243         // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
    244         // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
    245         // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
    246         // SubtreeLayoutScope (in RenderView::layout()).
    247         if (needsLayout && !child->isSVGResourceContainer())
    248             layoutScope.setNeedsLayout(child);
    249 
    250         layoutResourcesIfNeeded(child);
    251 
    252         if (child->needsLayout()) {
    253             child->layout();
    254             // Renderers are responsible for repainting themselves when changing, except
    255             // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds.
    256             // We could handle this in the individual objects, but for now it's easier to have
    257             // parent containers call repaint().  (RenderBlock::layout* has similar logic.)
    258             if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
    259                 child->paintInvalidationForWholeRenderer();
    260         } else if (layoutSizeChanged) {
    261             notlayoutedObjects.add(child);
    262         }
    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     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
    288     ASSERT(!object->isDocumentElement());
    289 
    290     return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL;
    291 }
    292 
    293 void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect)
    294 {
    295     ASSERT(renderer);
    296 
    297     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
    298     if (!resources)
    299         return;
    300 
    301     if (RenderSVGResourceFilter* filter = resources->filter())
    302         repaintRect = filter->resourceBoundingBox(renderer);
    303 
    304     if (RenderSVGResourceClipper* clipper = resources->clipper())
    305         repaintRect.intersect(clipper->resourceBoundingBox(renderer));
    306 
    307     if (RenderSVGResourceMasker* masker = resources->masker())
    308         repaintRect.intersect(masker->resourceBoundingBox(renderer));
    309 }
    310 
    311 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
    312 {
    313     // If any of this container's children need to be laid out, and a filter is applied
    314     // to the container, we need to repaint the entire container.
    315     if (!object->normalChildNeedsLayout())
    316         return false;
    317 
    318     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    319     if (!resources || !resources->filter())
    320         return false;
    321 
    322     return true;
    323 }
    324 
    325 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
    326 {
    327     ASSERT(object);
    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     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    332     if (!resources)
    333         return true;
    334 
    335     if (RenderSVGResourceClipper* clipper = resources->clipper())
    336         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
    337 
    338     return true;
    339 }
    340 
    341 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
    342 {
    343     ASSERT(context);
    344     ASSERT(style);
    345     ASSERT(object);
    346     ASSERT(object->node());
    347     ASSERT(object->node()->isSVGElement());
    348 
    349     const SVGRenderStyle* svgStyle = style->svgStyle();
    350     ASSERT(svgStyle);
    351 
    352     SVGLengthContext lengthContext(toSVGElement(object->node()));
    353     context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext));
    354     context->setLineCap(svgStyle->capStyle());
    355     context->setLineJoin(svgStyle->joinStyle());
    356     context->setMiterLimit(svgStyle->strokeMiterLimit());
    357 
    358     RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
    359     if (dashes->isEmpty())
    360         return;
    361 
    362     DashArray dashArray;
    363     SVGLengthList::ConstIterator it = dashes->begin();
    364     SVGLengthList::ConstIterator itEnd = dashes->end();
    365     for (; it != itEnd; ++it)
    366         dashArray.append(it->value(lengthContext));
    367 
    368     context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
    369 }
    370 
    371 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
    372 {
    373     ASSERT(strokeData);
    374     ASSERT(style);
    375     ASSERT(object);
    376     ASSERT(object->node());
    377     ASSERT(object->node()->isSVGElement());
    378 
    379     const SVGRenderStyle* svgStyle = style->svgStyle();
    380     ASSERT(svgStyle);
    381 
    382     SVGLengthContext lengthContext(toSVGElement(object->node()));
    383     strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext));
    384     strokeData->setLineCap(svgStyle->capStyle());
    385     strokeData->setLineJoin(svgStyle->joinStyle());
    386     strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
    387 
    388     RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
    389     if (dashes->isEmpty())
    390         return;
    391 
    392     DashArray dashArray;
    393     size_t length = dashes->length();
    394     for (size_t i = 0; i < length; ++i)
    395         dashArray.append(dashes->at(i)->value(lengthContext));
    396 
    397     strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
    398 }
    399 
    400 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
    401 {
    402     ASSERT(object->isText());
    403     // <br> is marked as text, but is not handled by the SVG rendering code-path.
    404     return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();
    405 }
    406 
    407 }
    408