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.
23 #include "core/rendering/svg/RenderSVGResourcePattern.h"
25 #include "core/rendering/svg/SVGRenderSupport.h"
26 #include "core/rendering/svg/SVGRenderingContext.h"
27 #include "core/svg/SVGFitToViewBox.h"
28 #include "platform/graphics/GraphicsContext.h"
32 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
34 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
35 : RenderSVGResourceContainer(node)
36 , m_shouldCollectPatternAttributes(true)
40 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
43 m_shouldCollectPatternAttributes = true;
44 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
47 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
50 m_patternMap.remove(client);
51 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
54 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
57 PatternData* currentData = m_patternMap.get(object);
58 if (currentData && currentData->pattern)
61 SVGPatternElement* patternElement = toSVGPatternElement(element());
65 if (m_shouldCollectPatternAttributes) {
66 patternElement->synchronizeAnimatedSVGAttribute(anyQName());
68 m_attributes = PatternAttributes();
69 patternElement->collectPatternAttributes(m_attributes);
70 m_shouldCollectPatternAttributes = false;
73 // If we couldn't determine the pattern content element root, stop here.
74 if (!m_attributes.patternContentElement())
77 // An empty viewBox disables rendering.
78 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
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))
87 AffineTransform absoluteTransformIgnoringRotation;
88 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransformIgnoringRotation);
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;
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()));
100 OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
104 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
109 OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
110 patternData->pattern = Pattern::create(copiedImage, true, true);
112 // Compute pattern space transformation.
113 const IntSize tileImageSize = tileImage->size();
114 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
115 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
117 AffineTransform patternTransform = m_attributes.patternTransform();
118 if (!patternTransform.isIdentity())
119 patternData->transform = patternTransform * patternData->transform;
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;
127 patternData->pattern->setPatternSpaceTransform(patternData->transform);
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()).storedValue->value.get();
135 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
140 ASSERT(resourceMode != ApplyToDefaultMode);
142 clearInvalidationMask();
144 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
145 // then the given effect (e.g. a gradient or a filter) will be ignored.
146 FloatRect objectBoundingBox = object->objectBoundingBox();
147 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
150 PatternData* patternData = buildPattern(object, resourceMode);
157 const SVGRenderStyle* svgStyle = style->svgStyle();
160 if (resourceMode & ApplyToFillMode) {
161 context->setAlpha(svgStyle->fillOpacity());
162 context->setFillPattern(patternData->pattern);
163 context->setFillRule(svgStyle->fillRule());
164 } else if (resourceMode & ApplyToStrokeMode) {
165 if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
166 patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
167 context->setAlpha(svgStyle->strokeOpacity());
168 context->setStrokePattern(patternData->pattern);
169 SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
172 if (resourceMode & ApplyToTextMode) {
173 if (resourceMode & ApplyToFillMode)
174 context->setTextDrawingMode(TextModeFill);
175 else if (resourceMode & ApplyToStrokeMode)
176 context->setTextDrawingMode(TextModeStroke);
182 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
185 ASSERT(resourceMode != ApplyToDefaultMode);
187 if (resourceMode & ApplyToFillMode) {
189 context->fillPath(*path);
191 shape->fillShape(context);
193 if (resourceMode & ApplyToStrokeMode) {
195 context->strokePath(*path);
197 shape->strokeShape(context);
203 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
204 const FloatRect& objectBoundingBox,
205 const SVGPatternElement* patternElement)
207 ASSERT(patternElement);
208 return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
211 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
212 const PatternAttributes& attributes,
213 const SVGPatternElement* patternElement,
214 FloatRect& patternBoundaries,
215 AffineTransform& tileImageTransform) const
218 ASSERT(patternElement);
220 FloatRect objectBoundingBox = renderer->objectBoundingBox();
221 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
222 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
225 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
227 // Apply viewBox/objectBoundingBox transformations.
228 if (!viewBoxCTM.isIdentity())
229 tileImageTransform = viewBoxCTM;
230 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
231 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
236 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
237 const FloatRect& tileBoundaries,
238 const FloatRect& absoluteTileBoundaries,
239 const AffineTransform& tileImageTransform,
240 FloatRect& clampedAbsoluteTileBoundaries) const
242 clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
244 IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size()));
245 if (imageSize.isEmpty())
247 OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
251 GraphicsContext* tileImageContext = tileImage->context();
252 ASSERT(tileImageContext);
253 IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
254 tileImageContext->scale(FloatSize(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height()));
256 // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
257 tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
258 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
260 // Apply tile image transformations.
261 if (!tileImageTransform.isIdentity())
262 tileImageContext->concatCTM(tileImageTransform);
264 AffineTransform contentTransformation;
265 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
266 contentTransformation = tileImageTransform;
268 // Draw the content into the ImageBuffer.
269 for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
270 if (!node->isSVGElement() || !node->renderer())
272 if (node->renderer()->needsLayout())
274 SVGRenderingContext::renderSubtree(tileImage->context(), node->renderer(), contentTransformation);
277 return tileImage.release();