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