RenderSVGResourceFilter.cpp [plain text]
#include "config.h"
#include "RenderSVGResourceFilter.h"
#include "ElementChildIterator.h"
#include "FilterEffect.h"
#include "FloatPoint.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "Image.h"
#include "ImageData.h"
#include "IntRect.h"
#include "Page.h"
#include "RenderSVGResourceFilterPrimitive.h"
#include "RenderView.h"
#include "SVGFilterPrimitiveStandardAttributes.h"
#include "SVGNames.h"
#include "SVGRenderingContext.h"
#include "Settings.h"
#include "SourceGraphic.h"
namespace WebCore {
RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement& element, Ref<RenderStyle>&& style)
: RenderSVGResourceContainer(element, WTF::move(style))
{
}
RenderSVGResourceFilter::~RenderSVGResourceFilter()
{
}
void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
{
m_filter.clear();
markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
}
void RenderSVGResourceFilter::removeClientFromCache(RenderElement& client, bool markForInvalidation)
{
if (FilterData* filterData = m_filter.get(&client)) {
if (filterData->savedContext)
filterData->state = FilterData::MarkedForRemoval;
else
m_filter.remove(&client);
}
markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
}
std::unique_ptr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter& filter) const
{
static const unsigned maxCountChildNodes = 200;
if (filterElement().countChildNodes() > maxCountChildNodes)
return nullptr;
FloatRect targetBoundingBox = filter.targetBoundingBox();
auto builder = std::make_unique<SVGFilterBuilder>(SourceGraphic::create(filter));
for (auto& element : childrenOfType<SVGFilterPrimitiveStandardAttributes>(filterElement())) {
RefPtr<FilterEffect> effect = element.build(builder.get(), filter);
if (!effect) {
builder->clearEffects();
return nullptr;
}
builder->appendEffectToEffectReferences(effect, element.renderer());
element.setStandardAttributes(effect.get());
effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(&element, filterElement().primitiveUnits(), targetBoundingBox));
effect->setOperatingColorSpace(element.renderer()->style().svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB);
builder->add(element.result(), effect.release());
}
return builder;
}
bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode)
{
ASSERT(context);
ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
if (m_filter.contains(&renderer)) {
FilterData* filterData = m_filter.get(&renderer);
if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying)
filterData->state = FilterData::CycleDetected;
return false; }
auto filterData = std::make_unique<FilterData>();
FloatRect targetBoundingBox = renderer.objectBoundingBox();
filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox);
if (filterData->boundaries.isEmpty())
return false;
AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer);
if (!absoluteTransform.isInvertible())
return false;
filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0);
FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
filterData->drawingRegion = renderer.strokeBoundingBox();
filterData->drawingRegion.intersect(filterData->boundaries);
FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion);
bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
filterData->builder = buildPrimitives(*filterData->filter);
if (!filterData->builder)
return false;
FloatSize scale(1, 1);
if (filterElement().hasAttribute(SVGNames::filterResAttr)) {
scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width());
scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height());
}
if (scale.isEmpty())
return false;
FloatRect tempSourceRect = absoluteDrawingRegion;
ImageBuffer::sizeNeedsClamping(tempSourceRect.size(), scale);
tempSourceRect.scale(scale.width(), scale.height());
filterData->filter->setFilterResolution(scale);
static const unsigned maxTotalOfEffectInputs = 100;
FilterEffect* lastEffect = filterData->builder->lastEffect();
if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs)
return false;
RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
FloatRect subRegion = lastEffect->maxEffectRect();
if (ImageBuffer::sizeNeedsClamping(subRegion.size(), scale)) {
filterData->filter->setFilterResolution(scale);
RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect);
}
if (filterData->drawingRegion.isEmpty()) {
ASSERT(!m_filter.contains(&renderer));
filterData->savedContext = context;
m_filter.set(&renderer, WTF::move(filterData));
return false;
}
AffineTransform effectiveTransform;
effectiveTransform.scale(scale.width(), scale.height());
effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform);
RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated;
auto sourceGraphic = SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, ColorSpaceLinearRGB, renderingMode);
if (!sourceGraphic) {
ASSERT(!m_filter.contains(&renderer));
filterData->savedContext = context;
m_filter.set(&renderer, WTF::move(filterData));
return false;
}
filterData->filter->setRenderingMode(renderingMode);
GraphicsContext* sourceGraphicContext = sourceGraphic->context();
ASSERT(sourceGraphicContext);
filterData->sourceGraphicBuffer = WTF::move(sourceGraphic);
filterData->savedContext = context;
context = sourceGraphicContext;
ASSERT(!m_filter.contains(&renderer));
m_filter.set(&renderer, WTF::move(filterData));
return true;
}
void RenderSVGResourceFilter::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path*, const RenderSVGShape*)
{
ASSERT(context);
ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
FilterData* filterData = m_filter.get(&renderer);
if (!filterData)
return;
switch (filterData->state) {
case FilterData::MarkedForRemoval:
m_filter.remove(&renderer);
return;
case FilterData::CycleDetected:
case FilterData::Applying:
filterData->state = FilterData::PaintingSource;
return;
case FilterData::PaintingSource:
if (!filterData->savedContext) {
removeClientFromCache(renderer);
return;
}
context = filterData->savedContext;
filterData->savedContext = 0;
break;
case FilterData::Built: { } }
FilterEffect* lastEffect = filterData->builder->lastEffect();
if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
if (filterData->state != FilterData::Built)
filterData->filter->setSourceImage(WTF::move(filterData->sourceGraphicBuffer));
if (!lastEffect->hasResult()) {
filterData->state = FilterData::Applying;
lastEffect->applyAll();
lastEffect->correctFilterResultIfNeeded();
lastEffect->transformResultColorSpace(ColorSpaceDeviceRGB);
}
filterData->state = FilterData::Built;
ImageBuffer* resultImage = lastEffect->asImageBuffer();
if (resultImage) {
context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
context->drawImageBuffer(resultImage, renderer.style().colorSpace(), lastEffect->absolutePaintRect());
context->scale(filterData->filter->filterResolution());
context->concatCTM(filterData->shearFreeAbsoluteTransform);
}
}
filterData->sourceGraphicBuffer.reset();
}
FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject& object)
{
return SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), object.objectBoundingBox());
}
void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute)
{
SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node());
for (const auto& objectFilterDataPair : m_filter) {
const auto& filterData = objectFilterDataPair.value;
if (filterData->state != FilterData::Built)
continue;
SVGFilterBuilder* builder = filterData->builder.get();
FilterEffect* effect = builder->effectByRenderer(object);
if (!effect)
continue;
if (!primitve->setFilterEffectAttribute(effect, attribute))
return;
builder->clearResultsRecursive(effect);
markClientForInvalidation(*objectFilterDataPair.key, RepaintInvalidation);
}
markAllClientLayersForInvalidation();
}
FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const
{
FilterData* filterData = m_filter.get(object);
return filterData ? filterData->drawingRegion : FloatRect();
}
}