Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / RenderSVGResourcePattern.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22
23 #include "core/rendering/svg/RenderSVGResourcePattern.h"
24
25 #include "core/rendering/svg/SVGRenderSupport.h"
26 #include "core/rendering/svg/SVGRenderingContext.h"
27 #include "core/svg/SVGFitToViewBox.h"
28 #include "platform/graphics/GraphicsContext.h"
29
30 namespace WebCore {
31
32 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
33
34 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
35     : RenderSVGResourceContainer(node)
36     , m_shouldCollectPatternAttributes(true)
37 {
38 }
39
40 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
41 {
42     m_patternMap.clear();
43     m_shouldCollectPatternAttributes = true;
44     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
45 }
46
47 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
48 {
49     ASSERT(client);
50     m_patternMap.remove(client);
51     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
52 }
53
54 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
55 {
56     ASSERT(object);
57     PatternData* currentData = m_patternMap.get(object);
58     if (currentData && currentData->pattern)
59         return currentData;
60
61     SVGPatternElement* patternElement = toSVGPatternElement(element());
62     if (!patternElement)
63         return 0;
64
65     if (m_shouldCollectPatternAttributes) {
66         patternElement->synchronizeAnimatedSVGAttribute(anyQName());
67
68         m_attributes = PatternAttributes();
69         patternElement->collectPatternAttributes(m_attributes);
70         m_shouldCollectPatternAttributes = false;
71     }
72
73     // If we couldn't determine the pattern content element root, stop here.
74     if (!m_attributes.patternContentElement())
75         return 0;
76
77     // An empty viewBox disables rendering.
78     if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
79         return 0;
80
81     // Compute all necessary transformations to build the tile image & the pattern.
82     FloatRect tileBoundaries;
83     AffineTransform tileImageTransform;
84     if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
85         return 0;
86
87     AffineTransform absoluteTransformIgnoringRotation;
88     SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransformIgnoringRotation);
89
90     // Ignore 2D rotation, as it doesn't affect the size of the tile.
91     SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
92     FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
93     FloatRect clampedAbsoluteTileBoundaries;
94
95     // Scale the tile size to match the scale level of the patternTransform.
96     absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
97         static_cast<float>(m_attributes.patternTransform().yScale()));
98
99     // Build tile image.
100     OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
101     if (!tileImage)
102         return 0;
103
104     RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
105     if (!copiedImage)
106         return 0;
107
108     // Build pattern.
109     OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
110     patternData->pattern = Pattern::create(copiedImage, true, true);
111
112     // Compute pattern space transformation.
113     const IntSize tileImageSize = tileImage->size();
114     patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
115     patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
116
117     AffineTransform patternTransform = m_attributes.patternTransform();
118     if (!patternTransform.isIdentity())
119         patternData->transform = patternTransform * patternData->transform;
120
121     // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
122     // failures in the SVG image cache for example). To avoid having our PatternData deleted by
123     // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
124     return m_patternMap.set(object, patternData.release()).storedValue->value.get();
125 }
126
127 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
128 {
129     ASSERT(object);
130     ASSERT(style);
131     ASSERT(context);
132     ASSERT(resourceMode != ApplyToDefaultMode);
133
134     clearInvalidationMask();
135
136     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
137     // then the given effect (e.g. a gradient or a filter) will be ignored.
138     FloatRect objectBoundingBox = object->objectBoundingBox();
139     if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
140         return false;
141
142     PatternData* patternData = buildPattern(object, resourceMode);
143     if (!patternData)
144         return false;
145
146     const SVGRenderStyle* svgStyle = style->svgStyle();
147     ASSERT(svgStyle);
148
149     AffineTransform computedPatternSpaceTransform = computeResourceSpaceTransform(object, patternData->transform, svgStyle, resourceMode);
150     patternData->pattern->setPatternSpaceTransform(computedPatternSpaceTransform);
151
152     // Draw pattern
153     context->save();
154
155     if (resourceMode & ApplyToFillMode) {
156         context->setAlphaAsFloat(svgStyle->fillOpacity());
157         context->setFillPattern(patternData->pattern);
158         context->setFillRule(svgStyle->fillRule());
159     } else if (resourceMode & ApplyToStrokeMode) {
160         context->setAlphaAsFloat(svgStyle->strokeOpacity());
161         context->setStrokePattern(patternData->pattern);
162         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
163     }
164
165     if (resourceMode & ApplyToTextMode) {
166         if (resourceMode & ApplyToFillMode)
167             context->setTextDrawingMode(TextModeFill);
168         else if (resourceMode & ApplyToStrokeMode)
169             context->setTextDrawingMode(TextModeStroke);
170     }
171
172     return true;
173 }
174
175 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
176 {
177     ASSERT(context);
178     ASSERT(resourceMode != ApplyToDefaultMode);
179
180     if (resourceMode & ApplyToFillMode) {
181         if (path)
182             context->fillPath(*path);
183         else if (shape)
184             shape->fillShape(context);
185     }
186     if (resourceMode & ApplyToStrokeMode) {
187         if (path)
188             context->strokePath(*path);
189         else if (shape)
190             shape->strokeShape(context);
191     }
192
193     context->restore();
194 }
195
196 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
197                                                    const FloatRect& objectBoundingBox,
198                                                    const SVGPatternElement* patternElement)
199 {
200     ASSERT(patternElement);
201     return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
202 }
203
204 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
205                                                        const PatternAttributes& attributes,
206                                                        const SVGPatternElement* patternElement,
207                                                        FloatRect& patternBoundaries,
208                                                        AffineTransform& tileImageTransform) const
209 {
210     ASSERT(renderer);
211     ASSERT(patternElement);
212
213     FloatRect objectBoundingBox = renderer->objectBoundingBox();
214     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
215     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
216         return false;
217
218     AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
219
220     // Apply viewBox/objectBoundingBox transformations.
221     if (!viewBoxCTM.isIdentity())
222         tileImageTransform = viewBoxCTM;
223     else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
224         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
225
226     return true;
227 }
228
229 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
230                                                                   const FloatRect& tileBoundaries,
231                                                                   const FloatRect& absoluteTileBoundaries,
232                                                                   const AffineTransform& tileImageTransform,
233                                                                   FloatRect& clampedAbsoluteTileBoundaries) const
234 {
235     clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
236
237     IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size()));
238     if (imageSize.isEmpty())
239         return nullptr;
240     OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
241     if (!tileImage)
242         return nullptr;
243
244     GraphicsContext* tileImageContext = tileImage->context();
245     ASSERT(tileImageContext);
246     IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
247     tileImageContext->scale(FloatSize(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height()));
248
249     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
250     tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
251                                       clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
252
253     // Apply tile image transformations.
254     if (!tileImageTransform.isIdentity())
255         tileImageContext->concatCTM(tileImageTransform);
256
257     AffineTransform contentTransformation;
258     if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
259         contentTransformation = tileImageTransform;
260
261     // Draw the content into the ImageBuffer.
262     for (Element* element = ElementTraversal::firstWithin(*attributes.patternContentElement()); element; element = ElementTraversal::nextSibling(*element)) {
263         if (!element->isSVGElement() || !element->renderer())
264             continue;
265         if (element->renderer()->needsLayout())
266             return nullptr;
267         SVGRenderingContext::renderSubtree(tileImage->context(), element->renderer(), contentTransformation);
268     }
269
270     return tileImage.release();
271 }
272
273 }