Applying a filter on an SVG element, which is larger than 4096 pixels, causes this...
[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 #include "RenderSVGResourceFilter.h"
26
27 #include "ElementChildIterator.h"
28 #include "FilterEffect.h"
29 #include "FloatPoint.h"
30 #include "Frame.h"
31 #include "GraphicsContext.h"
32 #include "Image.h"
33 #include "ImageData.h"
34 #include "IntRect.h"
35 #include "Page.h"
36 #include "RenderSVGResourceFilterPrimitive.h"
37 #include "RenderView.h"
38 #include "SVGFilterPrimitiveStandardAttributes.h"
39 #include "SVGNames.h"
40 #include "SVGRenderingContext.h"
41 #include "Settings.h"
42 #include "SourceGraphic.h"
43
44 namespace WebCore {
45
46 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement& element, Ref<RenderStyle>&& style)
47     : RenderSVGResourceContainer(element, WTF::move(style))
48 {
49 }
50
51 RenderSVGResourceFilter::~RenderSVGResourceFilter()
52 {
53 }
54
55 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
56 {
57     m_filter.clear();
58
59     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
60 }
61
62 void RenderSVGResourceFilter::removeClientFromCache(RenderElement& client, bool markForInvalidation)
63 {
64     if (FilterData* filterData = m_filter.get(&client)) {
65         if (filterData->savedContext)
66             filterData->state = FilterData::MarkedForRemoval;
67         else
68             m_filter.remove(&client);
69     }
70
71     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
72 }
73
74 std::unique_ptr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter& filter) const
75 {
76     static const unsigned maxCountChildNodes = 200;
77     if (filterElement().countChildNodes() > maxCountChildNodes)
78         return nullptr;
79
80     FloatRect targetBoundingBox = filter.targetBoundingBox();
81
82     // Add effects to the builder
83     auto builder = std::make_unique<SVGFilterBuilder>(SourceGraphic::create(filter));
84     for (auto& element : childrenOfType<SVGFilterPrimitiveStandardAttributes>(filterElement())) {
85         RefPtr<FilterEffect> effect = element.build(builder.get(), filter);
86         if (!effect) {
87             builder->clearEffects();
88             return nullptr;
89         }
90         builder->appendEffectToEffectReferences(effect, element.renderer());
91         element.setStandardAttributes(effect.get());
92         effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(&element, filterElement().primitiveUnits(), targetBoundingBox));
93         effect->setOperatingColorSpace(element.renderer()->style().svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
94         builder->add(element.result(), effect.release());
95     }
96     return builder;
97 }
98
99 bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode)
100 {
101     ASSERT(context);
102     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
103
104     if (m_filter.contains(&renderer)) {
105         FilterData* filterData = m_filter.get(&renderer);
106         if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
107             filterData->state = FilterData::CycleDetected;
108         return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
109     }
110
111     auto filterData = std::make_unique<FilterData>();
112     FloatRect targetBoundingBox = renderer.objectBoundingBox();
113
114     filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox);
115     if (filterData->boundaries.isEmpty())
116         return false;
117
118     // Determine absolute transformation matrix for filter. 
119     AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer);
120     if (!absoluteTransform.isInvertible())
121         return false;
122
123     // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
124     filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
125
126     // Determine absolute boundaries of the filter and the drawing region.
127     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
128     filterData->drawingRegion = renderer.strokeBoundingBox();
129     filterData->drawingRegion.intersect(filterData->boundaries);
130     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
131
132     // Create the SVGFilter object.
133     bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
134     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
135
136     // Create all relevant filter primitives.
137     filterData->builder = buildPrimitives(*filterData->filter);
138     if (!filterData->builder)
139         return false;
140
141     // Calculate the scale factor for the use of filterRes.
142     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
143     FloatSize scale(1, 1);
144     if (filterElement().hasAttribute(SVGNames::filterResAttr)) {
145         scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width());
146         scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height());
147     }
148
149     if (scale.isEmpty())
150         return false;
151
152     // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
153     FloatRect tempSourceRect = absoluteDrawingRegion;
154     ImageBuffer::isSizeClamped(tempSourceRect.size(), scale);
155     tempSourceRect.scale(scale.width(), scale.height());
156
157     // Set the scale level in SVGFilter.
158     filterData->filter->setFilterResolution(scale);
159
160     static const unsigned maxTotalOfEffectInputs = 100;
161     FilterEffect* lastEffect = filterData->builder->lastEffect();
162     if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs)
163         return false;
164
165     RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
166     FloatRect subRegion = lastEffect->maxEffectRect();
167     // At least one FilterEffect has a too big image size,
168     // recalculate the effect sizes with new scale factors.
169     if (!ImageBuffer::isSizeClamped(subRegion.size(), scale)) {
170         filterData->filter->setFilterResolution(scale);
171         RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
172     }
173
174     // If the drawingRegion is empty, we have something like <g filter=".."/>.
175     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
176     if (filterData->drawingRegion.isEmpty()) {
177         ASSERT(!m_filter.contains(&renderer));
178         filterData->savedContext = context;
179         m_filter.set(&renderer, WTF::move(filterData));
180         return false;
181     }
182
183     // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
184     AffineTransform effectiveTransform;
185     effectiveTransform.scale(scale.width(), scale.height());
186     effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
187
188     RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
189
190     auto sourceGraphic = SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, ColorSpaceLinearRGB, renderingMode);
191     if (!sourceGraphic) {
192         ASSERT(!m_filter.contains(&renderer));
193         filterData->savedContext = context;
194         m_filter.set(&renderer, WTF::move(filterData));
195         return false;
196     }
197     
198     // Set the rendering mode from the page's settings.
199     filterData->filter->setRenderingMode(renderingMode);
200
201     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
202     ASSERT(sourceGraphicContext);
203   
204     filterData->sourceGraphicBuffer = WTF::move(sourceGraphic);
205     filterData->savedContext = context;
206
207     context = sourceGraphicContext;
208
209     ASSERT(!m_filter.contains(&renderer));
210     m_filter.set(&renderer, WTF::move(filterData));
211
212     return true;
213 }
214
215 void RenderSVGResourceFilter::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
216 {
217     ASSERT(context);
218     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
219
220     FilterData* filterData = m_filter.get(&renderer);
221     if (!filterData)
222         return;
223
224     switch (filterData->state) {
225     case FilterData::MarkedForRemoval:
226         m_filter.remove(&renderer);
227         return;
228
229     case FilterData::CycleDetected:
230     case FilterData::Applying:
231         // We have a cycle if we are already applying the data.
232         // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
233         // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
234         // will continue correctly.
235         filterData->state = FilterData::PaintingSource;
236         return;
237
238     case FilterData::PaintingSource:
239         if (!filterData->savedContext) {
240             removeClientFromCache(renderer);
241             return;
242         }
243
244         context = filterData->savedContext;
245         filterData->savedContext = 0;
246         break;
247
248     case FilterData::Built: { } // Empty
249     }
250
251     FilterEffect* lastEffect = filterData->builder->lastEffect();
252
253     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
254         // This is the real filtering of the object. It just needs to be called on the
255         // initial filtering process. We just take the stored filter result on a
256         // second drawing.
257         if (filterData->state != FilterData::Built)
258             filterData->filter->setSourceImage(WTF::move(filterData->sourceGraphicBuffer));
259
260         // Always true if filterData is just built (filterData->state == FilterData::Built).
261         if (!lastEffect->hasResult()) {
262             filterData->state = FilterData::Applying;
263             lastEffect->applyAll();
264             lastEffect->correctFilterResultIfNeeded();
265             lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
266         }
267         filterData->state = FilterData::Built;
268
269         ImageBuffer* resultImage = lastEffect->asImageBuffer();
270         if (resultImage) {
271             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
272
273             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
274             context->drawImageBuffer(resultImage, renderer.style().colorSpace(), lastEffect->absolutePaintRect());
275             context->scale(filterData->filter->filterResolution());
276
277             context->concatCTM(filterData->shearFreeAbsoluteTransform);
278         }
279     }
280     filterData->sourceGraphicBuffer.reset();
281 }
282
283 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject& object)
284 {
285     return SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), object.objectBoundingBox());
286 }
287
288 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
289 {
290     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
291
292     for (const auto& objectFilterDataPair : m_filter) {
293         const auto& filterData = objectFilterDataPair.value;
294         if (filterData->state != FilterData::Built)
295             continue;
296
297         SVGFilterBuilder* builder = filterData->builder.get();
298         FilterEffect* effect = builder->effectByRenderer(object);
299         if (!effect)
300             continue;
301         // Since all effects shares the same attribute value, all
302         // or none of them will be changed.
303         if (!primitve->setFilterEffectAttribute(effect, attribute))
304             return;
305         builder->clearResultsRecursive(effect);
306
307         // Repaint the image on the screen.
308         markClientForInvalidation(*objectFilterDataPair.key, RepaintInvalidation);
309     }
310     markAllClientLayersForInvalidation();
311 }
312
313 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
314 {
315     FilterData* filterData = m_filter.get(object);
316     return filterData ? filterData->drawingRegion : FloatRect();
317 }
318
319 }