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 "FloatPoint.h"
31 #include "FloatRect.h"
32 #include "GraphicsContext.h"
33 #include "Image.h"
34 #include "ImageBuffer.h"
35 #include "ImageData.h"
36 #include "IntRect.h"
37 #include "RenderSVGResource.h"
38 #include "RenderSVGResourceFilterPrimitive.h"
39 #include "SVGElement.h"
40 #include "SVGFilter.h"
41 #include "SVGFilterElement.h"
42 #include "SVGFilterPrimitiveStandardAttributes.h"
43 #include "SVGImageBufferTools.h"
44 #include "SVGNames.h"
45 #include "SVGStyledElement.h"
46 #include "SVGUnitTypes.h"
47 
48 #include <wtf/UnusedParam.h>
49 #include <wtf/Vector.h>
50 
51 static const float kMaxFilterSize = 5000.0f;
52 
53 using namespace std;
54 
55 namespace WebCore {
56 
57 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
58 
RenderSVGResourceFilter(SVGFilterElement * node)59 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
60     : RenderSVGResourceContainer(node)
61 {
62 }
63 
~RenderSVGResourceFilter()64 RenderSVGResourceFilter::~RenderSVGResourceFilter()
65 {
66     if (m_filter.isEmpty())
67         return;
68 
69     deleteAllValues(m_filter);
70     m_filter.clear();
71 }
72 
removeAllClientsFromCache(bool markForInvalidation)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 
removeClientFromCache(RenderObject * client,bool markForInvalidation)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->markedForRemoval = true;
90         else
91             delete m_filter.take(client);
92     }
93 
94     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
95 }
96 
buildPrimitives(Filter * filter)97 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter)
98 {
99     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
100     bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
101 
102     // Add effects to the builder
103     RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter);
104     for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
105         if (!node->isSVGElement())
106             continue;
107 
108         SVGElement* element = static_cast<SVGElement*>(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(primitiveBoundingBoxMode, effect.get());
120         builder->add(effectElement->result(), effect);
121     }
122     return builder.release();
123 }
124 
fitsInMaximumImageSize(const FloatSize & size,FloatSize & scale)125 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
126 {
127     bool matchesFilterSize = true;
128     if (size.width() > kMaxFilterSize) {
129         scale.setWidth(scale.width() * kMaxFilterSize / size.width());
130         matchesFilterSize = false;
131     }
132     if (size.height() > kMaxFilterSize) {
133         scale.setHeight(scale.height() * kMaxFilterSize / size.height());
134         matchesFilterSize = false;
135     }
136 
137     return matchesFilterSize;
138 }
139 
applyResource(RenderObject * object,RenderStyle *,GraphicsContext * & context,unsigned short resourceMode)140 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
141 {
142     ASSERT(object);
143     ASSERT(context);
144 #ifndef NDEBUG
145     ASSERT(resourceMode == ApplyToDefaultMode);
146 #else
147     UNUSED_PARAM(resourceMode);
148 #endif
149 
150     // Returning false here, to avoid drawings onto the context. We just want to
151     // draw the stored filter output, not the unfiltered object as well.
152     if (m_filter.contains(object)) {
153         FilterData* filterData = m_filter.get(object);
154         if (filterData->builded)
155             return false;
156 
157         delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
158     }
159 
160     OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
161     FloatRect targetBoundingBox = object->objectBoundingBox();
162 
163     SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
164     filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox);
165     if (filterData->boundaries.isEmpty())
166         return false;
167 
168     // Determine absolute transformation matrix for filter.
169     AffineTransform absoluteTransform;
170     SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
171     if (!absoluteTransform.isInvertible())
172         return false;
173 
174     // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
175     filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f());
176 
177     // Determine absolute boundaries of the filter and the drawing region.
178     FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
179     FloatRect drawingRegion = object->strokeBoundingBox();
180     drawingRegion.intersect(filterData->boundaries);
181     FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion);
182 
183     // Create the SVGFilter object.
184     bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
185     filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
186 
187     // Create all relevant filter primitives.
188     filterData->builder = buildPrimitives(filterData->filter.get());
189     if (!filterData->builder)
190         return false;
191 
192     // Calculate the scale factor for the use of filterRes.
193     // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
194     FloatSize scale(1, 1);
195     if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
196         scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
197         scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
198     }
199 
200     if (scale.isEmpty())
201         return false;
202 
203     // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
204     FloatRect tempSourceRect = absoluteDrawingRegion;
205     tempSourceRect.scale(scale.width(), scale.height());
206     fitsInMaximumImageSize(tempSourceRect.size(), scale);
207 
208     // Set the scale level in SVGFilter.
209     filterData->filter->setFilterResolution(scale);
210 
211     FilterEffect* lastEffect = filterData->builder->lastEffect();
212     if (!lastEffect)
213         return false;
214 
215     RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
216     FloatRect subRegion = lastEffect->maxEffectRect();
217     // At least one FilterEffect has a too big image size,
218     // recalculate the effect sizes with new scale factors.
219     if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
220         filterData->filter->setFilterResolution(scale);
221         RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
222     }
223 
224     // If the drawingRegion is empty, we have something like <g filter=".."/>.
225     // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
226     if (drawingRegion.isEmpty()) {
227         ASSERT(!m_filter.contains(object));
228         filterData->savedContext = context;
229         m_filter.set(object, filterData.leakPtr());
230         return false;
231     }
232 
233     absoluteDrawingRegion.scale(scale.width(), scale.height());
234 
235     OwnPtr<ImageBuffer> sourceGraphic;
236     if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) {
237         ASSERT(!m_filter.contains(object));
238         filterData->savedContext = context;
239         m_filter.set(object, filterData.leakPtr());
240         return false;
241     }
242 
243     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
244     ASSERT(sourceGraphicContext);
245 
246     sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y());
247     if (scale.width() != 1 || scale.height() != 1)
248         sourceGraphicContext->scale(scale);
249 
250     sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform);
251     sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size()));
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 
postApplyResource(RenderObject * object,GraphicsContext * & context,unsigned short resourceMode,const Path *)263 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*)
264 {
265     ASSERT(object);
266     ASSERT(context);
267 #ifndef NDEBUG
268     ASSERT(resourceMode == ApplyToDefaultMode);
269 #else
270     UNUSED_PARAM(resourceMode);
271 #endif
272 
273     FilterData* filterData = m_filter.get(object);
274     if (!filterData)
275         return;
276 
277     if (filterData->markedForRemoval) {
278         delete m_filter.take(object);
279         return;
280     }
281 
282     if (!filterData->builded) {
283         if (!filterData->savedContext) {
284             removeClientFromCache(object);
285             return;
286         }
287 
288         context = filterData->savedContext;
289         filterData->savedContext = 0;
290 #if !USE(CG)
291         if (filterData->sourceGraphicBuffer)
292             filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
293 #endif
294     }
295 
296     FilterEffect* lastEffect = filterData->builder->lastEffect();
297 
298     if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
299         // This is the real filtering of the object. It just needs to be called on the
300         // initial filtering process. We just take the stored filter result on a
301         // second drawing.
302         if (!filterData->builded)
303             filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
304 
305         // Always true if filterData is just built (filterData->builded is false).
306         if (!lastEffect->hasResult()) {
307             lastEffect->apply();
308 #if !USE(CG)
309             ImageBuffer* resultImage = lastEffect->asImageBuffer();
310             if (resultImage)
311                 resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
312 #endif
313         }
314         filterData->builded = true;
315 
316         ImageBuffer* resultImage = lastEffect->asImageBuffer();
317         if (resultImage) {
318             context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
319 
320             context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
321             context->clip(lastEffect->maxEffectRect());
322             context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
323             context->scale(filterData->filter->filterResolution());
324 
325             context->concatCTM(filterData->shearFreeAbsoluteTransform);
326         }
327     }
328     filterData->sourceGraphicBuffer.clear();
329 }
330 
resourceBoundingBox(RenderObject * object)331 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
332 {
333     if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
334         return element->filterBoundingBox(object->objectBoundingBox());
335 
336     return FloatRect();
337 }
338 
primitiveAttributeChanged(RenderObject * object,const QualifiedName & attribute)339 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
340 {
341     HashMap<RenderObject*, FilterData*>::iterator it = m_filter.begin();
342     HashMap<RenderObject*, FilterData*>::iterator end = m_filter.end();
343     SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
344 
345     for (; it != end; ++it) {
346         FilterData* filterData = it->second;
347         if (!filterData->builded)
348             continue;
349 
350         SVGFilterBuilder* builder = filterData->builder.get();
351         FilterEffect* effect = builder->effectByRenderer(object);
352         if (!effect)
353             continue;
354         // Since all effects shares the same attribute value, all
355         // or none of them will be changed.
356         if (!primitve->setFilterEffectAttribute(effect, attribute))
357             return;
358         builder->clearResultsRecursive(effect);
359 
360         // Repaint the image on the screen.
361         markClientForInvalidation(it->first, RepaintInvalidation);
362     }
363 }
364 
365 }
366 #endif
367