RenderLayerFilters.cpp [plain text]
#include "config.h"
#include "RenderLayerFilters.h"
#include "CSSFilter.h"
#include "CachedSVGDocument.h"
#include "CachedSVGDocumentReference.h"
#include "Logging.h"
#include "RenderSVGResourceFilter.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
RenderLayerFilters::RenderLayerFilters(RenderLayer& layer)
: m_layer(layer)
{
}
RenderLayerFilters::~RenderLayerFilters()
{
removeReferenceFilterClients();
}
void RenderLayerFilters::setFilter(RefPtr<CSSFilter>&& filter)
{
m_filter = WTFMove(filter);
}
bool RenderLayerFilters::hasFilterThatMovesPixels() const
{
return m_filter && m_filter->hasFilterThatMovesPixels();
}
bool RenderLayerFilters::hasFilterThatShouldBeRestrictedBySecurityOrigin() const
{
return m_filter && m_filter->hasFilterThatShouldBeRestrictedBySecurityOrigin();
}
void RenderLayerFilters::notifyFinished(CachedResource&)
{
if (auto* enclosingElement = m_layer.enclosingElement())
enclosingElement->invalidateStyleAndLayerComposition();
m_layer.renderer().repaint();
}
void RenderLayerFilters::updateReferenceFilterClients(const FilterOperations& operations)
{
removeReferenceFilterClients();
for (auto& operation : operations.operations()) {
if (!is<ReferenceFilterOperation>(*operation))
continue;
auto& referenceOperation = downcast<ReferenceFilterOperation>(*operation);
auto* documentReference = referenceOperation.cachedSVGDocumentReference();
if (auto* cachedSVGDocument = documentReference ? documentReference->document() : nullptr) {
cachedSVGDocument->addClient(*this);
m_externalSVGReferences.append(cachedSVGDocument);
} else {
auto* filterElement = m_layer.renderer().document().getElementById(referenceOperation.fragment());
if (!filterElement)
continue;
auto* renderer = filterElement->renderer();
if (!is<RenderSVGResourceFilter>(renderer))
continue;
downcast<RenderSVGResourceFilter>(*renderer).addClientRenderLayer(&m_layer);
m_internalSVGReferences.append(filterElement);
}
}
}
void RenderLayerFilters::removeReferenceFilterClients()
{
for (auto& resourceHandle : m_externalSVGReferences)
resourceHandle->removeClient(*this);
m_externalSVGReferences.clear();
for (auto& filterElement : m_internalSVGReferences) {
if (auto* renderer = filterElement->renderer())
downcast<RenderSVGResourceContainer>(*renderer).removeClientRenderLayer(&m_layer);
}
m_internalSVGReferences.clear();
}
void RenderLayerFilters::buildFilter(RenderElement& renderer, float scaleFactor, RenderingMode renderingMode)
{
if (!m_filter) {
m_filter = CSSFilter::create();
m_filter->setFilterScale(scaleFactor);
m_filter->setRenderingMode(renderingMode);
} else if (m_filter->filterScale() != scaleFactor) {
m_filter->setFilterScale(scaleFactor);
m_filter->clearIntermediateResults();
}
if (!m_filter->build(renderer, renderer.style().filter(), FilterConsumer::FilterProperty))
m_filter = nullptr;
}
GraphicsContext* RenderLayerFilters::beginFilterEffect(GraphicsContext& destinationContext, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
{
if (!m_filter)
return nullptr;
auto& filter = *m_filter;
auto filterSourceRect = filter.computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
if (filterSourceRect.isEmpty())
return nullptr;
bool hasUpdatedBackingStore = filter.updateBackingStoreRect(filterSourceRect);
if (!filter.hasFilterThatMovesPixels())
m_repaintRect = dirtyRect;
else {
if (hasUpdatedBackingStore)
m_repaintRect = filterSourceRect;
else {
m_repaintRect = dirtyRect;
m_repaintRect.unite(layerRepaintRect);
m_repaintRect.intersect(filterSourceRect);
}
}
m_paintOffset = filterSourceRect.location();
resetDirtySourceRect();
filter.determineFilterPrimitiveSubregion();
filter.allocateBackingStoreIfNeeded(destinationContext);
auto* sourceGraphicsContext = filter.inputContext();
if (!sourceGraphicsContext || filter.filterRegion().isEmpty() || ImageBuffer::sizeNeedsClamping(filter.filterRegion().size()))
return nullptr;
sourceGraphicsContext->save();
sourceGraphicsContext->translate(-m_paintOffset);
sourceGraphicsContext->clearRect(m_repaintRect);
sourceGraphicsContext->clip(m_repaintRect);
return sourceGraphicsContext;
}
void RenderLayerFilters::applyFilterEffect(GraphicsContext& destinationContext)
{
ASSERT(m_filter->inputContext());
LOG_WITH_STREAM(Filters, stream << "\nRenderLayerFilters " << this << " applyFilterEffect");
auto& filter = *m_filter;
filter.inputContext()->restore();
filter.apply();
LayoutRect destRect = filter.outputRect();
destRect.move(m_paintOffset.x(), m_paintOffset.y());
if (auto* outputBuffer = filter.output())
destinationContext.drawImageBuffer(*outputBuffer, snapRectToDevicePixels(destRect, m_layer.renderer().document().deviceScaleFactor()));
filter.clearIntermediateResults();
LOG_WITH_STREAM(Filters, stream << "RenderLayerFilters " << this << " applyFilterEffect done\n");
}
}