Skip to content

Commit

Permalink
[Filters] GraphicsContext filters have to apply clipping to the targe…
Browse files Browse the repository at this point in the history
…t context before drawing

https://bugs.webkit.org/show_bug.cgi?id=266383
rdar://118522654

Reviewed by Simon Fraser.

Before applying the filter style and calling `beginTransparencyLayer()`, the
clipping rectangle of the target element has to be applied to the destination
GraphicsContext. Otherwise the hidden overflow drawing will appear once the
`endTransparencyLayer()` is called.

RenderLayer::paintLayerContents() needs to calculate the clipping rectangle
before calling setupFilters() so it can be passed to FilterStyleTargetSwitcher.

beginClipAndDrawSourceImage() and endClipAndDrawSourceImage() will be called
when applying CSSFilter. beginDrawSourceImage() and endDrawSourceImage() will
be called when applying the top-level SVGFilter since we clip to the bounding
rectangle of the root element.

* LayoutTests/css3/filters/drop-shadow-filter-overflow-expected.html: Added.
* LayoutTests/css3/filters/drop-shadow-filter-overflow.html: Added.
* Source/WebCore/platform/graphics/filters/FilterImageTargetSwitcher.cpp:
(WebCore::FilterImageTargetSwitcher::beginClipAndDrawSourceImage):
* Source/WebCore/platform/graphics/filters/FilterImageTargetSwitcher.h:
* Source/WebCore/platform/graphics/filters/FilterStyleTargetSwitcher.cpp:
(WebCore::FilterStyleTargetSwitcher::beginClipAndDrawSourceImage):
* Source/WebCore/platform/graphics/filters/FilterStyleTargetSwitcher.h:
* Source/WebCore/platform/graphics/filters/FilterTargetSwitcher.h:
(WebCore::FilterTargetSwitcher::beginClipAndDrawSourceImage): Deleted.
(WebCore::FilterTargetSwitcher::endClipAndDrawSourceImage): Deleted.
(WebCore::FilterTargetSwitcher::beginDrawSourceImage): Deleted.
(WebCore::FilterTargetSwitcher::endDrawSourceImage): Deleted.
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::setupFilters):
(WebCore::RenderLayer::applyFilters):
(WebCore::RenderLayer::paintLayerContents):
* Source/WebCore/rendering/RenderLayer.h:
* Source/WebCore/rendering/RenderLayerFilters.cpp:
(WebCore::RenderLayerFilters::beginFilterEffect):
* Source/WebCore/rendering/RenderLayerFilters.h:

Canonical link: https://commits.webkit.org/272078@main
  • Loading branch information
shallawa authored and Said Abou-Hallawa committed Dec 14, 2023
1 parent c553c8e commit ddf4bec
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 30 deletions.
24 changes: 24 additions & 0 deletions LayoutTests/css3/filters/drop-shadow-filter-overflow-expected.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<style>
.container {
position: absolute;
left: 10px;
top: 10px;
height: 200px;
width: 200px;
overflow: hidden;
border: 1px green solid;
}
.box {
position: absolute;
left: 100px;
top: 150px;
width: 100px;
height: 100px;
background: green;
}
</style>
<body>
<div class="container">
<div class="box"></div>
</div>
</body>
25 changes: 25 additions & 0 deletions LayoutTests/css3/filters/drop-shadow-filter-overflow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<style>
.container {
position: absolute;
left: 10px;
top: 10px;
height: 200px;
width: 200px;
overflow: hidden;
border: 1px green solid;
}
.box {
position: absolute;
left: 100px;
top: 150px;
width: 100px;
height: 100px;
background: green;
filter: drop-shadow(black 10px 10px 0px);
}
</style>
<body>
<div class="container">
<div class="box"></div>
</div>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ GraphicsContext* FilterImageTargetSwitcher::drawingContext(GraphicsContext& cont
return m_sourceImage ? &m_sourceImage->context() : &context;
}

