~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/rendering/svg/RenderSVGResourcePattern.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
23
#if ENABLE(SVG)
 
24
#include "RenderSVGResourcePattern.h"
 
25
 
 
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"
 
33
 
 
34
namespace WebCore {
 
35
 
 
36
RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
 
37
 
 
38
RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
 
39
    : RenderSVGResourceContainer(node)
 
40
    , m_shouldCollectPatternAttributes(true)
 
41
{
 
42
}
 
43
 
 
44
void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
 
45
{
 
46
    m_patternMap.clear();
 
47
    m_shouldCollectPatternAttributes = true;
 
48
    markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
 
49
}
 
50
 
 
51
void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
 
52
{
 
53
    ASSERT(client);
 
54
    m_patternMap.remove(client);
 
55
    markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
 
56
}
 
57
 
 
58
PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
 
59
{
 
60
    PatternData* currentData = m_patternMap.get(object);
 
61
    if (currentData && currentData->pattern)
 
62
        return currentData;
 
63
 
 
64
    SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
 
65
    if (!patternElement)
 
66
        return 0;
 
67
 
 
68
    if (m_shouldCollectPatternAttributes) {
 
69
        patternElement->updateAnimatedSVGAttribute(anyQName());
 
70
 
 
71
        m_attributes = PatternAttributes();
 
72
        patternElement->collectPatternAttributes(m_attributes);
 
73
        m_shouldCollectPatternAttributes = false;
 
74
    }
 
75
 
 
76
    // If we couldn't determine the pattern content element root, stop here.
 
77
    if (!m_attributes.patternContentElement())
 
78
        return 0;
 
79
 
 
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))
 
84
        return 0;
 
85
 
 
86
    AffineTransform absoluteTransformIgnoringRotation;
 
87
    SVGRenderingContext::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransformIgnoringRotation);
 
88
 
 
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;
 
93
 
 
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()));
 
97
 
 
98
    // Build tile image.
 
99
    OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
 
100
    if (!tileImage)
 
101
        return 0;
 
102
 
 
103
    RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
 
104
    if (!copiedImage)
 
105
        return 0;
 
106
 
 
107
    // Build pattern.
 
108
    OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
 
109
    patternData->pattern = Pattern::create(copiedImage, true, true);
 
110
 
 
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());
 
115
 
 
116
    AffineTransform patternTransform = m_attributes.patternTransform();
 
117
    if (!patternTransform.isIdentity())
 
118
        patternData->transform = patternTransform * patternData->transform;
 
119
 
 
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;
 
125
    }
 
126
    patternData->pattern->setPatternSpaceTransform(patternData->transform);
 
127
 
 
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();
 
132
}
 
133
 
 
134
bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
 
135
{
 
136
    ASSERT(object);
 
137
    ASSERT(style);
 
138
    ASSERT(context);
 
139
    ASSERT(resourceMode != ApplyToDefaultMode);
 
140
 
 
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())
 
145
        return false;
 
146
 
 
147
    PatternData* patternData = buildPattern(object, resourceMode);
 
148
    if (!patternData)
 
149
        return false;
 
150
 
 
151
    // Draw pattern
 
152
    context->save();
 
153
 
 
154
    const SVGRenderStyle* svgStyle = style->svgStyle();
 
155
    ASSERT(svgStyle);
 
156
 
 
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);
 
167
    }
 
168
 
 
169
    if (resourceMode & ApplyToTextMode) {
 
170
        if (resourceMode & ApplyToFillMode) {
 
171
            context->setTextDrawingMode(TextModeFill);
 
172
 
 
173
#if USE(CG)
 
174
            context->applyFillPattern();
 
175
#endif
 
176
        } else if (resourceMode & ApplyToStrokeMode) {
 
177
            context->setTextDrawingMode(TextModeStroke);
 
178
 
 
179
#if USE(CG)
 
180
            context->applyStrokePattern();
 
181
#endif
 
182
        }
 
183
    }
 
184
 
 
185
    return true;
 
186
}
 
187
 
 
188
void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
 
189
{
 
190
    ASSERT(context);
 
191
    ASSERT(resourceMode != ApplyToDefaultMode);
 
192
 
 
193
    if (resourceMode & ApplyToFillMode) {
 
194
        if (path)
 
195
            context->fillPath(*path);
 
196
        else if (shape)
 
197
            shape->fillShape(context);
 
198
    }
 
199
    if (resourceMode & ApplyToStrokeMode) {
 
200
        if (path)
 
201
            context->strokePath(*path);
 
202
        else if (shape)
 
203
            shape->strokeShape(context);
 
204
    }
 
205
 
 
206
    context->restore();
 
207
}
 
208
 
 
209
static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
 
210
                                                   const FloatRect& objectBoundingBox,
 
211
                                                   const SVGPatternElement* patternElement)
 
212
{
 
213
    ASSERT(patternElement);
 
214
    return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
 
215
}
 
216
 
 
217
bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
 
218
                                                       const PatternAttributes& attributes,
 
219
                                                       const SVGPatternElement* patternElement,
 
220
                                                       FloatRect& patternBoundaries,
 
221
                                                       AffineTransform& tileImageTransform) const
 
222
{
 
223
    ASSERT(renderer);
 
224
    ASSERT(patternElement);
 
225
 
 
226
    FloatRect objectBoundingBox = renderer->objectBoundingBox();
 
227
    patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 
 
228
    if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
 
229
        return false;
 
230
 
 
231
    AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
 
232
 
 
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());
 
238
 
 
239
    return true;
 
240
}
 
241
 
 
242
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
 
243
                                                                  const FloatRect& tileBoundaries,
 
244
                                                                  const FloatRect& absoluteTileBoundaries,
 
245
                                                                  const AffineTransform& tileImageTransform,
 
246
                                                                  FloatRect& clampedAbsoluteTileBoundaries) const
 
247
{
 
248
    clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
 
249
 
 
250
    OwnPtr<ImageBuffer> tileImage;
 
251
 
 
252
    if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated))
 
253
        return nullptr;
 
254
 
 
255
    GraphicsContext* tileImageContext = tileImage->context();
 
256
    ASSERT(tileImageContext);
 
257
 
 
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()));
 
261
 
 
262
    // Apply tile image transformations.
 
263
    if (!tileImageTransform.isIdentity())
 
264
        tileImageContext->concatCTM(tileImageTransform);
 
265
 
 
266
    AffineTransform contentTransformation;
 
267
    if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
 
268
        contentTransformation = tileImageTransform;
 
269
 
 
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())
 
273
            continue;
 
274
        if (node->renderer()->needsLayout())
 
275
            return nullptr;
 
276
        SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
 
277
    }
 
278
 
 
279
    return tileImage.release();
 
280
}
 
281
 
 
282
}
 
283
 
 
284
#endif