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/RenderSVGShape.h"
     38 #include "core/rendering/svg/RenderSVGText.h"
     39 #include "core/rendering/svg/RenderSVGViewportContainer.h"
     40 #include "core/rendering/svg/SVGResources.h"
     41 #include "core/rendering/svg/SVGResourcesCache.h"
     42 #include "core/svg/SVGElement.h"
     43 #include "platform/geometry/TransformState.h"
     44 #include "platform/graphics/Path.h"
     45 
     46 namespace blink {
     47 
     48 LayoutRect SVGRenderSupport::clippedOverflowRectForPaintInvalidation(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState)
     49 {
     50     // Return early for any cases where we don't actually paint
     51     if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
     52         return LayoutRect();
     53 
     54     // Pass our local paint rect to computeRectForPaintInvalidation() which will
     55     // map to parent coords and recurse up the parent chain.
     56     FloatRect paintInvalidationRect = object->paintInvalidationRectInLocalCoordinates();
     57     paintInvalidationRect.inflate(object->style()->outlineWidth());
     58 
     59     object->computeFloatRectForPaintInvalidation(paintInvalidationContainer, paintInvalidationRect, paintInvalidationState);
     60     return enclosingLayoutRect(paintInvalidationRect);
     61 }
     62 
     63 void SVGRenderSupport::computeFloatRectForPaintInvalidation(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, FloatRect& paintInvalidationRect, const PaintInvalidationState* paintInvalidationState)
     64 {
     65     // Translate to coords in our parent renderer, and then call computeFloatRectForPaintInvalidation() on our parent.
     66     paintInvalidationRect = object->localToParentTransform().mapRect(paintInvalidationRect);
     67     object->parent()->computeFloatRectForPaintInvalidation(paintInvalidationContainer, paintInvalidationRect, paintInvalidationState);
     68 }
     69 
     70 void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, TransformState& transformState, bool* wasFixed, const PaintInvalidationState* paintInvalidationState)
     71 {
     72     transformState.applyTransform(object->localToParentTransform());
     73 
     74     RenderObject* parent = object->parent();
     75 
     76     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
     77     // to map an element from SVG viewport coordinates to CSS box coordinates.
     78     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
     79     if (parent->isSVGRoot())
     80         transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
     81 
     82     MapCoordinatesFlags mode = UseTransforms;
     83     parent->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed, paintInvalidationState);
     84 }
     85 
     86 const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
     87 {
     88     ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
     89 
     90     RenderObject* parent = object->parent();
     91 
     92     // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
     93     // to map an element from SVG viewport coordinates to CSS box coordinates.
     94     // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates.
     95     if (parent->isSVGRoot()) {
     96         TransformationMatrix matrix(object->localToParentTransform());
     97         matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
     98         geometryMap.push(object, matrix);
     99     } else
    100         geometryMap.push(object, object->localToParentTransform());
    101 
    102     return parent;
    103 }
    104 
    105 // Update a bounding box taking into account the validity of the other bounding box.
    106 inline void SVGRenderSupport::updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
    107 {
    108     bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
    109     if (!otherValid)
    110         return;
    111 
    112     if (!objectBoundingBoxValid) {
    113         objectBoundingBox = otherBoundingBox;
    114         objectBoundingBoxValid = true;
    115         return;
    116     }
    117 
    118     objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
    119 }
    120 
    121 void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& paintInvalidationBoundingBox)
    122 {
    123     objectBoundingBox = FloatRect();
    124     objectBoundingBoxValid = false;
    125     strokeBoundingBox = FloatRect();
    126 
    127     // When computing the strokeBoundingBox, we use the paintInvalidationRects of the container's children so that the container's stroke includes
    128     // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound
    129     // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also.
    130     for (RenderObject* current = container->slowFirstChild(); current; current = current->nextSibling()) {
    131         if (current->isSVGHiddenContainer())
    132             continue;
    133 
    134         // Don't include elements in the union that do not render.
    135         if (current->isSVGShape() && toRenderSVGShape(current)->isShapeEmpty())
    136             continue;
    137 
    138         const AffineTransform& transform = current->localToParentTransform();
    139         updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
    140             transform.mapRect(current->objectBoundingBox()));
    141         strokeBoundingBox.unite(transform.mapRect(current->paintInvalidationRectInLocalCoordinates()));
    142     }
    143 
    144     paintInvalidationBoundingBox = strokeBoundingBox;
    145 }
    146 
    147 bool SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(const FloatRect& localPaintInvalidationRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
    148 {
    149     return localTransform.mapRect(localPaintInvalidationRect).intersects(paintInfo.rect);
    150 }
    151 
    152 const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
    153 {
    154     while (start && !start->isSVGRoot())
    155         start = start->parent();
    156 
    157     ASSERT(start);
    158     ASSERT(start->isSVGRoot());
    159     return toRenderSVGRoot(start);
    160 }
    161 
    162 inline bool SVGRenderSupport::layoutSizeOfNearestViewportChanged(const RenderObject* start)
    163 {
    164     while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
    165         start = start->parent();
    166 
    167     ASSERT(start);
    168     ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
    169     if (start->isSVGViewportContainer())
    170         return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
    171 
    172     return toRenderSVGRoot(start)->isLayoutSizeChanged();
    173 }
    174 
    175 bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
    176 {
    177     while (ancestor && !ancestor->isSVGRoot()) {
    178         if (ancestor->isSVGTransformableContainer())
    179             return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
    180         if (ancestor->isSVGViewportContainer())
    181             return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
    182         ancestor = ancestor->parent();
    183     }
    184 
    185     return false;
    186 }
    187 
    188 void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
    189 {
    190     // When hasRelativeLengths() is false, no descendants have relative lengths
    191     // (hence no one is interested in viewport size changes).
    192     bool layoutSizeChanged = toSVGElement(start->node())->hasRelativeLengths()
    193         && layoutSizeOfNearestViewportChanged(start);
    194     bool transformChanged = transformToRootChanged(start);
    195 
    196     for (RenderObject* child = start->slowFirstChild(); child; child = child->nextSibling()) {
    197         bool forceLayout = selfNeedsLayout;
    198 
    199         if (transformChanged) {
    200             // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true).
    201             if (child->isSVGText())
    202                 toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    203             forceLayout = true;
    204         }
    205 
    206         if (layoutSizeChanged) {
    207             // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
    208             if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
    209                 if (element->hasRelativeLengths()) {
    210                     // FIXME: this should be done on invalidation, not during layout.
    211                     // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object
    212                     if (child->isSVGShape()) {
    213                         toRenderSVGShape(child)->setNeedsShapeUpdate();
    214                     } else if (child->isSVGText()) {
    215                         toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    216                         toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
    217                     }
    218 
    219                     forceLayout = true;
    220                 }
    221             }
    222         }
    223 
    224         SubtreeLayoutScope layoutScope(*child);
    225         // Resource containers are nasty: they can invalidate clients outside the current SubtreeLayoutScope.
    226         // Since they only care about viewport size changes (to resolve their relative lengths), we trigger
    227         // their invalidation directly from SVGSVGElement::svgAttributeChange() or at a higher
    228         // SubtreeLayoutScope (in RenderView::layout()).
    229         if (forceLayout && !child->isSVGResourceContainer())
    230             layoutScope.setNeedsLayout(child);
    231 
    232         // Lay out any referenced resources before the child.
    233         layoutResourcesIfNeeded(child);
    234         child->layoutIfNeeded();
    235     }
    236 }
    237 
    238 void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
    239 {
    240     ASSERT(object);
    241 
    242     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    243     if (resources)
    244         resources->layoutIfNeeded();
    245 }
    246 
    247 bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
    248 {
    249     // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
    250     ASSERT(!object->isDocumentElement());
    251 
    252     return object->style()->overflowX() == OHIDDEN || object->style()->overflowX() == OSCROLL;
    253 }
    254 
    255 void SVGRenderSupport::intersectPaintInvalidationRectWithResources(const RenderObject* renderer, FloatRect& paintInvalidationRect)
    256 {
    257     ASSERT(renderer);
    258 
    259     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
    260     if (!resources)
    261         return;
    262 
    263     if (RenderSVGResourceFilter* filter = resources->filter())
    264         paintInvalidationRect = filter->resourceBoundingBox(renderer);
    265 
    266     if (RenderSVGResourceClipper* clipper = resources->clipper())
    267         paintInvalidationRect.intersect(clipper->resourceBoundingBox(renderer));
    268 
    269     if (RenderSVGResourceMasker* masker = resources->masker())
    270         paintInvalidationRect.intersect(masker->resourceBoundingBox(renderer));
    271 }
    272 
    273 bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
    274 {
    275     // If any of this container's children need to be laid out, and a filter is applied
    276     // to the container, we need to issue paint invalidations the entire container.
    277     if (!object->normalChildNeedsLayout())
    278         return false;
    279 
    280     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    281     if (!resources || !resources->filter())
    282         return false;
    283 
    284     return true;
    285 }
    286 
    287 bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
    288 {
    289     ASSERT(object);
    290 
    291     // We just take clippers into account to determine if a point is on the node. The Specification may
    292     // change later and we also need to check maskers.
    293     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    294     if (!resources)
    295         return true;
    296 
    297     if (RenderSVGResourceClipper* clipper = resources->clipper())
    298         return clipper->hitTestClipContent(object->objectBoundingBox(), point);
    299 
    300     return true;
    301 }
    302 
    303 bool SVGRenderSupport::transformToUserSpaceAndCheckClipping(RenderObject* object, const AffineTransform& localTransform, const FloatPoint& pointInParent, FloatPoint& localPoint)
    304 {
    305     if (!localTransform.isInvertible())
    306         return false;
    307     localPoint = localTransform.inverse().mapPoint(pointInParent);
    308     return pointInClippingArea(object, localPoint);
    309 }
    310 
    311 void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
    312 {
    313     ASSERT(context);
    314     ASSERT(style);
    315     ASSERT(object);
    316     ASSERT(object->node());
    317     ASSERT(object->node()->isSVGElement());
    318 
    319     const SVGRenderStyle& svgStyle = style->svgStyle();
    320 
    321     SVGLengthContext lengthContext(toSVGElement(object->node()));
    322     context->setStrokeThickness(svgStyle.strokeWidth()->value(lengthContext));
    323     context->setLineCap(svgStyle.capStyle());
    324     context->setLineJoin(svgStyle.joinStyle());
    325     context->setMiterLimit(svgStyle.strokeMiterLimit());
    326 
    327     RefPtr<SVGLengthList> dashes = svgStyle.strokeDashArray();
    328     if (dashes->isEmpty())
    329         return;
    330 
    331     DashArray dashArray;
    332     SVGLengthList::ConstIterator it = dashes->begin();
    333     SVGLengthList::ConstIterator itEnd = dashes->end();
    334     for (; it != itEnd; ++it)
    335         dashArray.append(it->value(lengthContext));
    336 
    337     context->setLineDash(dashArray, svgStyle.strokeDashOffset()->value(lengthContext));
    338 }
    339 
    340 void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
    341 {
    342     ASSERT(strokeData);
    343     ASSERT(style);
    344     ASSERT(object);
    345     ASSERT(object->node());
    346     ASSERT(object->node()->isSVGElement());
    347 
    348     const SVGRenderStyle& svgStyle = style->svgStyle();
    349 
    350     SVGLengthContext lengthContext(toSVGElement(object->node()));
    351     strokeData->setThickness(svgStyle.strokeWidth()->value(lengthContext));
    352     strokeData->setLineCap(svgStyle.capStyle());
    353     strokeData->setLineJoin(svgStyle.joinStyle());
    354     strokeData->setMiterLimit(svgStyle.strokeMiterLimit());
    355 
    356     RefPtr<SVGLengthList> dashes = svgStyle.strokeDashArray();
    357     if (dashes->isEmpty())
    358         return;
    359 
    360     DashArray dashArray;
    361     size_t length = dashes->length();
    362     for (size_t i = 0; i < length; ++i)
    363         dashArray.append(dashes->at(i)->value(lengthContext));
    364 
    365     strokeData->setLineDash(dashArray, svgStyle.strokeDashOffset()->value(lengthContext));
    366 }
    367 
    368 void SVGRenderSupport::fillOrStrokePath(GraphicsContext* context, unsigned short resourceMode, const Path& path)
    369 {
    370     ASSERT(resourceMode != ApplyToDefaultMode);
    371 
    372     if (resourceMode & ApplyToFillMode)
    373         context->fillPath(path);
    374     if (resourceMode & ApplyToStrokeMode)
    375         context->strokePath(path);
    376 }
    377 
    378 bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
    379 {
    380     ASSERT(object->isText());
    381     // <br> is marked as text, but is not handled by the SVG rendering code-path.
    382     return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();
    383 }
    384 
    385 }
    386