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/frame/Settings.h"
29 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h"
30 #include "core/rendering/svg/SVGRenderingContext.h"
31 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h"
32 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
33 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
34 #include "platform/graphics/filters/SourceAlpha.h"
35 #include "platform/graphics/filters/SourceGraphic.h"
41 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
43 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
44 : RenderSVGResourceContainer(node)
48 RenderSVGResourceFilter::~RenderSVGResourceFilter()
53 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
55 return child->isSVGResourceFilterPrimitive();
58 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
61 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
64 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
68 if (FilterData* filterData = m_filter.get(client)) {
69 if (filterData->savedContext)
70 filterData->state = FilterData::MarkedForRemoval;
72 m_filter.remove(client);
75 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
78 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
80 SVGFilterElement* filterElement = toSVGFilterElement(element());
81 FloatRect targetBoundingBox = filter->targetBoundingBox();
83 // Add effects to the builder
84 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
85 for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) {
86 if (!element->isFilterEffect() || !element->renderer())
89 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
90 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
92 builder->clearEffects();
95 builder->appendEffectToEffectReferences(effect, effectElement->renderer());
96 effectElement->setStandardAttributes(effect.get());
97 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox));
98 effect->setOperatingColorSpace(
99 effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
100 builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
102 return builder.release();
105 void RenderSVGResourceFilter::adjustScaleForMaximumImageSize(const FloatSize& size, FloatSize& filterScale)
107 FloatSize scaledSize(size);
108 scaledSize.scale(filterScale.width(), filterScale.height());
109 float scaledArea = scaledSize.width() * scaledSize.height();
111 if (scaledArea <= FilterEffect::maxFilterArea())
114 // If area of scaled size is bigger than the upper limit, adjust the scale
116 filterScale.scale(sqrt(FilterEffect::maxFilterArea() / scaledArea));
119 static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBuffer)
121 IntRect paintRect = filter->sourceImageRect();
122 // Don't create empty ImageBuffers.
123 if (paintRect.isEmpty())
126 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size()));
127 if (!surface->isValid())
129 OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release());
131 GraphicsContext* imageContext = image->context();
132 ASSERT(imageContext);
134 imageContext->translate(-paintRect.x(), -paintRect.y());
135 imageContext->concatCTM(filter->absoluteTransform());
136 imageBuffer = image.release();
140 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement)
142 SkiaImageFilterBuilder builder(context);
143 RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
144 // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed
145 if (!context->isRecordingCanvas()) // Recording canvases do not use the cache
146 filterData->filter->enableCache();
147 FloatRect boundaries = enclosingIntRect(filterData->boundaries);
149 float scaledArea = boundaries.width() * boundaries.height();
151 // If area of scaled size is bigger than the upper limit, adjust the scale
153 if (scaledArea > FilterEffect::maxFilterArea()) {
154 float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
155 context->scale(scale, scale);
157 // Clip drawing of filtered image to primitive boundaries.
158 context->clipRect(boundaries);
159 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
160 // Get boundaries in device coords.
161 // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
162 // zoom or High DPI adjustments?
163 FloatSize size = context->getCTM().mapSize(boundaries.size());
164 // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
165 float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width();
166 float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height();
167 // Scale the CTM so the primitive is drawn to filterRes.
168 context->scale(filterResScaleX, filterResScaleY);
169 // Create a resize filter with the inverse scale.
170 AffineTransform resizeMatrix;
171 resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY);
172 imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get());
174 // If the CTM contains rotation or shearing, apply the filter to
175 // the unsheared/unrotated matrix, and do the shearing/rotation
177 AffineTransform ctm = context->getCTM();
178 if (ctm.b() || ctm.c()) {
179 AffineTransform scaleAndTranslate;
180 scaleAndTranslate.translate(ctm.e(), ctm.f());
181 scaleAndTranslate.scale(ctm.xScale(), ctm.yScale());
182 ASSERT(scaleAndTranslate.isInvertible());
183 AffineTransform shearAndRotate = scaleAndTranslate.inverse();
184 shearAndRotate.multiply(ctm);
185 context->setCTM(scaleAndTranslate);
186 imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get());
188 context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
191 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData)
195 // FIXME: Remove the cache when impl-size painting is enabled on every platform and the non impl-side painting path is removed
196 if (!context->isRecordingCanvas()) // Recording canvases do not use the cache
197 filterData->filter->disableCache();
200 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
204 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
206 clearInvalidationMask();
208 bool deferredFiltersEnabled = object->document().settings()->deferredFiltersEnabled();
209 if (m_filter.contains(object)) {
210 FilterData* filterData = m_filter.get(object);
211 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
212 filterData->state = FilterData::CycleDetected;
213 if (deferredFiltersEnabled && filterData->state == FilterData::Built) {
214 SVGFilterElement* filterElement = toSVGFilterElement(element());
215 beginDeferredFilter(context, filterData, filterElement);
218 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
221 OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
222 FloatRect targetBoundingBox = object->objectBoundingBox();
224 SVGFilterElement* filterElement = toSVGFilterElement(element());
225 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox);
226 if (filterData->boundaries.isEmpty())
229 // Determine absolute transformation matrix for filter.
230 AffineTransform absoluteTransform;
231 SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransform);
232 if (!absoluteTransform.isInvertible())
235 // Filters cannot handle a full transformation, only scales in each direction.
236 FloatSize filterScale;
238 // Calculate the scale factor for the filter.
239 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
240 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
241 // If resolution is specified, scale to match it.
242 filterScale = FloatSize(
243 filterElement->filterResX()->currentValue()->value() / filterData->boundaries.width(),
244 filterElement->filterResY()->currentValue()->value() / filterData->boundaries.height());
246 // Otherwise, use the scale of the absolute transform.
247 filterScale = FloatSize(absoluteTransform.xScale(), absoluteTransform.yScale());
249 // The size of the scaled filter boundaries shouldn't be bigger than kMaxFilterSize.
250 // Intermediate filters are limited by the filter boundaries so they can't be bigger than this.
251 adjustScaleForMaximumImageSize(filterData->boundaries.size(), filterScale);
253 filterData->drawingRegion = object->strokeBoundingBox();
254 filterData->drawingRegion.intersect(filterData->boundaries);
255 FloatRect absoluteDrawingRegion = filterData->drawingRegion;
256 if (!deferredFiltersEnabled)
257 absoluteDrawingRegion.scale(filterScale.width(), filterScale.height());
259 IntRect intDrawingRegion = enclosingIntRect(absoluteDrawingRegion);
261 // Create the SVGFilter object.
262 bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
263 filterData->shearFreeAbsoluteTransform = AffineTransform();
264 if (!deferredFiltersEnabled)
265 filterData->shearFreeAbsoluteTransform.scale(filterScale.width(), filterScale.height());
266 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
268 // Create all relevant filter primitives.
269 filterData->builder = buildPrimitives(filterData->filter.get());
270 if (!filterData->builder)
273 FilterEffect* lastEffect = filterData->builder->lastEffect();
277 lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
279 if (deferredFiltersEnabled) {
280 FilterData* data = filterData.get();
281 m_filter.set(object, filterData.release());
282 beginDeferredFilter(context, data, filterElement);
286 // If the drawingRegion is empty, we have something like <g filter=".."/>.
287 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
288 if (filterData->drawingRegion.isEmpty()) {
289 ASSERT(!m_filter.contains(object));
290 filterData->savedContext = context;
291 m_filter.set(object, filterData.release());
295 OwnPtr<ImageBuffer> sourceGraphic;
296 if (!createImageBuffer(filterData->filter.get(), sourceGraphic)) {
297 ASSERT(!m_filter.contains(object));
298 filterData->savedContext = context;
299 m_filter.set(object, filterData.release());
303 GraphicsContext* sourceGraphicContext = sourceGraphic->context();
304 ASSERT(sourceGraphicContext);
306 filterData->sourceGraphicBuffer = sourceGraphic.release();
307 filterData->savedContext = context;
309 context = sourceGraphicContext;
311 ASSERT(!m_filter.contains(object));
312 m_filter.set(object, filterData.release());
317 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
321 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
323 FilterData* filterData = m_filter.get(object);
327 if (object->document().settings()->deferredFiltersEnabled() && (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Built)) {
328 endDeferredFilter(context, filterData);
329 filterData->state = FilterData::Built;
333 switch (filterData->state) {
334 case FilterData::MarkedForRemoval:
335 m_filter.remove(object);
338 case FilterData::CycleDetected:
339 case FilterData::Applying:
340 // We have a cycle if we are already applying the data.
341 // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
342 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
343 // will continue correctly.
344 filterData->state = FilterData::PaintingSource;
347 case FilterData::PaintingSource:
348 if (!filterData->savedContext) {
349 removeClientFromCache(object);
353 context = filterData->savedContext;
354 filterData->savedContext = 0;
357 case FilterData::Built: { } // Empty
360 FilterEffect* lastEffect = filterData->builder->lastEffect();
362 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
363 // This is the real filtering of the object. It just needs to be called on the
364 // initial filtering process. We just take the stored filter result on a
366 if (filterData->state != FilterData::Built)
367 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
369 // Always true if filterData is just built (filterData->state == FilterData::Built).
370 if (!lastEffect->hasResult()) {
371 filterData->state = FilterData::Applying;
373 lastEffect->correctFilterResultIfNeeded();
374 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
376 filterData->state = FilterData::Built;
378 ImageBuffer* resultImage = lastEffect->asImageBuffer();
380 context->drawImageBuffer(resultImage, filterData->filter->mapAbsoluteRectToLocalRect(lastEffect->absolutePaintRect()));
383 filterData->sourceGraphicBuffer.clear();
386 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
388 if (SVGFilterElement* element = toSVGFilterElement(this->element()))
389 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox());
394 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
396 FilterMap::iterator it = m_filter.begin();
397 FilterMap::iterator end = m_filter.end();
398 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
400 for (; it != end; ++it) {
401 FilterData* filterData = it->value.get();
402 if (filterData->state != FilterData::Built)
405 SVGFilterBuilder* builder = filterData->builder.get();
406 FilterEffect* effect = builder->effectByRenderer(object);
409 // Since all effects shares the same attribute value, all
410 // or none of them will be changed.
411 if (!primitve->setFilterEffectAttribute(effect, attribute))
413 builder->clearResultsRecursive(effect);
415 // Repaint the image on the screen.
416 markClientForInvalidation(it->key, RepaintInvalidation);
418 markAllClientLayersForInvalidation();
421 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
423 FilterData* filterData = m_filter.get(object);
424 return filterData ? filterData->drawingRegion : FloatRect();