Title: [295514] trunk
Revision
295514
Author
hironori.fu...@sony.com
Date
2022-06-13 20:34:12 -0700 (Mon, 13 Jun 2022)

Log Message

TextureMapper: if an element has both a mask image and reflection, the 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

Modified Paths

Added Paths

Diff

Added: trunk/LayoutTests/compositing/reflections/mask-and-reflection-expected.html (0 => 295514)


--- trunk/LayoutTests/compositing/reflections/mask-and-reflection-expected.html	                        (rev 0)
+++ trunk/LayoutTests/compositing/reflections/mask-and-reflection-expected.html	2022-06-14 03:34:12 UTC (rev 295514)
@@ -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>

Added: trunk/LayoutTests/compositing/reflections/mask-and-reflection.html (0 => 295514)


--- trunk/LayoutTests/compositing/reflections/mask-and-reflection.html	                        (rev 0)
+++ trunk/LayoutTests/compositing/reflections/mask-and-reflection.html	2022-06-14 03:34:12 UTC (rev 295514)
@@ -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>

Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp (295513 => 295514)


--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp	2022-06-14 03:10:02 UTC (rev 295513)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp	2022-06-14 03:34:12 UTC (rev 295514)
@@ -39,6 +39,7 @@
     float opacity { 1 };
     IntSize offset;
     TextureMapperLayer* backdropLayer { nullptr };
+    TextureMapperLayer* replicaLayer { nullptr };
     bool preserves3D { false };
 };
 
@@ -285,10 +286,7 @@
     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 @@
 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 @@
     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 @@
     data.clipBounds.move(-options.offset);
     computeOverlapRegions(data, options.transform);
     if (overlapRegion.isEmpty()) {
-        paintSelfAndChildrenWithReplica(options);
+        paintSelfChildrenReplicaFilterAndMask(options);
         return;
     }
 
@@ -412,7 +407,7 @@
 
     for (auto& rect : rects) {
         options.textureMapper.beginClip(TransformationMatrix(), FloatRoundedRect(rect));
-        paintSelfAndChildrenWithReplica(options);
+        paintSelfChildrenReplicaFilterAndMask(options);
         options.textureMapper.endClip();
     }
 
@@ -436,6 +431,43 @@
     }
 }
 
+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 @@
     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,16 +510,22 @@
         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,6 +533,29 @@
     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())
@@ -497,12 +563,10 @@
 
     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::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;

Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h (295513 => 295514)


--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h	2022-06-14 03:10:02 UTC (rev 295513)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.h	2022-06-14 03:34:12 UTC (rev 295514)
@@ -132,9 +132,12 @@
     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&);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to