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