SVG: applyResource() should take a RenderElement&.
[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 "ElementIterator.h"
27 #include "FrameView.h"
28 #include "GraphicsContext.h"
29 #include "PatternAttributes.h"
30 #include "RenderSVGRoot.h"
31 #include "SVGFitToViewBox.h"
32 #include "SVGRenderSupport.h"
33 #include "SVGRenderingContext.h"
34
35 namespace WebCore {
36
37 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
38
39 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement& element)
40     : RenderSVGResourceContainer(element)
41     , m_shouldCollectPatternAttributes(true)
42 {
43 }
44
45 SVGPatternElement& RenderSVGResourcePattern::patternElement() const
46 {
47     return toSVGPatternElement(RenderSVGResourceContainer::element());
48 }
49
50 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
51 {
52     m_patternMap.clear();
53     m_shouldCollectPatternAttributes = true;
54     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
55 }
56
57 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
58 {
59     ASSERT(client);
60     m_patternMap.remove(client);
61     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
62 }
63
64 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
65 {
66     PatternData* currentData = m_patternMap.get(object);
67     if (currentData && currentData->pattern)
68         return currentData;
69
70     if (m_shouldCollectPatternAttributes) {
71         patternElement().synchronizeAnimatedSVGAttribute(anyQName());
72
73         m_attributes = PatternAttributes();
74         patternElement().collectPatternAttributes(m_attributes);
75         m_shouldCollectPatternAttributes = false;
76     }
77
78     // If we couldn't determine the pattern content element root, stop here.
79     if (!m_attributes.patternContentElement())
80         return 0;
81
82     // An empty viewBox disables rendering.
83     if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
84         return 0;
85
86     // Compute all necessary transformations to build the tile image & the pattern.
87     FloatRect tileBoundaries;
88     AffineTransform tileImageTransform;
89     if (!buildTileImageTransform(object, m_attributes, patternElement(), tileBoundaries, tileImageTransform))
90         return 0;
91
92     AffineTransform absoluteTransformIgnoringRotation;
93     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransformIgnoringRotation);
94
95     // Ignore 2D rotation, as it doesn't affect the size of the tile.
96     SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
97     FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
98     FloatRect clampedAbsoluteTileBoundaries;
99
100     // Scale the tile size to match the scale level of the patternTransform.
101     absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
102         static_cast<float>(m_attributes.patternTransform().yScale()));
103
104     // Build tile image.
105     OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
106     if (!tileImage)
107         return 0;
108
109     RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
110     if (!copiedImage)
111         return 0;
112
113     // Build pattern.
114     OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
115     patternData->pattern = Pattern::create(copiedImage, true, true);
116
117     // Compute pattern space transformation.
118     const IntSize tileImageSize = tileImage->logicalSize();
119     patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
120     patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
121
122     AffineTransform patternTransform = m_attributes.patternTransform();
123     if (!patternTransform.isIdentity())
124         patternData->transform = patternTransform * patternData->transform;
125
126     // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows.
127     if (resourceMode & ApplyToTextMode) {
128         AffineTransform additionalTextTransformation;
129         if (shouldTransformOnTextPainting(object, additionalTextTransformation))
130             patternData->transform *= additionalTextTransformation;
131     }
132     patternData->pattern->setPatternSpaceTransform(patternData->transform);
133
134     // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
135     // failures in the SVG image cache for example). To avoid having our PatternData deleted by
136     // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
137     return m_patternMap.set(object, patternData.release()).iterator->value.get();
138 }
139
140 bool RenderSVGResourcePattern::applyResource(RenderElement& renderer, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
141 {
142     ASSERT(style);
143     ASSERT(context);
144     ASSERT(resourceMode != ApplyToDefaultMode);
145
146     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
147     // then the given effect (e.g. a gradient or a filter) will be ignored.
148     FloatRect objectBoundingBox = renderer.objectBoundingBox();
149     if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
150         return false;
151
152     PatternData* patternData = buildPattern(&renderer, resourceMode);
153     if (!patternData)
154         return false;
155
156     // Draw pattern
157     context->save();
158
159     const SVGRenderStyle* svgStyle = style->svgStyle();
160     ASSERT(svgStyle);
161
162     if (resourceMode & ApplyToFillMode) {
163         context->setAlpha(svgStyle->fillOpacity());
164         context->setFillPattern(patternData->pattern);
165         context->setFillRule(svgStyle->fillRule());
166     } else if (resourceMode & ApplyToStrokeMode) {
167         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
168             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(&renderer, patternData->transform));
169         context->setAlpha(svgStyle->strokeOpacity());
170         context->setStrokePattern(patternData->pattern);
171         SVGRenderSupport::applyStrokeStyleToContext(context, style, &renderer);
172     }
173
174     if (resourceMode & ApplyToTextMode) {
175         if (resourceMode & ApplyToFillMode) {
176             context->setTextDrawingMode(TextModeFill);
177
178 #if USE(CG)
179             context->applyFillPattern();
180 #endif
181         } else if (resourceMode & ApplyToStrokeMode) {
182             context->setTextDrawingMode(TextModeStroke);
183
184 #if USE(CG)
185             context->applyStrokePattern();
186 #endif
187         }
188     }
189
190     return true;
191 }
192
193 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
194 {
195     ASSERT(context);
196     ASSERT(resourceMode != ApplyToDefaultMode);
197
198     if (resourceMode & ApplyToFillMode) {
199         if (path)
200             context->fillPath(*path);
201         else if (shape)
202             shape->fillShape(context);
203     }
204     if (resourceMode & ApplyToStrokeMode) {
205         if (path)
206             context->strokePath(*path);
207         else if (shape)
208             shape->strokeShape(context);
209     }
210
211     context->restore();
212 }
213
214 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
215                                                    const FloatRect& objectBoundingBox,
216                                                    const SVGPatternElement& patternElement)
217 {
218     return SVGLengthContext::resolveRectangle(&patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
219 }
220
221 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
222                                                        const PatternAttributes& attributes,
223                                                        const SVGPatternElement& patternElement,
224                                                        FloatRect& patternBoundaries,
225                                                        AffineTransform& tileImageTransform) const
226 {
227     ASSERT(renderer);
228
229     FloatRect objectBoundingBox = renderer->objectBoundingBox();
230     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 
231     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
232         return false;
233
234     AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
235
236     // Apply viewBox/objectBoundingBox transformations.
237     if (!viewBoxCTM.isIdentity())
238         tileImageTransform = viewBoxCTM;
239     else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
240         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
241
242     return true;
243 }
244
245 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
246                                                                   const FloatRect& tileBoundaries,
247                                                                   const FloatRect& absoluteTileBoundaries,
248                                                                   const AffineTransform& tileImageTransform,
249                                                                   FloatRect& clampedAbsoluteTileBoundaries) const
250 {
251     clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
252
253     OwnPtr<ImageBuffer> tileImage;
254
255     if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated))
256         return nullptr;
257
258     GraphicsContext* tileImageContext = tileImage->context();
259     ASSERT(tileImageContext);
260
261     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
262     tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
263                                       clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
264
265     // Apply tile image transformations.
266     if (!tileImageTransform.isIdentity())
267         tileImageContext->concatCTM(tileImageTransform);
268
269     AffineTransform contentTransformation;
270     if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
271         contentTransformation = tileImageTransform;
272
273     // Draw the content into the ImageBuffer.
274     auto children = childrenOfType<SVGElement>(*attributes.patternContentElement());
275     for (auto it = children.begin(), end = children.end(); it != end; ++it) {
276         const SVGElement& child = *it;
277         if (!child.renderer())
278             continue;
279         if (child.renderer()->needsLayout())
280             return nullptr;
281         SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), *child.renderer(), contentTransformation);
282     }
283
284     return tileImage.release();
285 }
286
287 }
288
289 #endif