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