1/*
2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25
26#if ENABLE(SVG) && ENABLE(FILTERS)
27#include "RenderSVGResourceFilter.h"
28
29#include "AffineTransform.h"
30#include "FilterEffect.h"
31#include "FloatPoint.h"
32#include "FloatRect.h"
33#include "GraphicsContext.h"
34#include "Image.h"
35#include "ImageBuffer.h"
36#include "ImageData.h"
37#include "IntRect.h"
38#include "RenderSVGResource.h"
39#include "RenderSVGResourceFilterPrimitive.h"
40#include "SVGElement.h"
41#include "SVGFilter.h"
42#include "SVGFilterElement.h"
43#include "SVGFilterPrimitiveStandardAttributes.h"
44#include "SVGImageBufferTools.h"
45#include "SVGNames.h"
46#include "SVGStyledElement.h"
47#include "SVGUnitTypes.h"
48
49#include <wtf/UnusedParam.h>
50#include <wtf/Vector.h>
51
52using namespace std;
53
54namespace WebCore {
55
56RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
57
58RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
59    : RenderSVGResourceContainer(node)
60{
61}
62
63RenderSVGResourceFilter::~RenderSVGResourceFilter()
64{
65    if (m_filter.isEmpty())
66        return;
67
68    deleteAllValues(m_filter);
69    m_filter.clear();
70}
71
72void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
73{
74    if (!m_filter.isEmpty()) {
75        deleteAllValues(m_filter);
76        m_filter.clear();
77    }
78
79    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
80}
81
82void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
83{
84    ASSERT(client);
85
86    if (FilterData* filterData = m_filter.get(client)) {
87        if (filterData->savedContext)
88            filterData->markedForRemoval = true;
89        else
90            delete m_filter.take(client);
91    }
92
93    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
94}
95
96PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter)
97{
98    SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
99    bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
100
101    // Add effects to the builder
102    RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter);
103    for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
104        if (!node->isSVGElement())
105            continue;
106
107        SVGElement* element = static_cast<SVGElement*>(node);
108        if (!element->isFilterEffect())
109            continue;
110
111        SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
112        RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
113        if (!effect) {
114            builder->clearEffects();
115            return 0;
116        }
117        builder->appendEffectToEffectReferences(effect, effectElement->renderer());
118        effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get());
119        builder->add(effectElement->result(), effect);
120    }
121    return builder.release();
122}
123
124bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
125{
126    bool matchesFilterSize = true;
127    if (size.width() > kMaxFilterSize) {
128        scale.setWidth(scale.width() * kMaxFilterSize / size.width());
129        matchesFilterSize = false;
130    }
131    if (size.height() > kMaxFilterSize) {
132        scale.setHeight(scale.height() * kMaxFilterSize / size.height());
133        matchesFilterSize = false;
134    }
135
136    return matchesFilterSize;
137}
138
139bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
140{
141    ASSERT(object);
142    ASSERT(context);
143#ifndef NDEBUG
144    ASSERT(resourceMode == ApplyToDefaultMode);
145#else
146    UNUSED_PARAM(resourceMode);
147#endif
148
149    // Returning false here, to avoid drawings onto the context. We just want to
150    // draw the stored filter output, not the unfiltered object as well.
151    if (m_filter.contains(object)) {
152        FilterData* filterData = m_filter.get(object);
153        if (filterData->builded)
154            return false;
155
156        delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
157    }
158
159    OwnPtr<FilterData> filterData(new FilterData);
160    FloatRect targetBoundingBox = object->objectBoundingBox();
161
162    SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
163    filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox);
164    if (filterData->boundaries.isEmpty())
165        return false;
166
167    // Determine absolute transformation matrix for filter.
168    AffineTransform absoluteTransform;
169    SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
170    if (!absoluteTransform.isInvertible())
171        return false;
172
173    // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
174    filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f());
175
176    // Determine absolute boundaries of the filter and the drawing region.
177    FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
178    FloatRect drawingRegion = object->strokeBoundingBox();
179    drawingRegion.intersect(filterData->boundaries);
180    FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion);
181
182    // Create the SVGFilter object.
183    bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
184    filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
185
186    // Create all relevant filter primitives.
187    filterData->builder = buildPrimitives(filterData->filter.get());
188    if (!filterData->builder)
189        return false;
190
191    // Calculate the scale factor for the use of filterRes.
192    // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
193    FloatSize scale(1, 1);
194    if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
195        scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
196        scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
197    }
198
199    if (scale.isEmpty())
200        return false;
201
202    // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
203    FloatRect tempSourceRect = absoluteDrawingRegion;
204    tempSourceRect.scale(scale.width(), scale.height());
205    fitsInMaximumImageSize(tempSourceRect.size(), scale);
206
207    // Set the scale level in SVGFilter.
208    filterData->filter->setFilterResolution(scale);
209
210    FilterEffect* lastEffect = filterData->builder->lastEffect();
211    if (!lastEffect)
212        return false;
213
214    RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
215    FloatRect subRegion = lastEffect->maxEffectRect();
216    // At least one FilterEffect has a too big image size,
217    // recalculate the effect sizes with new scale factors.
218    if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
219        filterData->filter->setFilterResolution(scale);
220        RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
221    }
222
223    // If the drawingRegion is empty, we have something like <g filter=".."/>.
224    // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
225    if (drawingRegion.isEmpty()) {
226        ASSERT(!m_filter.contains(object));
227        filterData->savedContext = context;
228        m_filter.set(object, filterData.leakPtr());
229        return false;
230    }
231
232    absoluteDrawingRegion.scale(scale.width(), scale.height());
233
234    OwnPtr<ImageBuffer> sourceGraphic;
235    if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) {
236        ASSERT(!m_filter.contains(object));
237        filterData->savedContext = context;
238        m_filter.set(object, filterData.leakPtr());
239        return false;
240    }
241
242    GraphicsContext* sourceGraphicContext = sourceGraphic->context();
243    ASSERT(sourceGraphicContext);
244
245    sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y());
246    if (scale.width() != 1 || scale.height() != 1)
247        sourceGraphicContext->scale(scale);
248
249    sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform);
250    sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size()));
251    filterData->sourceGraphicBuffer = sourceGraphic.release();
252    filterData->savedContext = context;
253
254    context = sourceGraphicContext;
255
256    ASSERT(!m_filter.contains(object));
257    m_filter.set(object, filterData.leakPtr());
258
259    return true;
260}
261
262void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*)
263{
264    ASSERT(object);
265    ASSERT(context);
266#ifndef NDEBUG
267    ASSERT(resourceMode == ApplyToDefaultMode);
268#else
269    UNUSED_PARAM(resourceMode);
270#endif
271
272    FilterData* filterData = m_filter.get(object);
273    if (!filterData)
274        return;
275
276    if (filterData->markedForRemoval) {
277        delete m_filter.take(object);
278        return;
279    }
280
281    if (!filterData->builded) {
282        if (!filterData->savedContext) {
283            removeClientFromCache(object);
284            return;
285        }
286
287        context = filterData->savedContext;
288        filterData->savedContext = 0;
289#if !USE(CG)
290        if (filterData->sourceGraphicBuffer)
291            filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
292#endif
293    }
294
295    FilterEffect* lastEffect = filterData->builder->lastEffect();
296
297    if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
298        // This is the real filtering of the object. It just needs to be called on the
299        // initial filtering process. We just take the stored filter result on a
300        // second drawing.
301        if (!filterData->builded)
302            filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
303
304        // Always true if filterData is just built (filterData->builded is false).
305        if (!lastEffect->hasResult()) {
306            lastEffect->apply();
307#if !USE(CG)
308            ImageBuffer* resultImage = lastEffect->asImageBuffer();
309            if (resultImage)
310                resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
311#endif
312        }
313        filterData->builded = true;
314
315        ImageBuffer* resultImage = lastEffect->asImageBuffer();
316        if (resultImage) {
317            context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
318
319            context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
320            context->clip(lastEffect->maxEffectRect());
321            context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
322            context->scale(filterData->filter->filterResolution());
323
324            context->concatCTM(filterData->shearFreeAbsoluteTransform);
325        }
326    }
327    filterData->sourceGraphicBuffer.clear();
328}
329
330FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
331{
332    if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
333        return element->filterBoundingBox(object->objectBoundingBox());
334
335    return FloatRect();
336}
337
338void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
339{
340    HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
341    HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
342    SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
343
344    for (; it != end; ++it) {
345        FilterData* filterData = it->second;
346        if (!filterData->builded)
347            continue;
348
349        SVGFilterBuilder* builder = filterData->builder.get();
350        FilterEffect* effect = builder->effectByRenderer(object);
351        if (!effect)
352            continue;
353        // Since all effects shares the same attribute value, all
354        // or none of them will be changed.
355        if (!primitve->setFilterEffectAttribute(effect, attribute))
356            return;
357        builder->clearResultsRecursive(effect);
358
359        // Repaint the image on the screen.
360        markClientForInvalidation(it->first, RepaintInvalidation);
361    }
362}
363
364}
365#endif
366