Skip to content
Permalink
Browse files
TextureMapper: mask should be applied after filter is applied
https://bugs.webkit.org/show_bug.cgi?id=241772

Reviewed by Don Olmstead.

If an element has both a mask-image and filters, the mask should be
applied after the filters.

BitmapTextureGL::applyFilters didn't actually apply the last filter.
It stored the last filter information in it, and applied the last
filter when blitting onto the target. If the element has a mask,
applyFilters should apply all filters before applying the mask.

* LayoutTests/compositing/masks/mask-and-drop-shadow-expected.html: Added.
* LayoutTests/compositing/masks/mask-and-drop-shadow.html: Added.
* Source/WebCore/platform/graphics/texmap/BitmapTexture.h:
(WebCore::BitmapTexture::applyFilters):
* Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp:
(WebCore::BitmapTextureGL::applyFilters):
* Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h:
* Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp:
(WebCore::TextureMapperLayer::computeOverlapRegions):
(WebCore::TextureMapperLayer::paintIntoSurface):

Canonical link: https://commits.webkit.org/251722@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295717 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
fujii committed Jun 22, 2022
1 parent a2fdaa5 commit 9fed47ece21b27fd019b511fe5756cdebb9a6f60
Showing 6 changed files with 48 additions and 18 deletions.
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<style>
div {
width: 200px;
height: 200px;
background: green;
will-change: transform;
position: absolute;
-webkit-mask-image: linear-gradient(45deg, black, transparent);
}
</style>

You should see no red.

<div></div>
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<style>
div {
width: 200px;
height: 200px;
background: green;
filter: drop-shadow(50px 50px 0px red);
will-change: transform;
position: absolute;
-webkit-mask-image: linear-gradient(45deg, black, transparent);
}
</style>

You should see no red.

<div></div>
@@ -78,7 +78,7 @@ class BitmapTexture : public RefCounted<BitmapTexture> {
inline int numberOfBytes() const { return size().width() * size().height() * bpp() >> 3; }
inline bool isOpaque() const { return !(m_flags & SupportsAlpha); }

virtual RefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) { return this; }
virtual RefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&, bool) { return this; }

protected:
IntSize m_contentSize;
@@ -202,7 +202,7 @@ static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type)
}
}

RefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper, const FilterOperations& filters)
RefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper, const FilterOperations& filters, bool defersLastFilter)
{
if (filters.isEmpty())
return this;
@@ -222,17 +222,14 @@ RefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper
int numPasses = getPassesRequiredForFilter(filter->type());
for (int j = 0; j < numPasses; ++j) {
bool last = (i == filters.size() - 1) && (j == numPasses - 1);
if (!last) {
if (!intermediateSurface)
intermediateSurface = texmapGL.acquireTextureFromPool(contentSize(), BitmapTexture::SupportsAlpha);
texmapGL.bindSurface(intermediateSurface.get());
}

if (last) {
if (defersLastFilter && last) {
toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter.copyRef(), j, spareSurface.copyRef());
break;
}

if (!intermediateSurface)
intermediateSurface = texmapGL.acquireTextureFromPool(contentSize(), BitmapTexture::SupportsAlpha);
texmapGL.bindSurface(intermediateSurface.get());
texmapGL.drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j);
if (!j && filter->type() == FilterOperation::DROP_SHADOW) {
spareSurface = resultSurface;
@@ -60,7 +60,7 @@ class BitmapTextureGL : public BitmapTexture {
void updateContents(const void*, const IntRect& target, const IntPoint& sourceOffset, int bytesPerLine) override;
bool isBackedByOpenGL() const override { return true; }

RefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&) override;
RefPtr<BitmapTexture> applyFilters(TextureMapper&, const FilterOperations&, bool defersLastFilter) override;
struct FilterInfo {
RefPtr<FilterOperation> filter;
unsigned pass;
@@ -343,7 +343,7 @@ void TextureMapperLayer::computeOverlapRegions(ComputeOverlapRegionData& data, c
else if (m_contentsLayer || m_state.solidColor.isVisible())
localBoundingRect = m_state.contentsRect;

if (m_currentFilters.hasOutsets() && !m_state.backdropLayer) {
if (m_currentFilters.hasOutsets() && !m_state.backdropLayer && !m_state.masksToBounds && !m_state.maskLayer) {
auto outsets = m_currentFilters.outsets();
localBoundingRect.move(-outsets.left(), -outsets.top());
localBoundingRect.expand(outsets.left() + outsets.right(), outsets.top() + outsets.bottom());
@@ -485,14 +485,16 @@ void TextureMapperLayer::paintIntoSurface(TextureMapperPaintOptions& options)
rootLayer().paintSelfAndChildren(options);
} else
paintSelfAndChildren(options);
if (options.replicaLayer == this) {
if (m_state.replicaLayer->m_state.maskLayer)
m_state.replicaLayer->m_state.maskLayer->applyMask(options);
}
if (m_state.maskLayer)
m_state.maskLayer->applyMask(options);
options.surface = options.surface->applyFilters(options.textureMapper, m_currentFilters);

bool hasMask = !!m_state.maskLayer;
bool hasReplicaMask = options.replicaLayer == this && m_state.replicaLayer->m_state.maskLayer;
bool defersLastFilter = !hasMask && !hasReplicaMask;
options.surface = options.surface->applyFilters(options.textureMapper, m_currentFilters, defersLastFilter);
options.textureMapper.bindSurface(options.surface.get());
if (hasMask)
m_state.maskLayer->applyMask(options);
if (hasReplicaMask)
m_state.replicaLayer->m_state.maskLayer->applyMask(options);
}

static void commitSurface(TextureMapperPaintOptions& options, BitmapTexture& surface, const IntRect& rect, float opacity)

0 comments on commit 9fed47e

Please sign in to comment.