RenderSVGResourceFilter.cpp [plain text]
#include "config.h"
#if ENABLE(SVG) && ENABLE(FILTERS)
#include "RenderSVGResourceFilter.h"
#include "AffineTransform.h"
#include "FloatPoint.h"
#include "FloatRect.h"
#include "GraphicsContext.h"
#include "Image.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "IntRect.h"
#include "RenderSVGResource.h"
#include "SVGElement.h"
#include "SVGFilter.h"
#include "SVGFilterElement.h"
#include "SVGFilterPrimitiveStandardAttributes.h"
#include "SVGStyledElement.h"
#include "SVGUnitTypes.h"
#include <wtf/Vector.h>
#include <wtf/UnusedParam.h>
static const float kMaxFilterSize = 5000.0f;
using namespace std;
namespace WebCore {
RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
: RenderSVGResourceContainer(node)
, m_savedContext(0)
, m_sourceGraphicBuffer(0)
{
}
RenderSVGResourceFilter::~RenderSVGResourceFilter()
{
deleteAllValues(m_filter);
m_filter.clear();
}
void RenderSVGResourceFilter::invalidateClients()
{
HashMap<RenderObject*, FilterData*>::const_iterator end = m_filter.end();
for (HashMap<RenderObject*, FilterData*>::const_iterator it = m_filter.begin(); it != end; ++it) {
RenderObject* renderer = it->first;
renderer->setNeedsBoundariesUpdate();
renderer->setNeedsLayout(true);
}
deleteAllValues(m_filter);
m_filter.clear();
}
void RenderSVGResourceFilter::invalidateClient(RenderObject* object)
{
ASSERT(object);
if (!m_filter.contains(object))
return;
delete m_filter.take(object);
markForLayoutAndResourceInvalidation(object);
}
PassOwnPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives()
{
SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
OwnPtr<SVGFilterBuilder> builder(new SVGFilterBuilder);
builder->clearEffects();
for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
if (!node->isSVGElement())
continue;
SVGElement* element = static_cast<SVGElement*>(node);
if (!element->isFilterEffect())
continue;
SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
RefPtr<FilterEffect> effect = effectElement->build(builder.get());
if (!effect) {
builder->clearEffects();
return 0;
}
effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get());
builder->add(effectElement->result(), effect);
}
return builder.release();
}
bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
{
bool matchesFilterSize = true;
if (size.width() > kMaxFilterSize) {
scale.setWidth(scale.width() * kMaxFilterSize / size.width());
matchesFilterSize = false;
}
if (size.height() > kMaxFilterSize) {
scale.setHeight(scale.height() * kMaxFilterSize / size.height());
matchesFilterSize = false;
}
return matchesFilterSize;
}
bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
{
ASSERT(object);
ASSERT(context);
#ifndef NDEBUG
ASSERT(resourceMode == ApplyToDefaultMode);
#else
UNUSED_PARAM(resourceMode);
#endif
if (m_filter.contains(object)) {
FilterData* filterData = m_filter.get(object);
if (filterData->builded)
return false;
delete m_filter.take(object); }
OwnPtr<FilterData> filterData(new FilterData);
filterData->builder = buildPrimitives();
if (!filterData->builder)
return false;
FloatRect paintRect = object->strokeBoundingBox();
SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
filterData->boundaries = filterElement->filterBoundingBox(object->objectBoundingBox());
if (filterData->boundaries.isEmpty())
return false;
FloatSize scale(1.0f, 1.0f);
if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
scale.setWidth(filterElement->filterResX() / filterData->boundaries.width());
scale.setHeight(filterElement->filterResY() / filterData->boundaries.height());
}
if (scale.isEmpty())
return false;
FloatRect clippedSourceRect = paintRect;
clippedSourceRect.intersect(filterData->boundaries);
FloatRect tempSourceRect = clippedSourceRect;
tempSourceRect.scale(scale.width(), scale.height());
fitsInMaximumImageSize(tempSourceRect.size(), scale);
bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
filterData->filter = SVGFilter::create(paintRect, filterData->boundaries, primitiveBoundingBoxMode);
filterData->filter->setFilterResolution(scale);
FilterEffect* lastEffect = filterData->builder->lastEffect();
if (!lastEffect)
return false;
lastEffect->calculateEffectRect(filterData->filter.get());
if (!fitsInMaximumImageSize(filterData->filter->maxImageSize(), scale)) {
filterData->filter->setFilterResolution(scale);
lastEffect->calculateEffectRect(filterData->filter.get());
}
clippedSourceRect.scale(scale.width(), scale.height());
IntRect bufferRect = enclosingIntRect(clippedSourceRect);
OwnPtr<ImageBuffer> sourceGraphic(ImageBuffer::create(bufferRect.size(), LinearRGB));
if (!sourceGraphic.get())
return false;
GraphicsContext* sourceGraphicContext = sourceGraphic->context();
sourceGraphicContext->translate(-clippedSourceRect.x(), -clippedSourceRect.y());
sourceGraphicContext->scale(scale);
sourceGraphicContext->clearRect(FloatRect(FloatPoint(), paintRect.size()));
m_sourceGraphicBuffer.set(sourceGraphic.release());
m_savedContext = context;
context = sourceGraphicContext;
m_filter.set(object, filterData.release());
return true;
}
void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode)
{
ASSERT(object);
ASSERT(context);
#ifndef NDEBUG
ASSERT(resourceMode == ApplyToDefaultMode);
#else
UNUSED_PARAM(resourceMode);
#endif
if (!m_filter.contains(object))
return;
FilterData* filterData = m_filter.get(object);
if (!filterData->builded) {
if (!m_savedContext) {
invalidateClient(object);
return;
}
context = m_savedContext;
m_savedContext = 0;
#if !PLATFORM(CG)
m_sourceGraphicBuffer->transformColorSpace(DeviceRGB, LinearRGB);
#endif
}
FilterEffect* lastEffect = filterData->builder->lastEffect();
if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->subRegion().isEmpty()) {
if (!filterData->builded) {
filterData->filter->setSourceImage(m_sourceGraphicBuffer.release());
lastEffect->apply(filterData->filter.get());
filterData->builded = true;
}
ImageBuffer* resultImage = lastEffect->resultImage();
if (resultImage) {
#if !PLATFORM(CG)
resultImage->transformColorSpace(LinearRGB, DeviceRGB);
#endif
context->drawImage(resultImage->image(), object->style()->colorSpace(), lastEffect->subRegion());
}
}
m_sourceGraphicBuffer.clear();
}
FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
{
if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
return element->filterBoundingBox(object->objectBoundingBox());
return FloatRect();
}
}
#endif