2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
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.
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.
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.
22 #include "RenderSVGResourcePattern.h"
24 #include "ElementIterator.h"
25 #include "FrameView.h"
26 #include "GraphicsContext.h"
27 #include "RenderSVGRoot.h"
28 #include "SVGFitToViewBox.h"
29 #include "SVGRenderingContext.h"
33 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement& element, Ref<RenderStyle>&& style)
34 : RenderSVGResourceContainer(element, WTF::move(style))
35 , m_shouldCollectPatternAttributes(true)
39 SVGPatternElement& RenderSVGResourcePattern::patternElement() const
41 return downcast<SVGPatternElement>(RenderSVGResourceContainer::element());
44 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
47 m_shouldCollectPatternAttributes = true;
48 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
51 void RenderSVGResourcePattern::removeClientFromCache(RenderElement& client, bool markForInvalidation)
53 m_patternMap.remove(&client);
54 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
57 PatternData* RenderSVGResourcePattern::buildPattern(RenderElement& renderer, unsigned short resourceMode)
59 PatternData* currentData = m_patternMap.get(&renderer);
60 if (currentData && currentData->pattern)
63 if (m_shouldCollectPatternAttributes) {
64 patternElement().synchronizeAnimatedSVGAttribute(anyQName());
66 m_attributes = PatternAttributes();
67 patternElement().collectPatternAttributes(m_attributes);
68 m_shouldCollectPatternAttributes = false;
71 // If we couldn't determine the pattern content element root, stop here.
72 if (!m_attributes.patternContentElement())
75 // An empty viewBox disables rendering.
76 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
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))
85 AffineTransform absoluteTransformIgnoringRotation = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer);
87 // Ignore 2D rotation, as it doesn't affect the size of the tile.
88 SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
89 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
90 FloatRect clampedAbsoluteTileBoundaries;
92 // Scale the tile size to match the scale level of the patternTransform.
93 absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
94 static_cast<float>(m_attributes.patternTransform().yScale()));
97 auto tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
101 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
106 auto patternData = std::make_unique<PatternData>();
107 patternData->pattern = Pattern::create(copiedImage, true, true);
109 // Compute pattern space transformation.
110 const IntSize tileImageSize = tileImage->logicalSize();
111 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
112 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
114 AffineTransform patternTransform = m_attributes.patternTransform();
115 if (!patternTransform.isIdentity())
116 patternData->transform = patternTransform * patternData->transform;
118 // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows.
119 if (resourceMode & ApplyToTextMode) {
120 AffineTransform additionalTextTransformation;
121 if (shouldTransformOnTextPainting(renderer, additionalTextTransformation))
122 patternData->transform *= additionalTextTransformation;
124 patternData->pattern->setPatternSpaceTransform(patternData->transform);
126 // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
127 // failures in the SVG image cache for example). To avoid having our PatternData deleted by
128 // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
129 return m_patternMap.set(&renderer, WTF::move(patternData)).iterator->value.get();
132 bool RenderSVGResourcePattern::applyResource(RenderElement& renderer, const RenderStyle& style, GraphicsContext*& context, unsigned short resourceMode)
135 ASSERT(resourceMode != ApplyToDefaultMode);
137 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
138 // then the given effect (e.g. a gradient or a filter) will be ignored.
139 FloatRect objectBoundingBox = renderer.objectBoundingBox();
140 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
143 PatternData* patternData = buildPattern(renderer, resourceMode);
150 const SVGRenderStyle& svgStyle = style.svgStyle();
152 if (resourceMode & ApplyToFillMode) {
153 context->setAlpha(svgStyle.fillOpacity());
154 context->setFillPattern(*patternData->pattern);
155 context->setFillRule(svgStyle.fillRule());
156 } else if (resourceMode & ApplyToStrokeMode) {
157 if (svgStyle.vectorEffect() == VE_NON_SCALING_STROKE)
158 patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(&renderer, patternData->transform));
159 context->setAlpha(svgStyle.strokeOpacity());
160 context->setStrokePattern(*patternData->pattern);
161 SVGRenderSupport::applyStrokeStyleToContext(context, style, renderer);
164 if (resourceMode & ApplyToTextMode) {
165 if (resourceMode & ApplyToFillMode) {
166 context->setTextDrawingMode(TextModeFill);
169 context->applyFillPattern();
171 } else if (resourceMode & ApplyToStrokeMode) {
172 context->setTextDrawingMode(TextModeStroke);
175 context->applyStrokePattern();
183 void RenderSVGResourcePattern::postApplyResource(RenderElement&, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
186 ASSERT(resourceMode != ApplyToDefaultMode);
188 if (resourceMode & ApplyToFillMode) {
190 context->fillPath(*path);
192 shape->fillShape(context);
194 if (resourceMode & ApplyToStrokeMode) {
196 context->strokePath(*path);
198 shape->strokeShape(context);
204 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
205 const FloatRect& objectBoundingBox,
206 const SVGPatternElement& patternElement)
208 return SVGLengthContext::resolveRectangle(&patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
211 bool RenderSVGResourcePattern::buildTileImageTransform(RenderElement& renderer,
212 const PatternAttributes& attributes,
213 const SVGPatternElement& patternElement,
214 FloatRect& patternBoundaries,
215 AffineTransform& tileImageTransform) const
217 FloatRect objectBoundingBox = renderer.objectBoundingBox();
218 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
219 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
222 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
224 // Apply viewBox/objectBoundingBox transformations.
225 if (!viewBoxCTM.isIdentity())
226 tileImageTransform = viewBoxCTM;
227 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
228 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
233 std::unique_ptr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries) const
235 clampedAbsoluteTileBoundaries = ImageBuffer::clampedRect(absoluteTileBoundaries);
236 auto tileImage = SVGRenderingContext::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, ColorSpaceDeviceRGB, Unaccelerated);
240 GraphicsContext* tileImageContext = tileImage->context();
241 ASSERT(tileImageContext);
243 // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
244 tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
245 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
247 // Apply tile image transformations.
248 if (!tileImageTransform.isIdentity())
249 tileImageContext->concatCTM(tileImageTransform);
251 AffineTransform contentTransformation;
252 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
253 contentTransformation = tileImageTransform;
255 // Draw the content into the ImageBuffer.
256 for (auto& child : childrenOfType<SVGElement>(*attributes.patternContentElement())) {
257 if (!child.renderer())
259 if (child.renderer()->needsLayout())
261 SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), *child.renderer(), contentTransformation);