Skip to content
Permalink
Browse files
TextureMapper: if an element has both a mask image and reflection, th…
…e part of the reflection isn't painted

https://bugs.webkit.org/show_bug.cgi?id=241257

Reviewed by Don Olmstead.

If an element has both a mask image and reflection, the mask should be
applied both for the reflection and the real layers. However,
TextureMapper was using a single intermediate surface for painting the
reflection and the real layers.
1. Painting the reflection layer
2. Applying the mask to the reflection layer
3. Painting the real layer
4. Applying the mask to the real layer
In the step#4, if the mask layer is larger than the element rect, the
mask was unexpectedly applied to the part of the reflection.

The reflection and the real layers should be painted and masked
separately using intermediate surfaces. If the element has opacity,
they should have another intermediate surface to blend together with
the destination.

* LayoutTests/compositing/reflections/mask-and-reflection-expected.html: Added.
* LayoutTests/compositing/reflections/mask-and-reflection.html: Added.
* Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp:
(WebCore::TextureMapperLayer::shouldBlend const):
(WebCore::TextureMapperLayer::paintSelfAndChildrenWithReplica):
(WebCore::TextureMapperLayer::paintUsingOverlapRegions):
(WebCore::TextureMapperLayer::paintSelfChildrenFilterAndMask):
(WebCore::TextureMapperLayer::paintIntoSurface):
(WebCore::TextureMapperLayer::paintWithIntermediateSurface):
(WebCore::TextureMapperLayer::paintSelfAndChildrenWithIntermediateSurface):
(WebCore::TextureMapperLayer::paintSelfChildrenReplicaFilterAndMask):
(WebCore::TextureMapperLayer::paintRecursive):
(WebCore::TextureMapperLayer::setMaskLayer):
* Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h:

Canonical link: https://commits.webkit.org/251519@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295514 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
fujii committed Jun 14, 2022
1 parent 802c297 commit bc429581eac94171597099db27b425880ee288ee
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 26 deletions.
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<style>
.outer {
width: 100px;
height: 100px;
-webkit-box-reflect: below 10px;
}

.inner {
width: 100px;
height: 100px;
-webkit-box-reflect: right 10px;
background-color: green;
will-change: transform;
-webkit-mask-image: linear-gradient(-45deg, black, transparent);
mask-image: linear-gradient(-45deg, black, transparent);
}
</style>
</head>
<body>
<p>You should see four mirrored square boxes with linear gradient green.</p>
<div class="outer">
<div class="inner">
</div>
</div>
</body>
</html>
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<style>
.outer {
width: 100px;
height: 100px;
-webkit-box-reflect: below 10px;
}

.inner {
width: 100px;
height: 100px;
-webkit-box-reflect: right 10px;
background-color: green;
will-change: transform;
-webkit-mask-image: linear-gradient(-45deg, black, transparent);
mask-image: linear-gradient(-45deg, black, transparent);
}

.child {
position: relative;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
background-color: blue;
}
</style>
</head>
<body>
<p>You should see four mirrored square boxes with linear gradient green.</p>
<div class="outer">
<div class="inner">
<div class="child">
</div>
</div>
</div>
</body>
</html>
@@ -39,6 +39,7 @@ class TextureMapperPaintOptions {
float opacity { 1 };
IntSize offset;
TextureMapperLayer* backdropLayer { nullptr };
TextureMapperLayer* replicaLayer { nullptr };
bool preserves3D { false };
};

@@ -285,10 +286,7 @@ bool TextureMapperLayer::shouldBlend() const
if (m_state.preserves3D)
return false;

return m_currentOpacity < 1
|| hasFilters()
|| m_state.maskLayer
|| (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer);
return m_currentOpacity < 1;
}

