[SVG] Cached filter results are not invalidated on repaint rect change
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGResourceFilter.cpp
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 "Page.h"
39 #include "RenderSVGResource.h"
40 #include "RenderSVGResourceFilterPrimitive.h"
41 #include "SVGElement.h"
42 #include "SVGFilter.h"
43 #include "SVGFilterElement.h"
44 #include "SVGFilterPrimitiveStandardAttributes.h"
45 #include "SVGNames.h"
46 #include "SVGRenderingContext.h"
47 #include "SVGStyledElement.h"
48 #include "SVGUnitTypes.h"
49 #include "Settings.h"
50 #include "SourceAlpha.h"
51 #include "SourceGraphic.h"
52
53 #include <wtf/UnusedParam.h>
54 #include <wtf/Vector.h>
55
56 using namespace std;
57
58 namespace WebCore {
59
60 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
61
62 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
63     : RenderSVGResourceContainer(node)
64 {
65 }
66
67 RenderSVGResourceFilter::~RenderSVGResourceFilter()
68 {
69     if (m_filter.isEmpty())
70         return;
71
72     deleteAllValues(m_filter);
73     m_filter.clear();
74 }
75
76 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
77 {
78     if (!m_filter.isEmpty()) {
79         deleteAllValues(m_filter);
80         m_filter.clear();
81     }
82
83     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
84 }
85
86 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
87 {
88     ASSERT(client);
89
90     if (FilterData* filterData = m_filter.get(client)) {
91         if (filterData->savedContext)
92             filterData->state = FilterData::MarkedForRemoval;
93         else
94             delete m_filter.take(client);
95     }
96
97     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
98 }
99
100 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
101 {
102     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
103     FloatRect targetBoundingBox = filter->targetBoundingBox();
104
105     // Add effects to the builder
106     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
107     for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
108         if (!node->isSVGElement())
109             continue;
110
111         SVGElement* element = static_cast<SVGElement*>(node);
112         if (!element->isFilterEffect())
113             continue;
114
115         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
116         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
117         if (!effect) {
118             builder->clearEffects();
119             return 0;
120         }
121         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
122         effectElement->setStandardAttributes(effect.get());
123         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits(), targetBoundingBox));
124         effect->setColorSpace(effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB
125                 ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
126         builder->add(effectElement->result(), effect);
127     }
128     return builder.release();
129 }
130
131 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
132 {
133     bool matchesFilterSize = true;
134     if (size.width() > kMaxFilterSize) {
135         scale.setWidth(scale.width() * kMaxFilterSize / size.width());
136         matchesFilterSize = false;
137     }
138     if (size.height() > kMaxFilterSize) {
139         scale.setHeight(scale.height() * kMaxFilterSize / size.height());
140         matchesFilterSize = false;
141     }
142
143     return matchesFilterSize;
144 }
145
146 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
147 {
148     ASSERT(object);
149     ASSERT(context);
150     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
151
152     if (m_filter.contains(object)) {
153         FilterData* filterData = m_filter.get(object);
154         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
155             filterData->state = FilterData::CycleDetected;
156         return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
157     }
158
159     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
160     FloatRect targetBoundingBox = object->objectBoundingBox();
161
162     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
163     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox);
164     if (filterData->boundaries.isEmpty())
165         return false;
166
167     // Determine absolute transformation matrix for filter. 
168     AffineTransform absoluteTransform;
169     SVGRenderingContext::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(), 0, 0);
175
176     // Determine absolute boundaries of the filter and the drawing region.
177     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
178     filterData->drawingRegion = object->strokeBoundingBox();
179     filterData->drawingRegion.intersect(filterData->boundaries);
180     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->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 (filterData->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     // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
233     AffineTransform effectiveTransform;
234     effectiveTransform.scale(scale.width(), scale.height());
235     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
236
237     OwnPtr<ImageBuffer> sourceGraphic;
238     RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
239     if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
240         ASSERT(!m_filter.contains(object));
241         filterData->savedContext = context;
242         m_filter.set(object, filterData.leakPtr());
243         return false;
244     }
245     
246     // Set the rendering mode from the page's settings.
247     filterData->filter->setRenderingMode(renderingMode);
248
249     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
250     ASSERT(sourceGraphicContext);
251   
252     filterData->sourceGraphicBuffer = sourceGraphic.release();
253     filterData->savedContext = context;
254
255     context = sourceGraphicContext;
256
257     ASSERT(!m_filter.contains(object));
258     m_filter.set(object, filterData.leakPtr());
259
260     return true;
261 }
262
263 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
264 {
265     ASSERT(object);
266     ASSERT(context);
267     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
268
269     FilterData* filterData = m_filter.get(object);
270     if (!filterData)
271         return;
272
273     switch (filterData->state) {
274     case FilterData::MarkedForRemoval:
275         delete m_filter.take(object);
276         return;
277
278     case FilterData::CycleDetected:
279     case FilterData::Applying:
280         // We have a cycle if we are already applying the data.
281         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
282         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
283         // will continue correctly.
284         filterData->state = FilterData::PaintingSource;
285         return;
286
287     case FilterData::PaintingSource:
288         if (!filterData->savedContext) {
289             removeClientFromCache(object);
290             return;
291         }
292
293         context = filterData->savedContext;
294         filterData->savedContext = 0;
295         break;
296
297     case FilterData::Built: { } // Empty
298     }
299
300     FilterEffect* lastEffect = filterData->builder->lastEffect();
301
302     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
303         // This is the real filtering of the object. It just needs to be called on the
304         // initial filtering process. We just take the stored filter result on a
305         // second drawing.
306         if (filterData->state != FilterData::Built)
307             filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
308
309         // Always true if filterData is just built (filterData->state == FilterData::Built).
310         if (!lastEffect->hasResult()) {
311             filterData->state = FilterData::Applying;
312             lastEffect->applyAll();
313             lastEffect->correctFilterResultIfNeeded();
314             lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
315         }
316         filterData->state = FilterData::Built;
317
318         ImageBuffer* resultImage = lastEffect->asImageBuffer();
319         if (resultImage) {
320             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
321
322             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
323             context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
324             context->scale(filterData->filter->filterResolution());
325
326             context->concatCTM(filterData->shearFreeAbsoluteTransform);
327         }
328     }
329     filterData->sourceGraphicBuffer.clear();
330 }
331
332 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
333 {
334     if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
335         return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits(), object->objectBoundingBox());
336
337     return FloatRect();
338 }
339
340 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
341 {
342     HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
343     HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
344     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
345
346     for (; it != end; ++it) {
347         FilterData* filterData = it->value;
348         if (filterData->state != FilterData::Built)
349             continue;
350
351         SVGFilterBuilder* builder = filterData->builder.get();
352         FilterEffect* effect = builder->effectByRenderer(object);
353         if (!effect)
354             continue;
355         // Since all effects shares the same attribute value, all
356         // or none of them will be changed.
357         if (!primitve->setFilterEffectAttribute(effect, attribute))
358             return;
359         builder->clearResultsRecursive(effect);
360
361         // Repaint the image on the screen.
362         markClientForInvalidation(it->key, RepaintInvalidation);
363     }
364     markAllClientLayersForInvalidation();
365 }
366
367 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
368 {
369     FilterData* filterData = m_filter.get(object);
370     return filterData ? filterData->drawingRegion : FloatRect();
371 }
372
373 }
374 #endif