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.
25 #include "RenderSVGResourceFilter.h"
27 #include "ElementChildIterator.h"
28 #include "FilterEffect.h"
29 #include "FloatPoint.h"
31 #include "GraphicsContext.h"
33 #include "ImageData.h"
36 #include "RenderSVGResourceFilterPrimitive.h"
37 #include "RenderView.h"
38 #include "SVGFilterPrimitiveStandardAttributes.h"
40 #include "SVGRenderingContext.h"
42 #include "SourceGraphic.h"
46 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement& element, Ref<RenderStyle>&& style)
47 : RenderSVGResourceContainer(element, WTF::move(style))
51 RenderSVGResourceFilter::~RenderSVGResourceFilter()
55 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
59 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
62 void RenderSVGResourceFilter::removeClientFromCache(RenderElement& client, bool markForInvalidation)
64 if (FilterData* filterData = m_filter.get(&client)) {
65 if (filterData->savedContext)
66 filterData->state = FilterData::MarkedForRemoval;
68 m_filter.remove(&client);
71 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
74 std::unique_ptr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter& filter) const
76 static const unsigned maxCountChildNodes = 200;
77 if (filterElement().countChildNodes() > maxCountChildNodes)
80 FloatRect targetBoundingBox = filter.targetBoundingBox();
82 // Add effects to the builder
83 auto builder = std::make_unique<SVGFilterBuilder>(SourceGraphic::create(filter));
84 for (auto& element : childrenOfType<SVGFilterPrimitiveStandardAttributes>(filterElement())) {
85 RefPtr<FilterEffect> effect = element.build(builder.get(), filter);
87 builder->clearEffects();
90 builder->appendEffectToEffectReferences(effect, element.renderer());
91 element.setStandardAttributes(effect.get());
92 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(&element, filterElement().primitiveUnits(), targetBoundingBox));
93 effect->setOperatingColorSpace(element.renderer()->style().svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
94 builder->add(element.result(), effect.release());
99 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
101 FloatSize scaledSize(size);
102 scaledSize.scale(scale.width(), scale.height());
103 float scaledArea = scaledSize.width() * scaledSize.height();
105 if (scaledArea <= FilterEffect::maxFilterArea())
108 // If area of scaled size is bigger than the upper limit, adjust the scale
110 scale.scale(sqrt(FilterEffect::maxFilterArea() / scaledArea));
114 bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode)
117 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
119 if (m_filter.contains(&renderer)) {
120 FilterData* filterData = m_filter.get(&renderer);
121 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
122 filterData->state = FilterData::CycleDetected;
123 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
126 auto filterData = std::make_unique<FilterData>();
127 FloatRect targetBoundingBox = renderer.objectBoundingBox();
129 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox);
130 if (filterData->boundaries.isEmpty())
133 // Determine absolute transformation matrix for filter.
134 AffineTransform absoluteTransform;
135 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer, absoluteTransform);
136 if (!absoluteTransform.isInvertible())
139 // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
140 filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
142 // Determine absolute boundaries of the filter and the drawing region.
143 FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
144 filterData->drawingRegion = renderer.strokeBoundingBox();
145 filterData->drawingRegion.intersect(filterData->boundaries);
146 FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
148 // Create the SVGFilter object.
149 bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
150 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
152 // Create all relevant filter primitives.
153 filterData->builder = buildPrimitives(*filterData->filter);
154 if (!filterData->builder)
157 // Calculate the scale factor for the use of filterRes.
158 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
159 FloatSize scale(1, 1);
160 if (filterElement().hasAttribute(SVGNames::filterResAttr)) {
161 scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width());
162 scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height());
168 // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
169 FloatRect tempSourceRect = absoluteDrawingRegion;
170 tempSourceRect.scale(scale.width(), scale.height());
171 fitsInMaximumImageSize(tempSourceRect.size(), scale);
173 // Set the scale level in SVGFilter.
174 filterData->filter->setFilterResolution(scale);
176 static const unsigned maxTotalOfEffectInputs = 100;
177 FilterEffect* lastEffect = filterData->builder->lastEffect();
178 if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs)
181 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
182 FloatRect subRegion = lastEffect->maxEffectRect();
183 // At least one FilterEffect has a too big image size,
184 // recalculate the effect sizes with new scale factors.
185 if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
186 filterData->filter->setFilterResolution(scale);
187 RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
190 // If the drawingRegion is empty, we have something like <g filter=".."/>.
191 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
192 if (filterData->drawingRegion.isEmpty()) {
193 ASSERT(!m_filter.contains(&renderer));
194 filterData->savedContext = context;
195 m_filter.set(&renderer, WTF::move(filterData));
199 // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter.
200 AffineTransform effectiveTransform;
201 effectiveTransform.scale(scale.width(), scale.height());
202 effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
204 std::unique_ptr<ImageBuffer> sourceGraphic;
205 RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
206 if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) {
207 ASSERT(!m_filter.contains(&renderer));
208 filterData->savedContext = context;
209 m_filter.set(&renderer, WTF::move(filterData));
213 // Set the rendering mode from the page's settings.
214 filterData->filter->setRenderingMode(renderingMode);
216 GraphicsContext* sourceGraphicContext = sourceGraphic->context();
217 ASSERT(sourceGraphicContext);
219 filterData->sourceGraphicBuffer = WTF::move(sourceGraphic);
220 filterData->savedContext = context;
222 context = sourceGraphicContext;
224 ASSERT(!m_filter.contains(&renderer));
225 m_filter.set(&renderer, WTF::move(filterData));
230 void RenderSVGResourceFilter::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
233 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
235 FilterData* filterData = m_filter.get(&renderer);
239 switch (filterData->state) {
240 case FilterData::MarkedForRemoval:
241 m_filter.remove(&renderer);
244 case FilterData::CycleDetected:
245 case FilterData::Applying:
246 // We have a cycle if we are already applying the data.
247 // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
248 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
249 // will continue correctly.
250 filterData->state = FilterData::PaintingSource;
253 case FilterData::PaintingSource:
254 if (!filterData->savedContext) {
255 removeClientFromCache(renderer);
259 context = filterData->savedContext;
260 filterData->savedContext = 0;
263 case FilterData::Built: { } // Empty
266 FilterEffect* lastEffect = filterData->builder->lastEffect();
268 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
269 // This is the real filtering of the object. It just needs to be called on the
270 // initial filtering process. We just take the stored filter result on a
272 if (filterData->state != FilterData::Built)
273 filterData->filter->setSourceImage(WTF::move(filterData->sourceGraphicBuffer));
275 // Always true if filterData is just built (filterData->state == FilterData::Built).
276 if (!lastEffect->hasResult()) {
277 filterData->state = FilterData::Applying;
278 lastEffect->applyAll();
279 lastEffect->correctFilterResultIfNeeded();
280 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
282 filterData->state = FilterData::Built;
284 ImageBuffer* resultImage = lastEffect->asImageBuffer();
286 context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
288 context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
289 context->drawImageBuffer(resultImage, renderer.style().colorSpace(), lastEffect->absolutePaintRect());
290 context->scale(filterData->filter->filterResolution());
292 context->concatCTM(filterData->shearFreeAbsoluteTransform);
295 filterData->sourceGraphicBuffer.reset();
298 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject& object)
300 return SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), object.objectBoundingBox());
303 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
305 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
307 for (const auto& objectFilterDataPair : m_filter) {
308 const auto& filterData = objectFilterDataPair.value;
309 if (filterData->state != FilterData::Built)
312 SVGFilterBuilder* builder = filterData->builder.get();
313 FilterEffect* effect = builder->effectByRenderer(object);
316 // Since all effects shares the same attribute value, all
317 // or none of them will be changed.
318 if (!primitve->setFilterEffectAttribute(effect, attribute))
320 builder->clearResultsRecursive(effect);
322 // Repaint the image on the screen.
323 markClientForInvalidation(*objectFilterDataPair.key, RepaintInvalidation);
325 markAllClientLayersForInvalidation();
328 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
330 FilterData* filterData = m_filter.get(object);
331 return filterData ? filterData->drawingRegion : FloatRect();