void FilterImageTargetSwitcher::beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect)
void FilterImageTargetSwitcher::beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect, const FloatRect&)
{
if (auto* context = drawingContext(destinationContext)) {
context->save();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ class FilterImageTargetSwitcher final : public FilterTargetSwitcher {

bool hasSourceImage() const override { return m_sourceImage; }

void beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect) override;
void beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect, const FloatRect& clipRect) override;
void endClipAndDrawSourceImage(GraphicsContext& destinationContext) override;

void beginDrawSourceImage(GraphicsContext&) override { }
void endDrawSourceImage(GraphicsContext& destinationContext) override;

RefPtr<ImageBuffer> m_sourceImage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ FilterStyleTargetSwitcher::FilterStyleTargetSwitcher(Filter& filter, const Float
{
}

void FilterStyleTargetSwitcher::beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect&, const FloatRect& clipRect)
{
for (auto& filterStyle : m_filterStyles) {
destinationContext.save();
destinationContext.clip(intersection(filterStyle.imageRect, clipRect));
destinationContext.setStyle(filterStyle.style);
destinationContext.beginTransparencyLayer(1);
}
}

void FilterStyleTargetSwitcher::beginDrawSourceImage(GraphicsContext& destinationContext)
{
for (auto& filterStyle : m_filterStyles) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class FilterStyleTargetSwitcher : public FilterTargetSwitcher {
FilterStyleTargetSwitcher(Filter&, const FloatRect &sourceImageRect);

private:
void beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect, const FloatRect& clipRect) override;
void endClipAndDrawSourceImage(GraphicsContext& destinationContext) override { endDrawSourceImage(destinationContext); }

void beginDrawSourceImage(GraphicsContext& destinationContext) override;
void endDrawSourceImage(GraphicsContext& destinationContext) override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ class FilterTargetSwitcher {

virtual bool hasSourceImage() const { return false; }

virtual void beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect&) { beginDrawSourceImage(destinationContext); }
virtual void endClipAndDrawSourceImage(GraphicsContext& destinationContext) { endDrawSourceImage(destinationContext); }
virtual void beginClipAndDrawSourceImage(GraphicsContext& destinationContext, const FloatRect& repaintRect, const FloatRect& clipRect) = 0;
virtual void endClipAndDrawSourceImage(GraphicsContext& destinationContext) = 0;

virtual void beginDrawSourceImage(GraphicsContext&) { }
virtual void endDrawSourceImage(GraphicsContext&) { }
virtual void beginDrawSourceImage(GraphicsContext& destinationContext) = 0;
virtual void endDrawSourceImage(GraphicsContext& destinationContext) = 0;

protected:
FilterTargetSwitcher(Filter&);
Expand Down
41 changes: 22 additions & 19 deletions Source/WebCore/rendering/RenderLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3209,7 +3209,7 @@ RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, Op
return nullptr;
}

GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags, const LayoutSize& offsetFromRoot)
GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags, const LayoutSize& offsetFromRoot, const ClipRect& backgroundRect)
{
auto* paintingFilters = filtersForPainting(destinationContext, paintFlags);
if (!paintingFilters)
Expand All @@ -3220,7 +3220,7 @@ GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext,

auto rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, { });

GraphicsContext* filterContext = paintingFilters->beginFilterEffect(renderer(), destinationContext, enclosingIntRect(rootRelativeBounds), enclosingIntRect(paintingInfo.paintDirtyRect), enclosingIntRect(filterRepaintRect));
GraphicsContext* filterContext = paintingFilters->beginFilterEffect(renderer(), destinationContext, enclosingIntRect(rootRelativeBounds), enclosingIntRect(paintingInfo.paintDirtyRect), enclosingIntRect(filterRepaintRect), backgroundRect.rect());
if (!filterContext)
return nullptr;

Expand All @@ -3236,15 +3236,12 @@ GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext,
return filterContext;
}

