RenderObject::document() should return a reference.
[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 "SVGUnitTypes.h"
48 #include "Settings.h"
49 #include "SourceAlpha.h"
50 #include "SourceGraphic.h"
51 #include <wtf/Vector.h>
52
53 using namespace std;
54
55 namespace WebCore {
56
57 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
58
59 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
60     : RenderSVGResourceContainer(node)
61 {
62 }
63
64 RenderSVGResourceFilter::~RenderSVGResourceFilter()
65 {
66     if (m_filter.isEmpty())
67         return;
68
69     deleteAllValues(m_filter);
70     m_filter.clear();
71 }
72
73 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
74 {
75     if (!m_filter.isEmpty()) {
76         deleteAllValues(m_filter);
77         m_filter.clear();
78     }
79
80     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
81 }
82
83 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
84 {
85     ASSERT(client);
86
87     if (FilterData* filterData = m_filter.get(client)) {
88         if (filterData->savedContext)
89             filterData->state = FilterData::MarkedForRemoval;
90         else
91             delete m_filter.take(client);
92     }
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     for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
105         if (!node->isSVGElement())
106             continue;
107
108         SVGElement* element = toSVGElement(node);
109         if (!element->isFilterEffect())
110             continue;
111
112         SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
113         RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
114         if (!effect) {
115             builder->clearEffects();
116             return 0;
117         }
118         builder->appendEffectToEffectReferences(effect, effectElement->renderer());
119         effectElement->setStandardAttributes(effect.get());
120         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits(), targetBoundingBox));
121         effect->setOperatingColorSpace(
122             effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
123         builder->add(effectElement->result(), effect);
124     }
125     return builder.release();
126 }
127
128 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
129 {
130     bool matchesFilterSize = true;
131     if (size.width() > kMaxFilterSize) {
132         scale.setWidth(scale.width() * kMaxFilterSize / size.width());
133         matchesFilterSize = false;
134     }
135     if (size.height() > kMaxFilterSize) {
136         scale.setHeight(scale.height() * kMaxFilterSize / size.height());
137         matchesFilterSize = false;
138     }
139
140     return matchesFilterSize;
141 }
142
143 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
144 {
145     ASSERT(object);
146     ASSERT(context);
147     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
148
149     if (m_filter.contains(object)) {
150         FilterData* filterData = m_filter.get(object);
151         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
152             filterData->state = FilterData::CycleDetected;
153         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     FloatRect targetBoundingBox = object->objectBoundingBox();
158
159     SVGFilterElement* filterElement = toSVGFilterElement(node());
160     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox);
161     if (filterData->boundaries.isEmpty())
162         return false;
163
164     // Determine absolute transformation matrix for filter. 
165     AffineTransform absoluteTransform;
166     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
167     if (!absoluteTransform.isInvertible())
168         return false;
169
170     // 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     // Determine absolute boundaries of the filter and the drawing region.
174     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
175     filterData->drawingRegion = object->strokeBoundingBox();
176     filterData->drawingRegion.intersect(filterData->boundaries);
177     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
178
179     // Create the SVGFilter object.
180     bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
181     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
182
183     // Create all relevant filter primitives.
184     filterData->builder = buildPrimitives(filterData->filter.get());
185     if (!filterData->builder)
186         return false;
187
188     // Calculate the scale factor for the use of filterRes.
189     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
190     FloatSize scale(1, 1);
191     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
192         scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
193         scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
194     }
195
196     if (scale.isEmpty())
197         return false;
198
199     // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
200     FloatRect tempSourceRect = absoluteDrawingRegion;
201     tempSourceRect.scale(scale.width(), scale.height());
202     fitsInMaximumImageSize(tempSourceRect.size(), scale);
203
204     // Set the scale level in SVGFilter.
205     filterData->filter->setFilterResolution(scale);
206
207     FilterEffect* lastEffect = filterData->builder->lastEffect();
208     if (!lastEffect)
209         return false;
210
211     RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
212     FloatRect subRegion = lastEffect->maxEffectRect();
213     // At least one FilterEffect has a too big image size,
214     // recalculate the effect sizes with new scale factors.
215     if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
216         filterData->filter->setFilterResolution(scale);
217         RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
218     }
219
220     // If the drawingRegion is empty, we have something like <g filter=".."/>.
221     // 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         ASSERT(!m_filter.contains(object));
224         filterData->savedContext = context;
225         m_filter.set(object, filterData.leakPtr());
226         return false;
227     }
228
229     // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
230     AffineTransform effectiveTransform;
231     effectiveTransform.scale(scale.width(), scale.height());
232     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
233
234     OwnPtr<ImageBuffer> sourceGraphic;
235     RenderingMode renderingMode = object->document().page()->settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
236     if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
237         ASSERT(!m_filter.contains(object));
238         filterData->savedContext = context;
239         m_filter.set(object, filterData.leakPtr());
240         return false;
241     }
242     
243     // Set the rendering mode from the page's settings.
244     filterData->filter->setRenderingMode(renderingMode);
245
246     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
247     ASSERT(sourceGraphicContext);
248   
249     filterData->sourceGraphicBuffer = sourceGraphic.release();
250     filterData->savedContext = context;
251
252     context = sourceGraphicContext;
253
254     ASSERT(!m_filter.contains(object));
255     m_filter.set(object, filterData.leakPtr());
256
257     return true;
258 }
259
260 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
261 {
262     ASSERT(object);
263     ASSERT(context);
264     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
265
266     FilterData* filterData = m_filter.get(object);
267     if (!filterData)
268         return;
269
270     switch (filterData->state) {
271     case FilterData::MarkedForRemoval:
272         delete m_filter.take(object);
273         return;
274
275     case FilterData::CycleDetected:
276     case FilterData::Applying:
277         // We have a cycle if we are already applying the data.
278         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
279         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
280         // will continue correctly.
281         filterData->state = FilterData::PaintingSource;
282         return;
283
284     case FilterData::PaintingSource:
285         if (!filterData->savedContext) {
286             removeClientFromCache(object);
287             return;
288         }
289
290         context = filterData->savedContext;
291         filterData->savedContext = 0;
292         break;
293
294     case FilterData::Built: { } // Empty
295     }
296
297     FilterEffect* lastEffect = filterData->builder->lastEffect();
298
299     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
300         // This is the real filtering of the object. It just needs to be called on the
301         // initial filtering process. We just take the stored filter result on a
302         // 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         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->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
321             context->scale(filterData->filter->filterResolution());
322
323             context->concatCTM(filterData->shearFreeAbsoluteTransform);
324         }
325     }
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     return FloatRect();
335 }
336
337 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
338 {
339     HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
340     HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
341     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
342
343     for (; it != end; ++it) {
344         FilterData* filterData = it->value;
345         if (filterData->state != FilterData::Built)
346             continue;
347
348         SVGFilterBuilder* builder = filterData->builder.get();
349         FilterEffect* effect = builder->effectByRenderer(object);
350         if (!effect)
351             continue;
352         // Since all effects shares the same attribute value, all
353         // or none of them will be changed.
354         if (!primitve->setFilterEffectAttribute(effect, attribute))
355             return;
356         builder->clearResultsRecursive(effect);
357
358         // Repaint the image on the screen.
359         markClientForInvalidation(it->key, RepaintInvalidation);
360     }
361     markAllClientLayersForInvalidation();
362 }
363
364 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
365 {
366     FilterData* filterData = m_filter.get(object);
367     return filterData ? filterData->drawingRegion : FloatRect();
368 }
369
370 }
371 #endif