initial import
[vuplus_webkit] / 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 RenderSVGResourcePattern::~RenderSVGResourcePattern()
44 {
45     if (m_pattern.isEmpty())
46         return;
47
48     deleteAllValues(m_pattern);
49     m_pattern.clear();
50 }
51
52 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
53 {
54     if (!m_pattern.isEmpty()) {
55         deleteAllValues(m_pattern);
56         m_pattern.clear();
57     }
58
59     m_shouldCollectPatternAttributes = true;
60     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
61 }
62
63 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
64 {
65     ASSERT(client);
66
67     if (m_pattern.contains(client))
68         delete m_pattern.take(client);
69
70     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
71 }
72
73 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
74 {
75     ASSERT(object);
76     ASSERT(style);
77     ASSERT(context);
78     ASSERT(resourceMode != ApplyToDefaultMode);
79
80     // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
81     // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property
82     // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
83     // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
84     SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
85     if (!patternElement)
86         return false;
87
88     if (m_shouldCollectPatternAttributes) {
89         patternElement->updateAnimatedSVGAttribute(anyQName());
90
91         m_attributes = PatternAttributes();
92         patternElement->collectPatternAttributes(m_attributes);
93         m_shouldCollectPatternAttributes = false;
94     }
95
96     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
97     // then the given effect (e.g. a gradient or a filter) will be ignored.
98     FloatRect objectBoundingBox = object->objectBoundingBox();
99     if (m_attributes.boundingBoxMode() && objectBoundingBox.isEmpty())
100         return false;
101
102     if (!m_pattern.contains(object))
103         m_pattern.set(object, new PatternData);
104
105     PatternData* patternData = m_pattern.get(object);
106     if (!patternData->pattern) {
107         // If we couldn't determine the pattern content element root, stop here.
108         if (!m_attributes.patternContentElement())
109             return false;
110
111         // Compute all necessary transformations to build the tile image & the pattern.
112         FloatRect tileBoundaries;
113         AffineTransform tileImageTransform;
114         if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
115             return false;
116
117         AffineTransform absoluteTransformIgnoringRotation;
118         SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation);
119
120         // Ignore 2D rotation, as it doesn't affect the size of the tile.
121         SVGImageBufferTools::clear2DRotation(absoluteTransformIgnoringRotation);
122         FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
123
124         // Build tile image.
125         OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform);
126         if (!tileImage)
127             return false;
128
129         RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
130         if (!copiedImage)
131             return false;
132
133         // Build pattern.
134         patternData->pattern = Pattern::create(copiedImage, true, true);
135         if (!patternData->pattern)
136             return false;
137
138         // Compute pattern space transformation.
139         patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
140         patternData->transform.scale(tileBoundaries.width() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height());
141
142         AffineTransform patternTransform = m_attributes.patternTransform();
143         if (!patternTransform.isIdentity())
144             patternData->transform = patternTransform * patternData->transform;
145
146         patternData->pattern->setPatternSpaceTransform(patternData->transform);
147     }
148
149     // Draw pattern
150     context->save();
151
152     const SVGRenderStyle* svgStyle = style->svgStyle();
153     ASSERT(svgStyle);
154
155     if (resourceMode & ApplyToFillMode) {
156         context->setAlpha(svgStyle->fillOpacity());
157         context->setFillPattern(patternData->pattern);
158         context->setFillRule(svgStyle->fillRule());
159     } else if (resourceMode & ApplyToStrokeMode) {
160         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
161             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
162         context->setAlpha(svgStyle->strokeOpacity());
163         context->setStrokePattern(patternData->pattern);
164         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
165     }
166
167     if (resourceMode & ApplyToTextMode) {
168         if (resourceMode & ApplyToFillMode) {
169             context->setTextDrawingMode(TextModeFill);
170
171 #if USE(CG)
172             context->applyFillPattern();
173 #endif
174         } else if (resourceMode & ApplyToStrokeMode) {
175             context->setTextDrawingMode(TextModeStroke);
176
177 #if USE(CG)
178             context->applyStrokePattern();
179 #endif
180         }
181     }
182
183     return true;
184 }
185
186 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
187 {
188     ASSERT(context);
189     ASSERT(resourceMode != ApplyToDefaultMode);
190
191     if (path && !(resourceMode & ApplyToTextMode)) {
192         if (resourceMode & ApplyToFillMode)
193             context->fillPath(*path);
194         else if (resourceMode & ApplyToStrokeMode)
195             context->strokePath(*path);
196     }
197
198     context->restore();
199 }
200
201 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
202                                                    const FloatRect& objectBoundingBox,
203                                                    const SVGPatternElement* patternElement)
204 {
205     ASSERT(patternElement);
206
207     if (attributes.boundingBoxMode())
208         return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
209                          attributes.y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
210                          attributes.width().valueAsPercentage() * objectBoundingBox.width(),
211                          attributes.height().valueAsPercentage() * objectBoundingBox.height());
212
213     return FloatRect(attributes.x().value(patternElement),
214                      attributes.y().value(patternElement),
215                      attributes.width().value(patternElement),
216                      attributes.height().value(patternElement));
217 }
218
219 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
220                                                        const PatternAttributes& attributes,
221                                                        const SVGPatternElement* patternElement,
222                                                        FloatRect& patternBoundaries,
223                                                        AffineTransform& tileImageTransform) const
224 {
225     ASSERT(renderer);
226     ASSERT(patternElement);
227
228     FloatRect objectBoundingBox = renderer->objectBoundingBox();
229     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 
230     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
231         return false;
232
233     AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
234
235     // Apply viewBox/objectBoundingBox transformations.
236     if (!viewBoxCTM.isIdentity())
237         tileImageTransform = viewBoxCTM;
238     else if (attributes.boundingBoxModeContent())
239         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
240
241     return true;
242 }
243
244 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object,
245                                                                   const PatternAttributes& attributes,
246                                                                   const FloatRect& tileBoundaries,
247                                                                   const FloatRect& absoluteTileBoundaries,
248                                                                   const AffineTransform& tileImageTransform) const
249 {
250     ASSERT(object);
251
252     // Clamp tile image size against SVG viewport size, as last resort, to avoid allocating huge image buffers.
253     FloatRect contentBoxRect = SVGRenderSupport::findTreeRootObject(object)->contentBoxRect();
254
255     FloatRect clampedAbsoluteTileBoundaries = absoluteTileBoundaries;
256     if (clampedAbsoluteTileBoundaries.width() > contentBoxRect.width())
257         clampedAbsoluteTileBoundaries.setWidth(contentBoxRect.width());
258
259     if (clampedAbsoluteTileBoundaries.height() > contentBoxRect.height())
260         clampedAbsoluteTileBoundaries.setHeight(contentBoxRect.height());
261
262     OwnPtr<ImageBuffer> tileImage;
263
264     if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB))
265         return nullptr;
266
267     GraphicsContext* tileImageContext = tileImage->context();
268     ASSERT(tileImageContext);
269
270     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
271     tileImageContext->scale(FloatSize(absoluteTileBoundaries.width() / tileBoundaries.width(),
272                                       absoluteTileBoundaries.height() / tileBoundaries.height()));
273
274     // Apply tile image transformations.
275     if (!tileImageTransform.isIdentity())
276         tileImageContext->concatCTM(tileImageTransform);
277
278     AffineTransform contentTransformation;
279     if (attributes.boundingBoxModeContent())
280         contentTransformation = tileImageTransform;
281
282     // Draw the content into the ImageBuffer.
283     for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
284         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
285             continue;
286         SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
287     }
288
289     return tileImage.release();
290 }
291
292 }
293
294 #endif