void RenderLayer::applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo& paintingInfo, OptionSet<PaintBehavior> behavior, const LayerFragments& layerFragments)
void RenderLayer::applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo& paintingInfo, OptionSet<PaintBehavior> behavior, const ClipRect& backgroundRect)
{
GraphicsContextStateSaver stateSaver(originalContext, false);
bool needsClipping = m_filters->hasSourceImage();

if (needsClipping) {
// FIXME: Handle more than one fragment.
ClipRect backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect;

RegionContextStateSaver regionContextStateSaver(paintingInfo.regionContext);

clipToRect(originalContext, stateSaver, regionContextStateSaver, paintingInfo, behavior, backgroundRect);
Expand Down Expand Up @@ -3382,8 +3379,24 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
}();

{ // Scope for filter-related state changes.
ClipRect backgroundRect;

if (filtersForPainting(context, paintFlags)) {
// When we called collectFragments() last time, paintDirtyRect was reset to represent the filter bounds.
// Now we need to compute the backgroundRect uncontaminated by filters, in order to clip the filtered result.
// Note that we also use paintingInfo here, not localPaintingInfo which filters also contaminated.
LayerFragments layerFragments;
auto clipRectOptions = isPaintingOverflowContents ? clipRectOptionsForPaintingOverflowControls : clipRectDefaultOptions;
collectFragments(layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerFlag::TemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, clipRectOptions, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, paintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);

// FIXME: Handle more than one fragment.
backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect;
}

LayerPaintingInfo localPaintingInfo(paintingInfo);
GraphicsContext* filterContext = setupFilters(context, localPaintingInfo, paintFlags, columnAwareOffsetFromRoot);
GraphicsContext* filterContext = setupFilters(context, localPaintingInfo, paintFlags, columnAwareOffsetFromRoot, backgroundRect);
if (filterContext && haveTransparency) {
// If we have a filter and transparency, we have to eagerly start a transparency layer here, rather than risk a child layer lazily starts one with the wrong context.
beginTransparencyLayers(context, localPaintingInfo, paintingInfo.paintDirtyRect);
Expand Down Expand Up @@ -3461,18 +3474,8 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
paintOverflowControlsForFragments(layerFragments, currentContext, localPaintingInfo);
}

if (filterContext) {
// When we called collectFragments() last time, paintDirtyRect was reset to represent the filter bounds.
// Now we need to compute the backgroundRect uncontaminated by filters, in order to clip the filtered result.
// Note that we also use paintingInfo here, not localPaintingInfo which filters also contaminated.
LayerFragments layerFragments;
auto clipRectOptions = isPaintingOverflowContents ? clipRectOptionsForPaintingOverflowControls : clipRectDefaultOptions;
collectFragments(layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerFlag::TemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, clipRectOptions, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, paintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);

applyFilters(context, paintingInfo, paintBehavior, layerFragments);
}
if (filterContext)
applyFilters(context, paintingInfo, paintBehavior, backgroundRect);
}

if (shouldPaintContent && !(selectionOnly || selectionAndBackgroundsOnly)) {
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/rendering/RenderLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1106,8 +1106,8 @@ class RenderLayer : public CanMakeSingleThreadWeakPtr<RenderLayer>, public CanMa
void clearLayerScrollableArea();

RenderLayerFilters* filtersForPainting(GraphicsContext&, OptionSet<PaintLayerFlag>) const;
GraphicsContext* setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo&, OptionSet<PaintLayerFlag>, const LayoutSize& offsetFromRoot);
void applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo&, OptionSet<PaintBehavior>, const LayerFragments&);
GraphicsContext* setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo&, OptionSet<PaintLayerFlag>, const LayoutSize& offsetFromRoot, const ClipRect& backgroundRect);
void applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo&, OptionSet<PaintBehavior>, const ClipRect& backgroundRect);

void paintLayer(GraphicsContext&, const LayerPaintingInfo&, OptionSet<PaintLayerFlag>);
void paintLayerWithEffects(GraphicsContext&, const LayerPaintingInfo&, OptionSet<PaintLayerFlag>);
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/rendering/RenderLayerFilters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ IntOutsets RenderLayerFilters::calculateOutsets(RenderElement& renderer, const F
return CSSFilter::calculateOutsets(renderer, operations, targetBoundingBox);
}

GraphicsContext* RenderLayerFilters::beginFilterEffect(RenderElement& renderer, GraphicsContext& context, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
GraphicsContext* RenderLayerFilters::beginFilterEffect(RenderElement& renderer, GraphicsContext& context, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect, const LayoutRect& clipRect)
{
auto expandedDirtyRect = dirtyRect;
auto targetBoundingBox = intersection(filterBoxRect, dirtyRect);
Expand Down Expand Up @@ -195,7 +195,7 @@ GraphicsContext* RenderLayerFilters::beginFilterEffect(RenderElement& renderer,
if (!m_targetSwitcher)
return nullptr;

m_targetSwitcher->beginClipAndDrawSourceImage(context, m_repaintRect);
m_targetSwitcher->beginClipAndDrawSourceImage(context, m_repaintRect, clipRect);

return m_targetSwitcher->drawingContext(context);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/rendering/RenderLayerFilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class RenderLayerFilters final : private CachedSVGDocumentClient {
// Per render
LayoutRect repaintRect() const { return m_repaintRect; }

GraphicsContext* beginFilterEffect(RenderElement&, GraphicsContext&, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect);
GraphicsContext* beginFilterEffect(RenderElement&, GraphicsContext&, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect, const LayoutRect& clipRect);
void applyFilterEffect(GraphicsContext& destinationContext);

private:
Expand Down

0 comments on commit ddf4bec

Please sign in to comment.