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.
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.
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.
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.
26 #if ENABLE(SVG) && ENABLE(FILTERS)
27 #include "RenderSVGResourceFilter.h"
29 #include "AffineTransform.h"
30 #include "FilterEffect.h"
31 #include "FloatPoint.h"
32 #include "FloatRect.h"
33 #include "GraphicsContext.h"
35 #include "ImageBuffer.h"
36 #include "ImageData.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"
46 #include "SVGStyledElement.h"
47 #include "SVGUnitTypes.h"
49 #include <wtf/UnusedParam.h>
50 #include <wtf/Vector.h>
56 RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
58 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
59 : RenderSVGResourceContainer(node)
63 RenderSVGResourceFilter::~RenderSVGResourceFilter()
65 if (m_filter.isEmpty())
68 deleteAllValues(m_filter);
72 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
74 if (!m_filter.isEmpty()) {
75 deleteAllValues(m_filter);
79 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
82 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
86 if (FilterData* filterData = m_filter.get(client)) {
87 if (filterData->savedContext)
88 filterData->markedForRemoval = true;
90 delete m_filter.take(client);
93 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
96 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter)
98 SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
99 bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
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())
107 SVGElement* element = static_cast<SVGElement*>(node);
108 if (!element->isFilterEffect())
111 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
112 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
114 builder->clearEffects();
117 builder->appendEffectToEffectReferences(effect, effectElement->renderer());
118 effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get());
119 builder->add(effectElement->result(), effect);
121 return builder.release();
124 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
126 bool matchesFilterSize = true;
127 if (size.width() > kMaxFilterSize) {
128 scale.setWidth(scale.width() * kMaxFilterSize / size.width());
129 matchesFilterSize = false;
131 if (size.height() > kMaxFilterSize) {
132 scale.setHeight(scale.height() * kMaxFilterSize / size.height());
133 matchesFilterSize = false;
136 return matchesFilterSize;
139 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
144 ASSERT(resourceMode == ApplyToDefaultMode);
146 UNUSED_PARAM(resourceMode);
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)
156 delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
159 OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
160 FloatRect targetBoundingBox = object->objectBoundingBox();
162 SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
163 filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox);
164 if (filterData->boundaries.isEmpty())
167 // Determine absolute transformation matrix for filter.
168 AffineTransform absoluteTransform;
169 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
170 if (!absoluteTransform.isInvertible())
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(), 0, 0);
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);
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);
186 // Create all relevant filter primitives.
187 filterData->builder = buildPrimitives(filterData->filter.get());
188 if (!filterData->builder)
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());
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);
207 // Set the scale level in SVGFilter.
208 filterData->filter->setFilterResolution(scale);
210 FilterEffect* lastEffect = filterData->builder->lastEffect();
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);
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());
232 absoluteDrawingRegion.scale(scale.width(), scale.height());
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());
242 GraphicsContext* sourceGraphicContext = sourceGraphic->context();
243 ASSERT(sourceGraphicContext);
245 sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y());
246 if (scale.width() != 1 || scale.height() != 1)
247 sourceGraphicContext->scale(scale);
249 sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform);
250 sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size()));
251 filterData->sourceGraphicBuffer = sourceGraphic.release();
252 filterData->savedContext = context;
254 context = sourceGraphicContext;
256 ASSERT(!m_filter.contains(object));
257 m_filter.set(object, filterData.leakPtr());
262 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*)
267 ASSERT(resourceMode == ApplyToDefaultMode);
269 UNUSED_PARAM(resourceMode);
272 FilterData* filterData = m_filter.get(object);
276 if (filterData->markedForRemoval) {
277 delete m_filter.take(object);
281 if (!filterData->builded) {
282 if (!filterData->savedContext) {
283 removeClientFromCache(object);
287 context = filterData->savedContext;
288 filterData->savedContext = 0;
290 if (filterData->sourceGraphicBuffer)
291 filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
295 FilterEffect* lastEffect = filterData->builder->lastEffect();
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
301 if (!filterData->builded)
302 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
304 // Always true if filterData is just built (filterData->builded is false).
305 if (!lastEffect->hasResult()) {
308 ImageBuffer* resultImage = lastEffect->asImageBuffer();
310 resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
313 filterData->builded = true;
315 ImageBuffer* resultImage = lastEffect->asImageBuffer();
317 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
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());
324 context->concatCTM(filterData->shearFreeAbsoluteTransform);
327 filterData->sourceGraphicBuffer.clear();
330 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
332 if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
333 return element->filterBoundingBox(object->objectBoundingBox());
338 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
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());
344 for (; it != end; ++it) {
345 FilterData* filterData = it->second;
346 if (!filterData->builded)
349 SVGFilterBuilder* builder = filterData->builder.get();
350 FilterEffect* effect = builder->effectByRenderer(object);
353 // Since all effects shares the same attribute value, all
354 // or none of them will be changed.
355 if (!primitve->setFilterEffectAttribute(effect, attribute))
357 builder->clearResultsRecursive(effect);
359 // Repaint the image on the screen.
360 markClientForInvalidation(it->first, RepaintInvalidation);