1/*
2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Google, Inc.  All rights reserved.
6 * Copyright (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#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
46namespace blink {
47
48LayoutRect 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
63void 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
70void 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
86const 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.
106inline 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
121void 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
147bool SVGRenderSupport::paintInfoIntersectsPaintInvalidationRect(const FloatRect& localPaintInvalidationRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
148{
149    return localTransform.mapRect(localPaintInvalidationRect).intersects(paintInfo.rect);
150}
151
152const 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
162inline 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
175bool 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
188void 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
238void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
239{
240    ASSERT(object);
241
242    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
243    if (resources)
244        resources->layoutIfNeeded();
245}
246
247bool 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
255void 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
273bool 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
287bool 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
303bool 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
311void 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
340void 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
368void 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
378bool 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