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"
36 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
42 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
44 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
45 : RenderSVGResourceContainer(node)
49 RenderSVGResourceFilter::~RenderSVGResourceFilter()
54 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const
56 return child->isSVGResourceFilterPrimitive();
59 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
62 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
65 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
69 if (FilterData* filterData = m_filter.get(client)) {
70 if (filterData->savedContext)
71 filterData->state = FilterData::MarkedForRemoval;
73 m_filter.remove(client);
76 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
79 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter)
81 SVGFilterElement* filterElement = toSVGFilterElement(element());
82 FloatRect targetBoundingBox = filter->targetBoundingBox();
84 // Add effects to the builder
85 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter));
86 for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
87 if (!node->isSVGElement())
90 SVGElement* element = toSVGElement(node);
91 if (!element->isFilterEffect() || !element->renderer())
94 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
95 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
97 builder->clearEffects();
100 builder->appendEffectToEffectReferences(effect, effectElement->renderer());
101 effectElement->setStandardAttributes(effect.get());
102 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnitsCurrentValue(), targetBoundingBox));
103 effect->setOperatingColorSpace(
104 effectElement->renderer()->style()->svgStyle()->colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
105 builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect);
107 return builder.release();
110 bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
112 bool matchesFilterSize = true;
113 if (size.width() * scale.width() > kMaxFilterSize) {
114 scale.setWidth(kMaxFilterSize / size.width());
115 matchesFilterSize = false;
117 if (size.height() * scale.height() > kMaxFilterSize) {
118 scale.setHeight(kMaxFilterSize / size.height());
119 matchesFilterSize = false;
122 return matchesFilterSize;
125 static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBuffer, bool accelerated)
127 IntRect paintRect = filter->sourceImageRect();
128 // Don't create empty ImageBuffers.
129 if (paintRect.isEmpty())
132 OwnPtr<ImageBufferSurface> surface;
134 surface = adoptPtr(new AcceleratedImageBufferSurface(paintRect.size()));
135 if (!accelerated || !surface->isValid())
136 surface = adoptPtr(new UnacceleratedImageBufferSurface(paintRect.size()));
137 if (!surface->isValid())
139 OwnPtr<ImageBuffer> image = ImageBuffer::create(surface.release());
141 GraphicsContext* imageContext = image->context();
142 ASSERT(imageContext);
144 imageContext->translate(-paintRect.x(), -paintRect.y());
145 imageContext->concatCTM(filter->absoluteTransform());
146 imageBuffer = image.release();
150 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
154 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
156 clearInvalidationMask();
158 bool deferredFiltersEnabled = object->document().settings()->deferredFiltersEnabled();
159 if (deferredFiltersEnabled) {
160 if (m_objects.contains(object))
161 return false; // We're in a cycle.
162 } else if (m_filter.contains(object)) {
163 FilterData* filterData = m_filter.get(object);
164 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
165 filterData->state = FilterData::CycleDetected;
166 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now.
169 OwnPtr<FilterData> filterData(adoptPtr(new FilterData));
170 FloatRect targetBoundingBox = object->objectBoundingBox();
172 SVGFilterElement* filterElement = toSVGFilterElement(element());
173 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnitsCurrentValue(), targetBoundingBox);
174 if (filterData->boundaries.isEmpty())
177 // Determine absolute transformation matrix for filter.
178 AffineTransform absoluteTransform;
179 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
180 if (!absoluteTransform.isInvertible())
183 // Filters cannot handle a full transformation, only scales in each direction.
184 FloatSize filterScale;
186 // Calculate the scale factor for the filter.
187 // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
188 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
189 // If resolution is specified, scale to match it.
190 filterScale = FloatSize(
191 filterElement->filterResX()->currentValue()->value() / filterData->boundaries.width(),
192 filterElement->filterResY()->currentValue()->value() / filterData->boundaries.height());
194 // Otherwise, use the scale of the absolute transform.
195 filterScale = FloatSize(absoluteTransform.xScale(), absoluteTransform.yScale());
197 // The size of the scaled filter boundaries shouldn't be bigger than kMaxFilterSize.
198 // Intermediate filters are limited by the filter boundaries so they can't be bigger than this.
199 fitsInMaximumImageSize(filterData->boundaries.size(), filterScale);
201 filterData->drawingRegion = object->strokeBoundingBox();
202 filterData->drawingRegion.intersect(filterData->boundaries);
203 FloatRect absoluteDrawingRegion = filterData->drawingRegion;
204 if (!deferredFiltersEnabled)
205 absoluteDrawingRegion.scale(filterScale.width(), filterScale.height());
207 IntRect intDrawingRegion = enclosingIntRect(absoluteDrawingRegion);
209 // Create the SVGFilter object.
210 bool primitiveBoundingBoxMode = filterElement->primitiveUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
211 filterData->shearFreeAbsoluteTransform = AffineTransform();
212 if (!deferredFiltersEnabled)
213 filterData->shearFreeAbsoluteTransform.scale(filterScale.width(), filterScale.height());
214 filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
216 // Create all relevant filter primitives.
217 filterData->builder = buildPrimitives(filterData->filter.get());
218 if (!filterData->builder)
221 FilterEffect* lastEffect = filterData->builder->lastEffect();
225 lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion);
227 if (deferredFiltersEnabled) {
228 SkiaImageFilterBuilder builder(context);
229 FloatRect oldBounds = context->getClipBounds();
230 m_objects.set(object, oldBounds);
231 RefPtr<ImageFilter> imageFilter = builder.build(lastEffect, ColorSpaceDeviceRGB);
232 FloatRect boundaries = enclosingIntRect(filterData->boundaries);
233 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
235 // Get boundaries in device coords.
236 FloatSize size = context->getCTM().mapSize(boundaries.size());
237 // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels.
238 FloatSize filterResScale(
239 filterElement->filterResX()->currentValue()->value() / size.width(),
240 filterElement->filterResY()->currentValue()->value() / size.height());
241 // Scale the CTM so the primitive is drawn to filterRes.
242 context->translate(boundaries.x(), boundaries.y());
243 context->scale(filterResScale);
244 context->translate(-boundaries.x(), -boundaries.y());
245 // Create a resize filter with the inverse scale.
246 imageFilter = builder.buildResize(1 / filterResScale.width(), 1 / filterResScale.height(), imageFilter.get());
247 // Clip the context so that the offscreen created in beginLayer()
248 // is clipped to filterResX by filerResY. Use Replace mode since
249 // this clip may be larger than the parent device.
250 context->clipRectReplace(boundaries);
252 context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get());
256 // If the drawingRegion is empty, we have something like <g filter=".."/>.
257 // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
258 if (filterData->drawingRegion.isEmpty()) {
259 ASSERT(!m_filter.contains(object));
260 filterData->savedContext = context;
261 m_filter.set(object, filterData.release());
265 OwnPtr<ImageBuffer> sourceGraphic;
266 bool isAccelerated = object->document().settings()->acceleratedFiltersEnabled();
267 if (!createImageBuffer(filterData->filter.get(), sourceGraphic, isAccelerated)) {
268 ASSERT(!m_filter.contains(object));
269 filterData->savedContext = context;
270 m_filter.set(object, filterData.release());
274 // Set the rendering mode from the page's settings.
275 filterData->filter->setIsAccelerated(isAccelerated);
277 GraphicsContext* sourceGraphicContext = sourceGraphic->context();
278 ASSERT(sourceGraphicContext);
280 filterData->sourceGraphicBuffer = sourceGraphic.release();
281 filterData->savedContext = context;
283 context = sourceGraphicContext;
285 ASSERT(!m_filter.contains(object));
286 m_filter.set(object, filterData.release());
291 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
295 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
297 if (object->document().settings()->deferredFiltersEnabled()) {
298 SVGFilterElement* filterElement = toSVGFilterElement(element());
299 if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
300 // Restore the clip bounds before endLayer(), so the filtered
301 // image draw is clipped to the original device bounds, not the
302 // clip we set before the beginLayer() above.
303 FloatRect oldBounds = m_objects.get(object);
304 context->clipRectReplace(oldBounds);
310 m_objects.remove(object);
314 FilterData* filterData = m_filter.get(object);
318 switch (filterData->state) {
319 case FilterData::MarkedForRemoval:
320 m_filter.remove(object);
323 case FilterData::CycleDetected:
324 case FilterData::Applying:
325 // We have a cycle if we are already applying the data.
326 // This can occur due to FeImage referencing a source that makes use of the FEImage itself.
327 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack
328 // will continue correctly.
329 filterData->state = FilterData::PaintingSource;
332 case FilterData::PaintingSource:
333 if (!filterData->savedContext) {
334 removeClientFromCache(object);
338 context = filterData->savedContext;
339 filterData->savedContext = 0;
342 case FilterData::Built: { } // Empty
345 FilterEffect* lastEffect = filterData->builder->lastEffect();
347 if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
348 // This is the real filtering of the object. It just needs to be called on the
349 // initial filtering process. We just take the stored filter result on a
351 if (filterData->state != FilterData::Built)
352 filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
354 // Always true if filterData is just built (filterData->state == FilterData::Built).
355 if (!lastEffect->hasResult()) {
356 filterData->state = FilterData::Applying;
358 lastEffect->correctFilterResultIfNeeded();
359 lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
361 filterData->state = FilterData::Built;
363 ImageBuffer* resultImage = lastEffect->asImageBuffer();
365 context->drawImageBuffer(resultImage, filterData->filter->mapAbsoluteRectToLocalRect(lastEffect->absolutePaintRect()));
368 filterData->sourceGraphicBuffer.clear();
371 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object)
373 if (SVGFilterElement* element = toSVGFilterElement(this->element()))
374 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnitsCurrentValue(), object->objectBoundingBox());
379 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
381 if (object->document().settings()->deferredFiltersEnabled()) {
382 markAllClientsForInvalidation(RepaintInvalidation);
383 markAllClientLayersForInvalidation();
387 FilterMap::iterator it = m_filter.begin();
388 FilterMap::iterator end = m_filter.end();
389 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
391 for (; it != end; ++it) {
392 FilterData* filterData = it->value.get();
393 if (filterData->state != FilterData::Built)
396 SVGFilterBuilder* builder = filterData->builder.get();
397 FilterEffect* effect = builder->effectByRenderer(object);
400 // Since all effects shares the same attribute value, all
401 // or none of them will be changed.
402 if (!primitve->setFilterEffectAttribute(effect, attribute))
404 builder->clearResultsRecursive(effect);
406 // Repaint the image on the screen.
407 markClientForInvalidation(it->key, RepaintInvalidation);
409 markAllClientLayersForInvalidation();
412 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
414 FilterData* filterData = m_filter.get(object);
415 return filterData ? filterData->drawingRegion : FloatRect();