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: http://svn.webkit.org/repository/webkit/trunk@295717 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/compositing/masks/mask-and-drop-shadow-expected.html b/LayoutTests/compositing/masks/mask-and-drop-shadow-expected.html
new file mode 100644
index 0000000..21978ee
--- /dev/null
+++ b/LayoutTests/compositing/masks/mask-and-drop-shadow-expected.html
@@ -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>
diff --git a/LayoutTests/compositing/masks/mask-and-drop-shadow.html b/LayoutTests/compositing/masks/mask-and-drop-shadow.html
new file mode 100644
index 0000000..928eec3
--- /dev/null
+++ b/LayoutTests/compositing/masks/mask-and-drop-shadow.html
@@ -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>
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTexture.h b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h
index 01d6a6c..36be940 100644
--- a/Source/WebCore/platform/graphics/texmap/BitmapTexture.h
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTexture.h
@@ -78,7 +78,7 @@
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;
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp
index 91e8ea8..7b69f8a 100644
--- a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.cpp
@@ -202,7 +202,7 @@
}
}
-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 @@
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;
diff --git a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h
index 5b163f6..b2a9182 100644
--- a/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h
+++ b/Source/WebCore/platform/graphics/texmap/BitmapTextureGL.h
@@ -60,7 +60,7 @@
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;
diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
index fd5b2de..98a25da 100644
--- a/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
+++ b/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
@@ -343,7 +343,7 @@
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 @@
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)