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.
24 #include "RenderSVGResourcePattern.h"
26 #include "FrameView.h"
27 #include "GraphicsContext.h"
28 #include "PatternAttributes.h"
29 #include "RenderSVGRoot.h"
30 #include "SVGImageBufferTools.h"
31 #include "SVGRenderSupport.h"
35 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
37 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
38 : RenderSVGResourceContainer(node)
39 , m_shouldCollectPatternAttributes(true)
43 RenderSVGResourcePattern::~RenderSVGResourcePattern()
45 if (m_pattern.isEmpty())
48 deleteAllValues(m_pattern);
52 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
54 if (!m_pattern.isEmpty()) {
55 deleteAllValues(m_pattern);
59 m_shouldCollectPatternAttributes = true;
60 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
63 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
67 if (m_pattern.contains(client))
68 delete m_pattern.take(client);
70 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
73 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
78 ASSERT(resourceMode != ApplyToDefaultMode);
80 // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
81 // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property
82 // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
83 // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
84 SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
88 if (m_shouldCollectPatternAttributes) {
89 patternElement->updateAnimatedSVGAttribute(anyQName());
91 m_attributes = PatternAttributes();
92 patternElement->collectPatternAttributes(m_attributes);
93 m_shouldCollectPatternAttributes = false;
96 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
97 // then the given effect (e.g. a gradient or a filter) will be ignored.
98 FloatRect objectBoundingBox = object->objectBoundingBox();
99 if (m_attributes.boundingBoxMode() && objectBoundingBox.isEmpty())
102 if (!m_pattern.contains(object))
103 m_pattern.set(object, new PatternData);
105 PatternData* patternData = m_pattern.get(object);
106 if (!patternData->pattern) {
107 // If we couldn't determine the pattern content element root, stop here.
108 if (!m_attributes.patternContentElement())
111 // Compute all necessary transformations to build the tile image & the pattern.
112 FloatRect tileBoundaries;
113 AffineTransform tileImageTransform;
114 if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
117 AffineTransform absoluteTransformIgnoringRotation;
118 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation);
120 // Ignore 2D rotation, as it doesn't affect the size of the tile.
121 SVGImageBufferTools::clear2DRotation(absoluteTransformIgnoringRotation);
122 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
125 OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform);
129 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
134 patternData->pattern = Pattern::create(copiedImage, true, true);
135 if (!patternData->pattern)
138 // Compute pattern space transformation.
139 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
140 patternData->transform.scale(tileBoundaries.width() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height());
142 AffineTransform patternTransform = m_attributes.patternTransform();
143 if (!patternTransform.isIdentity())
144 patternData->transform = patternTransform * patternData->transform;
146 patternData->pattern->setPatternSpaceTransform(patternData->transform);
152 const SVGRenderStyle* svgStyle = style->svgStyle();
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(object, patternData->transform));
162 context->setAlpha(svgStyle->strokeOpacity());
163 context->setStrokePattern(patternData->pattern);
164 SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
167 if (resourceMode & ApplyToTextMode) {
168 if (resourceMode & ApplyToFillMode) {
169 context->setTextDrawingMode(TextModeFill);
172 context->applyFillPattern();
174 } else if (resourceMode & ApplyToStrokeMode) {
175 context->setTextDrawingMode(TextModeStroke);
178 context->applyStrokePattern();
186 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
189 ASSERT(resourceMode != ApplyToDefaultMode);
191 if (path && !(resourceMode & ApplyToTextMode)) {
192 if (resourceMode & ApplyToFillMode)
193 context->fillPath(*path);
194 else if (resourceMode & ApplyToStrokeMode)
195 context->strokePath(*path);
201 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
202 const FloatRect& objectBoundingBox,
203 const SVGPatternElement* patternElement)
205 ASSERT(patternElement);
207 if (attributes.boundingBoxMode())
208 return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
209 attributes.y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
210 attributes.width().valueAsPercentage() * objectBoundingBox.width(),
211 attributes.height().valueAsPercentage() * objectBoundingBox.height());
213 return FloatRect(attributes.x().value(patternElement),
214 attributes.y().value(patternElement),
215 attributes.width().value(patternElement),
216 attributes.height().value(patternElement));
219 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
220 const PatternAttributes& attributes,
221 const SVGPatternElement* patternElement,
222 FloatRect& patternBoundaries,
223 AffineTransform& tileImageTransform) const
226 ASSERT(patternElement);
228 FloatRect objectBoundingBox = renderer->objectBoundingBox();
229 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
230 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
233 AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
235 // Apply viewBox/objectBoundingBox transformations.
236 if (!viewBoxCTM.isIdentity())
237 tileImageTransform = viewBoxCTM;
238 else if (attributes.boundingBoxModeContent())
239 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
244 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object,
245 const PatternAttributes& attributes,
246 const FloatRect& tileBoundaries,
247 const FloatRect& absoluteTileBoundaries,
248 const AffineTransform& tileImageTransform) const
252 // Clamp tile image size against SVG viewport size, as last resort, to avoid allocating huge image buffers.
253 FloatRect contentBoxRect = SVGRenderSupport::findTreeRootObject(object)->contentBoxRect();
255 FloatRect clampedAbsoluteTileBoundaries = absoluteTileBoundaries;
256 if (clampedAbsoluteTileBoundaries.width() > contentBoxRect.width())
257 clampedAbsoluteTileBoundaries.setWidth(contentBoxRect.width());
259 if (clampedAbsoluteTileBoundaries.height() > contentBoxRect.height())
260 clampedAbsoluteTileBoundaries.setHeight(contentBoxRect.height());
262 OwnPtr<ImageBuffer> tileImage;
264 if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB))
267 GraphicsContext* tileImageContext = tileImage->context();
268 ASSERT(tileImageContext);
270 // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
271 tileImageContext->scale(FloatSize(absoluteTileBoundaries.width() / tileBoundaries.width(),
272 absoluteTileBoundaries.height() / tileBoundaries.height()));
274 // Apply tile image transformations.
275 if (!tileImageTransform.isIdentity())
276 tileImageContext->concatCTM(tileImageTransform);
278 AffineTransform contentTransformation;
279 if (attributes.boundingBoxModeContent())
280 contentTransformation = tileImageTransform;
282 // Draw the content into the ImageBuffer.
283 for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
284 if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
286 SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
289 return tileImage.release();