• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
52 using namespace std;
53 
54 namespace WebCore {
55 
56 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
57 
RenderSVGResourceFilter(SVGFilterElement * node)58 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
59     : RenderSVGResourceContainer(node)
60 {
61 }
62 
~RenderSVGResourceFilter()63 RenderSVGResourceFilter::~RenderSVGResourceFilter()
64 {
65     if (m_filter.isEmpty())
66         return;
67 
68     deleteAllValues(m_filter);
69     m_filter.clear();
70 }
71 
removeAllClientsFromCache(bool markForInvalidation)72 void 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 
removeClientFromCache(RenderObject * client,bool markForInvalidation)82 void 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 
buildPrimitives(Filter * filter)96 PassRefPtr<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 
fitsInMaximumImageSize(const FloatSize & size,FloatSize & scale)124 bool 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 
applyResource(RenderObject * object,RenderStyle *,GraphicsContext * & context,unsigned short resourceMode)139 bool 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 
postApplyResource(RenderObject * object,GraphicsContext * & context,unsigned short resourceMode,const Path *)262 void 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 
resourceBoundingBox(RenderObject * object)330 FloatRect 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 
primitiveAttributeChanged(RenderObject * object,const QualifiedName & attribute)338 void 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