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 #include "core/rendering/svg/RenderSVGResourceFilter.h"
28 #include "core/page/Settings.h"
29 #include "core/platform/graphics/filters/SourceAlpha.h"
30 #include "core/platform/graphics/filters/SourceGraphic.h"
31 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h"
32 #include "core/rendering/svg/SVGRenderingContext.h"
33 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
39 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
41 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
42 : RenderSVGResourceContainer(node)
46 RenderSVGResourceFilter::~RenderSVGResourceFilter()
51 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
54 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
57 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
61 if (FilterData* filterData = m_filter.get(client)) {
62 if (filterData->savedContext)
63 filterData->state = FilterData::MarkedForRemoval;
65 m_filter.remove(client);
68 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
71 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
73 SVGFilterElement* filterElement = toSVGFilterElement(element());
74 FloatRect targetBoundingBox = filter->targetBoundingBox();
76 // Add effects to the builder
77 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
78 for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
79 if (!node->isSVGElement())
82 SVGElement* element = toSVGElement(node);
83 if (!element->isFilterEffect() || !element->renderer())
86 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
87 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
89 builder->clearEffects();
92 builder->appendEffectToEffectReferences(effect, effectElement->renderer());
93 effectElement->setStandardAttributes(effect.get());
94 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnitsCurrentValue(), targetBoundingBox));
95 effect->setOperatingColorSpace(
96 effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
97 builder->add(effectElement->resultCurrentValue(), effect);
99 return builder.release();
102 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
104 bool matchesFilterSize = true;
105 if (size.width() > kMaxFilterSize) {
106 scale.setWidth(scale.width() * kMaxFilterSize / size.width());
107 matchesFilterSize = false;
109 if (size.height() > kMaxFilterSize) {
110 scale.setHeight(scale.height() * kMaxFilterSize / size.height());
111 matchesFilterSize = false;
114 return matchesFilterSize;
117 static bool createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform,
118 OwnPtr<ImageBuffer>& imageBuffer, RenderingMode renderingMode)
120 IntRect paintRect = SVGRenderingContext::calculateImageBufferRect(targetRect, absoluteTransform);
121 // Don't create empty ImageBuffers.
122 if (paintRect.isEmpty())
125 OwnPtr<ImageBuffer> image = ImageBuffer::create(paintRect.size(), 1, renderingMode);
129 GraphicsContext* imageContext = image->context();
130 ASSERT(imageContext);
132 imageContext->translate(-paintRect.x(), -paintRect.y());
133 imageContext->concatCTM(absoluteTransform);
135 imageBuffer = image.release();
139 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
143 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
145 if (m_filter.contains(object)) {
146 FilterData* filterData = m_filter.get(object);
147 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
148 filterData->state = FilterData::CycleDetected;
149 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
152 OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
153 FloatRect targetBoundingBox = object->objectBoundingBox();
155 SVGFilterElement* filterElement = toSVGFilterElement(element());
156 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnitsCurrentValue(), targetBoundingBox);
157 if (filterData->boundaries.isEmpty())
160 // Determine absolute transformation matrix for filter.
161 AffineTransform absoluteTransform;
162 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
163 if (!absoluteTransform.isInvertible())
166 // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
167 filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
169 // Determine absolute boundaries of the filter and the drawing region.
170 FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
171 filterData->drawingRegion = object->strokeBoundingBox();
172 filterData->drawingRegion.intersect(filterData->boundaries);
173 FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
175 // Create the SVGFilter object.
176 bool primitiveBoundingBoxMode = filterElement->primitiveUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
177 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
179 // Create all relevant filter primitives.
180 filterData->builder = buildPrimitives(filterData->filter.get());
181 if (!filterData->builder)
184 // Calculate the scale factor for the use of filterRes.
185 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
186 FloatSize scale(1, 1);
187 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
188 scale.setWidth(filterElement->filterResXCurrentValue() / absoluteFilterBoundaries.width());
189 scale.setHeight(filterElement->filterResYCurrentValue() / absoluteFilterBoundaries.height());
195 // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
196 FloatRect tempSourceRect = absoluteDrawingRegion;
197 tempSourceRect.scale(scale.width(), scale.height());
198 fitsInMaximumImageSize(tempSourceRect.size(), scale);
200 // Set the scale level in SVGFilter.
201 filterData->filter->setFilterResolution(scale);
203 FilterEffect* lastEffect = filterData->builder->lastEffect();
207 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
208 FloatRect subRegion = lastEffect->maxEffectRect();
209 // At least one FilterEffect has a too big image size,
210 // recalculate the effect sizes with new scale factors.
211 if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
212 filterData->filter->setFilterResolution(scale);
213 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect);
216 // If the drawingRegion is empty, we have something like <g filter=".."/>.
217 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
218 if (filterData->drawingRegion.isEmpty()) {
219 ASSERT(!m_filter.contains(object));
220 filterData->savedContext = context;
221 m_filter.set(object, filterData.release());
225 // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
226 AffineTransform effectiveTransform;
227 effectiveTransform.scale(scale.width(), scale.height());
228 effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
230 OwnPtr<ImageBuffer> sourceGraphic;
231 RenderingMode renderingMode = object->document().settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
232 if (!createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, renderingMode)) {
233 ASSERT(!m_filter.contains(object));
234 filterData->savedContext = context;
235 m_filter.set(object, filterData.release());
239 // Set the rendering mode from the page's settings.
240 filterData->filter->setRenderingMode(renderingMode);
242 GraphicsContext* sourceGraphicContext = sourceGraphic->context();
243 ASSERT(sourceGraphicContext);
245 filterData->sourceGraphicBuffer = sourceGraphic.release();
246 filterData->savedContext = context;
248 context = sourceGraphicContext;
250 ASSERT(!m_filter.contains(object));
251 m_filter.set(object, filterData.release());
256 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
260 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
262 FilterData* filterData = m_filter.get(object);
266 switch (filterData->state) {
267 case FilterData::MarkedForRemoval:
268 m_filter.remove(object);
271 case FilterData::CycleDetected:
272 case FilterData::Applying:
273 // We have a cycle if we are already applying the data.
274 // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
275 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
276 // will continue correctly.
277 filterData->state = FilterData::PaintingSource;
280 case FilterData::PaintingSource:
281 if (!filterData->savedContext) {
282 removeClientFromCache(object);
286 context = filterData->savedContext;
287 filterData->savedContext = 0;
290 case FilterData::Built: { } // Empty
293 FilterEffect* lastEffect = filterData->builder->lastEffect();
295 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
296 // This is the real filtering of the object. It just needs to be called on the
297 // initial filtering process. We just take the stored filter result on a
299 if (filterData->state != FilterData::Built)
300 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
302 // Always true if filterData is just built (filterData->state == FilterData::Built).
303 if (!lastEffect->hasResult()) {
304 filterData->state = FilterData::Applying;
306 lastEffect->correctFilterResultIfNeeded();
307 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
309 filterData->state = FilterData::Built;
311 ImageBuffer* resultImage = lastEffect->asImageBuffer();
313 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
315 context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
316 context->drawImageBuffer(resultImage, lastEffect->absolutePaintRect());
317 context->scale(filterData->filter->filterResolution());
319 context->concatCTM(filterData->shearFreeAbsoluteTransform);
322 filterData->sourceGraphicBuffer.clear();
325 FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
327 if (SVGFilterElement* element = toSVGFilterElement(this->element()))
328 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnitsCurrentValue(), object->objectBoundingBox());
333 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
335 FilterMap::iterator it = m_filter.begin();
336 FilterMap::iterator end = m_filter.end();
337 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
339 for (; it != end; ++it) {
340 FilterData* filterData = it->value.get();
341 if (filterData->state != FilterData::Built)
344 SVGFilterBuilder* builder = filterData->builder.get();
345 FilterEffect* effect = builder->effectByRenderer(object);
348 // Since all effects shares the same attribute value, all
349 // or none of them will be changed.
350 if (!primitve->setFilterEffectAttribute(effect, attribute))
352 builder->clearResultsRecursive(effect);
354 // Repaint the image on the screen.
355 markClientForInvalidation(it->key, RepaintInvalidation);
357 markAllClientLayersForInvalidation();
360 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
362 FilterData* filterData = m_filter.get(object);
363 return filterData ? filterData->drawingRegion : FloatRect();