Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / SVGRenderSupport.cpp
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
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 }