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 "SVGFitToViewBox.h"
31
#include "SVGRenderSupport.h"
32
#include "SVGRenderingContext.h"
36
RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
38
RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
39
: RenderSVGResourceContainer(node)
40
, m_shouldCollectPatternAttributes(true)
44
void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
47
m_shouldCollectPatternAttributes = true;
48
markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
51
void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
54
m_patternMap.remove(client);
55
markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
58
PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
60
PatternData* currentData = m_patternMap.get(object);
61
if (currentData && currentData->pattern)
64
SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
68
if (m_shouldCollectPatternAttributes) {
69
patternElement->updateAnimatedSVGAttribute(anyQName());
71
m_attributes = PatternAttributes();
72
patternElement->collectPatternAttributes(m_attributes);
73
m_shouldCollectPatternAttributes = false;
76
// If we couldn't determine the pattern content element root, stop here.
77
if (!m_attributes.patternContentElement())
80
// Compute all necessary transformations to build the tile image & the pattern.
81
FloatRect tileBoundaries;
82
AffineTransform tileImageTransform;
83
if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
86
AffineTransform absoluteTransformIgnoringRotation;
87
SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation);
89
// Ignore 2D rotation, as it doesn't affect the size of the tile.
90
SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
91
FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
92
FloatRect clampedAbsoluteTileBoundaries;
94
// Scale the tile size to match the scale level of the patternTransform.
95
absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
96
static_cast<float>(m_attributes.patternTransform().yScale()));
99
OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
103
RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
108
OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
109
patternData->pattern = Pattern::create(copiedImage, true, true);
111
// Compute pattern space transformation.
112
const IntSize tileImageSize = tileImage->logicalSize();
113
patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
114
patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
116
AffineTransform patternTransform = m_attributes.patternTransform();
117
if (!patternTransform.isIdentity())
118
patternData->transform = patternTransform * patternData->transform;
120
// Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows.
121
if (resourceMode & ApplyToTextMode) {
122
AffineTransform additionalTextTransformation;
123
if (shouldTransformOnTextPainting(object, additionalTextTransformation))
124
patternData->transform *= additionalTextTransformation;
126
patternData->pattern->setPatternSpaceTransform(patternData->transform);
128
// Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
129
// failures in the SVG image cache for example). To avoid having our PatternData deleted by
130
// removeAllClientsFromCache(), we only make it visible in the cache at the very end.
131
return m_patternMap.set(object, patternData.release()).iterator->value.get();
134
bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
139
ASSERT(resourceMode != ApplyToDefaultMode);
141
// Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
142
// then the given effect (e.g. a gradient or a filter) will be ignored.
143
FloatRect objectBoundingBox = object->objectBoundingBox();
144
if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
147
PatternData* patternData = buildPattern(object, resourceMode);
154
const SVGRenderStyle* svgStyle = style->svgStyle();
157
if (resourceMode & ApplyToFillMode) {
158
context->setAlpha(svgStyle->fillOpacity());
159
context->setFillPattern(patternData->pattern);
160
context->setFillRule(svgStyle->fillRule());
161
} else if (resourceMode & ApplyToStrokeMode) {
162
if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
163
patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
164
context->setAlpha(svgStyle->strokeOpacity());
165
context->setStrokePattern(patternData->pattern);
166
SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
169
if (resourceMode & ApplyToTextMode) {
170
if (resourceMode & ApplyToFillMode) {
171
context->setTextDrawingMode(TextModeFill);
174
context->applyFillPattern();
176
} else if (resourceMode & ApplyToStrokeMode) {
177
context->setTextDrawingMode(TextModeStroke);
180
context->applyStrokePattern();
188
void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
191
ASSERT(resourceMode != ApplyToDefaultMode);
193
if (resourceMode & ApplyToFillMode) {
195
context->fillPath(*path);
197
shape->fillShape(context);
199
if (resourceMode & ApplyToStrokeMode) {
201
context->strokePath(*path);
203
shape->strokeShape(context);
209
static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
210
const FloatRect& objectBoundingBox,
211
const SVGPatternElement* patternElement)
213
ASSERT(patternElement);
214
return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
217
bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
218
const PatternAttributes& attributes,
219
const SVGPatternElement* patternElement,
220
FloatRect& patternBoundaries,
221
AffineTransform& tileImageTransform) const
224
ASSERT(patternElement);
226
FloatRect objectBoundingBox = renderer->objectBoundingBox();
227
patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
228
if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
231
AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
233
// Apply viewBox/objectBoundingBox transformations.
234
if (!viewBoxCTM.isIdentity())
235
tileImageTransform = viewBoxCTM;
236
else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
237
tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
242
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
243
const FloatRect& tileBoundaries,
244
const FloatRect& absoluteTileBoundaries,
245
const AffineTransform& tileImageTransform,
246
FloatRect& clampedAbsoluteTileBoundaries) const
248
clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
250
OwnPtr<ImageBuffer> tileImage;
252
if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated))
255
GraphicsContext* tileImageContext = tileImage->context();
256
ASSERT(tileImageContext);
258
// The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
259
tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
260
clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
262
// Apply tile image transformations.
263
if (!tileImageTransform.isIdentity())
264
tileImageContext->concatCTM(tileImageTransform);
266
AffineTransform contentTransformation;
267
if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
268
contentTransformation = tileImageTransform;
270
// Draw the content into the ImageBuffer.
271
for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
272
if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
274
if (node->renderer()->needsLayout())
276
SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
279
return tileImage.release();