2012-01-27 Branimir Lambov <blambov@google.com>
[WebKit-https.git] / Source / WebCore / 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 #if ENABLE(SVG)
24 #include "RenderSVGResourcePattern.h"
25
26 #include "FrameView.h"
27 #include "GraphicsContext.h"
28 #include "PatternAttributes.h"
29 #include "RenderSVGRoot.h"
30 #include "SVGImageBufferTools.h"
31 #include "SVGRenderSupport.h"
32
33 namespace WebCore {
34
35 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
36
37 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
38     : RenderSVGResourceContainer(node)
39     , m_shouldCollectPatternAttributes(true)
40 {
41 }
42
43 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
44 {
45     m_patternMap.clear();
46     m_shouldCollectPatternAttributes = true;
47     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
48 }
49
50 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
51 {
52     ASSERT(client);
53     m_patternMap.remove(client);
54     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
55 }
56
57 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
58 {
59     ASSERT(object);
60     ASSERT(style);
61     ASSERT(context);
62     ASSERT(resourceMode != ApplyToDefaultMode);
63
64     // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
65     // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property
66     // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
67     // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
68     SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
69     if (!patternElement)
70         return false;
71
72     if (m_shouldCollectPatternAttributes) {
73         patternElement->updateAnimatedSVGAttribute(anyQName());
74
75         m_attributes = PatternAttributes();
76         patternElement->collectPatternAttributes(m_attributes);
77         m_shouldCollectPatternAttributes = false;
78     }
79
80     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
81     // then the given effect (e.g. a gradient or a filter) will be ignored.
82     FloatRect objectBoundingBox = object->objectBoundingBox();
83     if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
84         return false;
85
86     OwnPtr<PatternData>& patternData = m_patternMap.add(object, nullptr).first->second;
87     if (!patternData)
88         patternData = adoptPtr(new PatternData);
89
90     if (!patternData->pattern) {
91         // If we couldn't determine the pattern content element root, stop here.
92         if (!m_attributes.patternContentElement())
93             return false;
94
95         // Compute all necessary transformations to build the tile image & the pattern.
96         FloatRect tileBoundaries;
97         AffineTransform tileImageTransform;
98         if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
99             return false;
100
101         AffineTransform absoluteTransformIgnoringRotation;
102         SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation);
103
104         // Ignore 2D rotation, as it doesn't affect the size of the tile.
105         SVGImageBufferTools::clear2DRotation(absoluteTransformIgnoringRotation);
106         FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
107         FloatRect clampedAbsoluteTileBoundaries;
108
109         // Scale the tile size to match the scale level of the patternTransform.
110         absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
111             static_cast<float>(m_attributes.patternTransform().yScale()));
112
113         // Build tile image.
114         OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
115         if (!tileImage)
116             return false;
117
118         RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
119         if (!copiedImage)
120             return false;
121
122         // Build pattern.
123         patternData->pattern = Pattern::create(copiedImage, true, true);
124         if (!patternData->pattern)
125             return false;
126
127         // Compute pattern space transformation.
128         patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
129         patternData->transform.scale(tileBoundaries.width() / clampedAbsoluteTileBoundaries.width(), tileBoundaries.height() / clampedAbsoluteTileBoundaries.height());
130
131         AffineTransform patternTransform = m_attributes.patternTransform();
132         if (!patternTransform.isIdentity())
133             patternData->transform = patternTransform * patternData->transform;
134
135         patternData->pattern->setPatternSpaceTransform(patternData->transform);
136     }
137
138     // Draw pattern
139     context->save();
140
141     const SVGRenderStyle* svgStyle = style->svgStyle();
142     ASSERT(svgStyle);
143
144     if (resourceMode & ApplyToFillMode) {
145         context->setAlpha(svgStyle->fillOpacity());
146         context->setFillPattern(patternData->pattern);
147         context->setFillRule(svgStyle->fillRule());
148     } else if (resourceMode & ApplyToStrokeMode) {
149         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
150             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
151         context->setAlpha(svgStyle->strokeOpacity());
152         context->setStrokePattern(patternData->pattern);
153         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
154     }
155
156     if (resourceMode & ApplyToTextMode) {
157         if (resourceMode & ApplyToFillMode) {
158             context->setTextDrawingMode(TextModeFill);
159
160 #if USE(CG)
161             context->applyFillPattern();
162 #endif
163         } else if (resourceMode & ApplyToStrokeMode) {
164             context->setTextDrawingMode(TextModeStroke);
165
166 #if USE(CG)
167             context->applyStrokePattern();
168 #endif
169         }
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 = patternElement->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 = SVGImageBufferTools::clampedAbsoluteTargetRect(absoluteTileBoundaries);
236
237     OwnPtr<ImageBuffer> tileImage;
238
239     if (!SVGImageBufferTools::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated))
240         return nullptr;
241
242     GraphicsContext* tileImageContext = tileImage->context();
243     ASSERT(tileImageContext);
244
245     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
246     tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
247                                       clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
248
249     // Apply tile image transformations.
250     if (!tileImageTransform.isIdentity())
251         tileImageContext->concatCTM(tileImageTransform);
252
253     AffineTransform contentTransformation;
254     if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
255         contentTransformation = tileImageTransform;
256
257     // Draw the content into the ImageBuffer.
258     for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
259         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
260             continue;
261         if (node->renderer()->needsLayout())
262             return nullptr;
263         SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
264     }
265
266     return tileImage.release();
267 }
268
269 }
270
271 #endif