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