RenderSVGResourceFilter.cpp 14.6 KB
Newer Older
1
2
/*
 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3
4
5
 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
 * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#if ENABLE(SVG) && ENABLE(FILTERS)
#include "RenderSVGResourceFilter.h"

#include "AffineTransform.h"
30
#include "FilterEffect.h"
31
32
33
34
35
36
37
#include "FloatPoint.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "Image.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "IntRect.h"
38
#include "Page.h"
39
#include "RenderSVGResource.h"
40
#include "RenderSVGResourceFilterPrimitive.h"
41
42
43
44
#include "SVGElement.h"
#include "SVGFilter.h"
#include "SVGFilterElement.h"
#include "SVGFilterPrimitiveStandardAttributes.h"
45
#include "SVGNames.h"
46
#include "SVGRenderingContext.h"
47
#include "SVGUnitTypes.h"
48
49
50
#include "Settings.h"
#include "SourceAlpha.h"
#include "SourceGraphic.h"
51
#include <wtf/Vector.h>
52
53
54
55
56
57
58

using namespace std;

namespace WebCore {

RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;

59
60
RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
    : RenderSVGResourceContainer(node)
61
62
63
64
65
{
}

RenderSVGResourceFilter::~RenderSVGResourceFilter()
{
66
67
68
    if (m_filter.isEmpty())
        return;

69
70
71
72
    deleteAllValues(m_filter);
    m_filter.clear();
}

73
void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
74
{
75
76
77
78
    if (!m_filter.isEmpty()) {
        deleteAllValues(m_filter);
        m_filter.clear();
    }
79

80
    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
81
82
}

83
void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
84
{
85
86
    ASSERT(client);

87
88
    if (FilterData* filterData = m_filter.get(client)) {
        if (filterData->savedContext)
89
            filterData->state = FilterData::MarkedForRemoval;
90
91
92
        else
            delete m_filter.take(client);
    }
93

94
    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
95
96
}

97
PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
98
{
99
    SVGFilterElement* filterElement = toSVGFilterElement(node());
100
    FloatRect targetBoundingBox = filter->targetBoundingBox();
101
102

    // Add effects to the builder
103
    RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
104
105
106
107
    for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
        if (!node->isSVGElement())
            continue;

108
        SVGElement* element = toSVGElement(node);
109
110
111
112
        if (!element->isFilterEffect())
            continue;

        SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
113
        RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
114
115
116
117
        if (!effect) {
            builder->clearEffects();
            return 0;
        }
118
        builder->appendEffectToEffectReferences(effect, effectElement->renderer());
119
120
        effectElement->setStandardAttributes(effect.get());
        effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits(), targetBoundingBox));
121
122
        effect->setOperatingColorSpace(
            effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        builder->add(effectElement->result(), effect);
    }
    return builder.release();
}

bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
{
    bool matchesFilterSize = true;
    if (size.width() > kMaxFilterSize) {
        scale.setWidth(scale.width() * kMaxFilterSize / size.width());
        matchesFilterSize = false;
    }
    if (size.height() > kMaxFilterSize) {
        scale.setHeight(scale.height() * kMaxFilterSize / size.height());
        matchesFilterSize = false;
    }

    return matchesFilterSize;
}

143
bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
144
145
146
{
    ASSERT(object);
    ASSERT(context);
147
    ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
148
149
150

    if (m_filter.contains(object)) {
        FilterData* filterData = m_filter.get(object);
151
152
153
        if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
            filterData->state = FilterData::CycleDetected;
        return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
154
155
    }

156
    OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
157
158
    FloatRect targetBoundingBox = object->objectBoundingBox();

159
    SVGFilterElement* filterElement = toSVGFilterElement(node());
160
    filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox);
161
162
163
164
165
    if (filterData->boundaries.isEmpty())
        return false;

    // Determine absolute transformation matrix for filter. 
    AffineTransform absoluteTransform;
166
    SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
167
168
169
170
    if (!absoluteTransform.isInvertible())
        return false;

    // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
171
    filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
172
173
174

    // Determine absolute boundaries of the filter and the drawing region.
    FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
175
176
177
    filterData->drawingRegion = object->strokeBoundingBox();
    filterData->drawingRegion.intersect(filterData->boundaries);
    FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
178
179
180
181
182
183

    // Create the SVGFilter object.
    bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
    filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);

    // Create all relevant filter primitives.
184
    filterData->builder = buildPrimitives(filterData->filter.get());
185
186
187
188
189
    if (!filterData->builder)
        return false;

    // Calculate the scale factor for the use of filterRes.
    // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
190
    FloatSize scale(1, 1);
191
    if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
192
193
        scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
        scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
194
195
196
197
198
    }

    if (scale.isEmpty())
        return false;

199
200
    // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
    FloatRect tempSourceRect = absoluteDrawingRegion;
201
202
203
    tempSourceRect.scale(scale.width(), scale.height());
    fitsInMaximumImageSize(tempSourceRect.size(), scale);

204
    // Set the scale level in SVGFilter.
205
206
207
208
209
    filterData->filter->setFilterResolution(scale);

    FilterEffect* lastEffect = filterData->builder->lastEffect();
    if (!lastEffect)
        return false;
210

211
    RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
212
213
214
215
    FloatRect subRegion = lastEffect->maxEffectRect();
    // At least one FilterEffect has a too big image size,
    // recalculate the effect sizes with new scale factors.
    if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
216
        filterData->filter->setFilterResolution(scale);
217
        RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
218
219
    }

220
221
    // If the drawingRegion is empty, we have something like <g filter=".."/>.
    // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
222
    if (filterData->drawingRegion.isEmpty()) {
223
224
225
        ASSERT(!m_filter.contains(object));
        filterData->savedContext = context;
        m_filter.set(object, filterData.leakPtr());
226
        return false;
227
    }
228

229
230
231
232
    // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
    AffineTransform effectiveTransform;
    effectiveTransform.scale(scale.width(), scale.height());
    effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
233
234

    OwnPtr<ImageBuffer> sourceGraphic;
235
    RenderingMode renderingMode = object->frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
236
    if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
237
238
239
        ASSERT(!m_filter.contains(object));
        filterData->savedContext = context;
        m_filter.set(object, filterData.leakPtr());
240
        return false;
241
    }
242
    
243
244
245
    // Set the rendering mode from the page's settings.
    filterData->filter->setRenderingMode(renderingMode);

246
    GraphicsContext* sourceGraphicContext = sourceGraphic->context();
247
248
    ASSERT(sourceGraphicContext);
  
249
    filterData->sourceGraphicBuffer = sourceGraphic.release();
250
    filterData->savedContext = context;
251
252

    context = sourceGraphicContext;
253
254
255

    ASSERT(!m_filter.contains(object));
    m_filter.set(object, filterData.leakPtr());
256
257
258
259

    return true;
}

260
void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
261
262
263
{
    ASSERT(object);
    ASSERT(context);
264
    ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
265

266
267
    FilterData* filterData = m_filter.get(object);
    if (!filterData)
268
269
        return;

270
271
    switch (filterData->state) {
    case FilterData::MarkedForRemoval:
272
273
274
        delete m_filter.take(object);
        return;

275
276
277
278
279
280
281
    case FilterData::CycleDetected:
    case FilterData::Applying:
        // We have a cycle if we are already applying the data.
        // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
        // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
        // will continue correctly.
        filterData->state = FilterData::PaintingSource;
282
283
        return;

284
    case FilterData::PaintingSource:
285
        if (!filterData->savedContext) {
286
            removeClientFromCache(object);
287
288
289
            return;
        }

290
291
        context = filterData->savedContext;
        filterData->savedContext = 0;
292
        break;
293

294
295
    case FilterData::Built: { } // Empty
    }
296

297
    FilterEffect* lastEffect = filterData->builder->lastEffect();
298

299
    if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
300
301
302
        // This is the real filtering of the object. It just needs to be called on the
        // initial filtering process. We just take the stored filter result on a
        // second drawing.
303
        if (filterData->state != FilterData::Built)
304
            filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
305

306
        // Always true if filterData is just built (filterData->state == FilterData::Built).
307
        if (!lastEffect->hasResult()) {
308
            filterData->state = FilterData::Applying;
309
            lastEffect->applyAll();
310
            lastEffect->correctFilterResultIfNeeded();
311
            lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
312
        }
313
        filterData->state = FilterData::Built;
314

315
        ImageBuffer* resultImage = lastEffect->asImageBuffer();
316
317
        if (resultImage) {
            context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
318

319
320
321
322
323
324
325
            context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
            context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
            context->scale(filterData->filter->filterResolution());

            context->concatCTM(filterData->shearFreeAbsoluteTransform);
        }
    }
326
    filterData->sourceGraphicBuffer.clear();
327
328
}

329
FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
330
{
331
    if (SVGFilterElement* element = toSVGFilterElement(node()))
332
        return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits(), object->objectBoundingBox());
333
334
335
336

    return FloatRect();
}

337
338
339
340
341
342
343
void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
{
    HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
    HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
    SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());

    for (; it != end; ++it) {
344
        FilterData* filterData = it->value;
345
        if (filterData->state != FilterData::Built)
346
347
348
349
350
351
            continue;

        SVGFilterBuilder* builder = filterData->builder.get();
        FilterEffect* effect = builder->effectByRenderer(object);
        if (!effect)
            continue;
352
353
354
355
        // Since all effects shares the same attribute value, all
        // or none of them will be changed.
        if (!primitve->setFilterEffectAttribute(effect, attribute))
            return;
356
357
358
        builder->clearResultsRecursive(effect);

        // Repaint the image on the screen.
359
        markClientForInvalidation(it->key, RepaintInvalidation);
360
    }
361
    markAllClientLayersForInvalidation();
362
363
}

364
365
366
367
368
369
FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
{
    FilterData* filterData = m_filter.get(object);
    return filterData ? filterData->drawingRegion : FloatRect();
}

370
371
}
#endif