bool TextureMapperLayer::isVisible() const
@@ -307,6 +305,7 @@ bool TextureMapperLayer::isVisible() const
void TextureMapperLayer::paintSelfAndChildrenWithReplica(TextureMapperPaintOptions& options)
{
if (m_state.replicaLayer) {
SetForScope scopedReplicaLayer(options.replicaLayer, this);
SetForScope scopedTransform(options.transform, options.transform);
options.transform.multiply(replicaTransform());
paintSelfAndChildren(options);
@@ -383,10 +382,6 @@ void TextureMapperLayer::paintUsingOverlapRegions(TextureMapperPaintOptions& opt
Region overlapRegion;
Region nonOverlapRegion;
auto mode = ComputeOverlapRegionMode::Intersection;
if (m_state.maskLayer)
mode = ComputeOverlapRegionMode::Mask;
else if (hasFilters() || (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer))
mode = ComputeOverlapRegionMode::Union;
ComputeOverlapRegionData data {
mode,
options.textureMapper.clipBounds(),
@@ -396,7 +391,7 @@ void TextureMapperLayer::paintUsingOverlapRegions(TextureMapperPaintOptions& opt
data.clipBounds.move(-options.offset);
computeOverlapRegions(data, options.transform);
if (overlapRegion.isEmpty()) {
paintSelfAndChildrenWithReplica(options);
paintSelfChildrenReplicaFilterAndMask(options);
return;
}

@@ -412,7 +407,7 @@ void TextureMapperLayer::paintUsingOverlapRegions(TextureMapperPaintOptions& opt

for (auto& rect : rects) {
options.textureMapper.beginClip(TransformationMatrix(), FloatRoundedRect(rect));
paintSelfAndChildrenWithReplica(options);
paintSelfChildrenReplicaFilterAndMask(options);
options.textureMapper.endClip();
}

@@ -436,6 +431,43 @@ void TextureMapperLayer::paintUsingOverlapRegions(TextureMapperPaintOptions& opt
}
}

void TextureMapperLayer::paintSelfChildrenFilterAndMask(TextureMapperPaintOptions& options)
{
Region overlapRegion;
Region nonOverlapRegion;
auto mode = ComputeOverlapRegionMode::Union;
if (m_state.maskLayer || (options.replicaLayer == this && m_state.replicaLayer->m_state.maskLayer))
mode = ComputeOverlapRegionMode::Mask;
ComputeOverlapRegionData data {
mode,
options.textureMapper.clipBounds(),
overlapRegion,
nonOverlapRegion
};
data.clipBounds.move(-options.offset);
computeOverlapRegions(data, options.transform, false);
ASSERT(nonOverlapRegion.isEmpty());

auto rects = overlapRegion.rects();
static const size_t OverlapRegionConsolidationThreshold = 4;
if (rects.size() > OverlapRegionConsolidationThreshold) {
rects.clear();
rects.append(overlapRegion.bounds());
}

IntSize maxTextureSize = options.textureMapper.maxTextureSize();
for (auto& rect : rects) {
for (int x = rect.x(); x < rect.maxX(); x += maxTextureSize.width()) {
for (int y = rect.y(); y < rect.maxY(); y += maxTextureSize.height()) {
IntRect tileRect(IntPoint(x, y), maxTextureSize);
tileRect.intersect(rect);

paintSelfAndChildrenWithIntermediateSurface(options, tileRect);
}
}
}
}

void TextureMapperLayer::applyMask(TextureMapperPaintOptions& options)
{
options.textureMapper.setMaskMode(true);
@@ -448,10 +480,15 @@ void TextureMapperLayer::paintIntoSurface(TextureMapperPaintOptions& options)
options.textureMapper.bindSurface(options.surface.get());
if (m_isBackdrop) {
SetForScope scopedTransform(options.transform, TransformationMatrix());
SetForScope scopedReplicaLayer(options.replicaLayer, nullptr);
SetForScope scopedBackdropLayer(options.backdropLayer, this);
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);
@@ -473,15 +510,21 @@ void TextureMapperLayer::paintWithIntermediateSurface(TextureMapperPaintOptions&
SetForScope scopedSurface(options.surface, surface);
SetForScope scopedOffset(options.offset, -toIntSize(rect.location()));
SetForScope scopedOpacity(options.opacity, 1);
if (m_state.replicaLayer) {
{
SetForScope scopedTransform(options.transform, options.transform);
options.transform.multiply(replicaTransform());
paintIntoSurface(options);
}
if (m_state.replicaLayer->m_state.maskLayer)
m_state.replicaLayer->m_state.maskLayer->applyMask(options);
}

options.textureMapper.bindSurface(options.surface.get());
paintSelfChildrenReplicaFilterAndMask(options);
}

commitSurface(options, *surface, rect, options.opacity);
}

void TextureMapperLayer::paintSelfAndChildrenWithIntermediateSurface(TextureMapperPaintOptions& options, const IntRect& rect)
{
auto surface = options.textureMapper.acquireTextureFromPool(rect.size(), BitmapTexture::SupportsAlpha);
{
SetForScope scopedSurface(options.surface, surface);
SetForScope scopedOffset(options.offset, -toIntSize(rect.location()));
SetForScope scopedOpacity(options.opacity, 1);

paintIntoSurface(options);
surface = options.surface;
@@ -490,19 +533,40 @@ void TextureMapperLayer::paintWithIntermediateSurface(TextureMapperPaintOptions&
commitSurface(options, *surface, rect, options.opacity);
}

void TextureMapperLayer::paintSelfChildrenReplicaFilterAndMask(TextureMapperPaintOptions& options)
{
bool hasFilterOrMask = [&] {
if (hasFilters())
return true;
if (m_state.maskLayer)
return true;
if (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer)
return true;
return false;
}();
if (hasFilterOrMask) {
if (m_state.replicaLayer) {
SetForScope scopedReplicaLayer(options.replicaLayer, this);
SetForScope scopedTransform(options.transform, options.transform);
options.transform.multiply(replicaTransform());
paintSelfChildrenFilterAndMask(options);
}
paintSelfChildrenFilterAndMask(options);
} else
paintSelfAndChildrenWithReplica(options);
}

void TextureMapperLayer::paintRecursive(TextureMapperPaintOptions& options)
{
if (!isVisible())
return;

SetForScope scopedOpacity(options.opacity, options.opacity * m_currentOpacity);

if (!shouldBlend()) {
paintSelfAndChildrenWithReplica(options);
return;
}

paintUsingOverlapRegions(options);
if (shouldBlend())
paintUsingOverlapRegions(options);
else
paintSelfChildrenReplicaFilterAndMask(options);
}

void TextureMapperLayer::setChildren(const Vector<TextureMapperLayer*>& newChildren)
@@ -544,7 +608,7 @@ void TextureMapperLayer::removeAllChildren()
void TextureMapperLayer::setMaskLayer(TextureMapperLayer* maskLayer)
{
if (maskLayer) {
maskLayer->m_effectTarget = *this;
maskLayer->m_effectTarget = m_isReplica ? m_effectTarget : *this;
m_state.maskLayer = *maskLayer;
} else
m_state.maskLayer = nullptr;
@@ -132,9 +132,12 @@ class WEBCORE_EXPORT TextureMapperLayer : public CanMakeWeakPtr<TextureMapperLay
void computeOverlapRegions(ComputeOverlapRegionData&, const TransformationMatrix&, bool includesReplica = true);

void paintRecursive(TextureMapperPaintOptions&);
void paintSelfChildrenReplicaFilterAndMask(TextureMapperPaintOptions&);
void paintUsingOverlapRegions(TextureMapperPaintOptions&);
void paintIntoSurface(TextureMapperPaintOptions&);
void paintWithIntermediateSurface(TextureMapperPaintOptions&, const IntRect&);
void paintSelfAndChildrenWithIntermediateSurface(TextureMapperPaintOptions&, const IntRect&);
void paintSelfChildrenFilterAndMask(TextureMapperPaintOptions&);
void paintSelf(TextureMapperPaintOptions&);
void paintSelfAndChildren(TextureMapperPaintOptions&);
void paintSelfAndChildrenWithReplica(TextureMapperPaintOptions&);

0 comments on commit bc42958

Please sign in to comment.