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