Skip to content

Commit

Permalink
[CSS Masking] SVG masks are not working as 'mask-image'
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=260732
rdar://114465545

Reviewed by Simon Fraser.

Implement CSS masking when it references an SVG masker. There seven cases for
mask-image values that need to be considered:

1. Bitmap image
2. Bitmap image fragment
3. External SVG image
4. External SVG image fragment
5. External SVG image mask
6. Inline data URL image
7. Inline SVG image mask

StyleCachedImage will be adjusted to take into account the seventh case. Functions
like StyleCachedImage::isLoaded() and StyleCachedImage::canRender() will have to
handle the new case where StyleCachedImage represents an inline referenced SVG
masker. So isLoaded() needs to a take a RenderElement to be able to resolve the
imageURL.

A new super-class of GeneratedImage named SVGResourceImage will be introduced to
handle drawing the SVG resource given its renderer. SVGResourceImage will handle
the cases of external and inline SVG resources.

We can't know whether the ID associated with an external image is a fragment ID or
a mask ID in an SVGImage till the CachedImage is loaded. StyleCachedImage::image()
is called only after the image is completely loaded. So it can decide to create
an SVGResourceImage or to let the CachedImage creates its Image.

* LayoutTests/TestExpectations:
Some of the WPT tests have been marked as ImageOnlyFailure in this patch because
the expected results are now working as expected but the tests are not.
Unfortunately the tests and the expected results are both using mask-image. So
without this change they match because they both draw nothing for the masked
elements. With this change, the tests are failing  because the mask geometry
is set to work with content-box only. This can be addressed in a separate patch.

* LayoutTests/css3/masking/mask-reference-png-fragment-expected.html: Added.
* LayoutTests/css3/masking/mask-reference-png-fragment.html: Added.
* LayoutTests/css3/masking/resources/red-100x100.png: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-fragment-expected.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-fragment.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-image-expected.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-image.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-mask-expected.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-external-svg-mask.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-inline-svg-image-expected.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-inline-svg-image.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-inline-svg-mask-expected.html: Added.
* LayoutTests/http/tests/css/css-masking/mask-inline-svg-mask.html: Added.
* LayoutTests/http/tests/css/css-masking/resources/heart.svg: Added.
* LayoutTests/http/tests/css/css-masking/resources/star-heart-fragments.svg: Added.
* LayoutTests/http/tests/css/css-masking/resources/star-heart-masks.svg: Added.
* LayoutTests/http/tests/css/css-masking/resources/star.svg: Added.

* LayoutTests/imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-opacity-1d.html:
This tests is now drawing the masked element but with noise around the borders.

* LayoutTests/platform/mac-wk1/TestExpectations:
* LayoutTests/platform/wpe/TestExpectations:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/css/CSSImageValue.cpp:
* Source/WebCore/platform/graphics/Image.cpp:
(WebCore::operator<<):
* Source/WebCore/platform/graphics/Image.h:
(WebCore::Image::isSVGResourceImage const):
* Source/WebCore/rendering/BorderPainter.cpp:
(WebCore::BorderPainter::paintBorder):
(WebCore::BorderPainter::paintNinePieceImage):
* Source/WebCore/rendering/CSSFilter.cpp:
(WebCore::referenceFilterElement):
* Source/WebCore/rendering/InlineBoxPainter.cpp:
(WebCore::InlineBoxPainter::paintMask):
(WebCore::InlineBoxPainter::paintDecorations):
* Source/WebCore/rendering/ReferencedSVGResources.cpp:
(WebCore::ReferencedSVGResources::referencedRenderResource):
* Source/WebCore/rendering/ReferencedSVGResources.h:
* Source/WebCore/rendering/RenderBox.cpp:
(WebCore::RenderBox::paintMaskImages):
(WebCore::RenderBox::repaintLayerRectsForImage):
* Source/WebCore/rendering/RenderElement.cpp:
(WebCore::RenderElement::borderImageIsLoadedAndCanBeRendered const):
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::setupClipPath):
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::canDirectlyCompositeBackgroundBackgroundImage):
(WebCore::hasPaintedBoxDecorationsOrBackgroundImage):
(WebCore::supportsDirectlyCompositedBoxDecorations):
(WebCore::RenderLayerBacking::contentChanged):
* Source/WebCore/rendering/style/FillLayer.cpp:
(WebCore::FillLayer::imagesAreLoaded const):
* Source/WebCore/rendering/style/FillLayer.h:
* Source/WebCore/rendering/style/NinePieceImage.cpp:
(WebCore::NinePieceImage::paint const):
* Source/WebCore/rendering/style/StyleCachedImage.cpp:
(WebCore::StyleCachedImage::renderSVGResource const):
(WebCore::StyleCachedImage::canRender const):
(WebCore::StyleCachedImage::isLoaded const):
(WebCore::StyleCachedImage::imageSize const):
(WebCore::StyleCachedImage::computeIntrinsicDimensions):
(WebCore::StyleCachedImage::setContainerContextForRenderer):
(WebCore::StyleCachedImage::image const):
* Source/WebCore/rendering/style/StyleCachedImage.h:
* Source/WebCore/rendering/style/StyleImage.h:
(WebCore::StyleImage::isLoaded const):
(WebCore::StyleImage::reresolvedURL const):
* Source/WebCore/rendering/style/StyleMultiImage.cpp:
(WebCore::StyleMultiImage::isLoaded const):
* Source/WebCore/rendering/style/StyleMultiImage.h:
* Source/WebCore/rendering/svg/RenderSVGResourceMasker.cpp:
(WebCore::RenderSVGResourceMasker::applyResource):
(WebCore::RenderSVGResourceMasker::drawContentIntoMaskImage):
(WebCore::RenderSVGResourceMasker::drawContentIntoContext):
* Source/WebCore/rendering/svg/RenderSVGResourceMasker.h:
* Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp:
(WebCore::writeResources):
* Source/WebCore/rendering/svg/SVGResources.cpp:
(WebCore::SVGResources::buildCachedResources):
* Source/WebCore/svg/graphics/SVGImage.cpp:
(WebCore::SVGImage::rootElement const):
* Source/WebCore/svg/graphics/SVGImage.h:
* Source/WebCore/svg/graphics/SVGResourceImage.cpp: Added.
(WebCore::SVGResourceImage::create):
(WebCore::SVGResourceImage::SVGResourceImage):
(WebCore::SVGResourceImage::draw):
(WebCore::SVGResourceImage::drawPattern):
(WebCore::SVGResourceImage::dump const):
* Source/WebCore/svg/graphics/SVGResourceImage.h: Added.

Canonical link: https://commits.webkit.org/268272@main
  • Loading branch information
shallawa authored and Said Abou-Hallawa committed Sep 21, 2023
1 parent a636b44 commit cbbe523
Show file tree
Hide file tree
Showing 51 changed files with 738 additions and 77 deletions.
10 changes: 5 additions & 5 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,8 @@ webkit.org/b/207586 css3/filters/effect-combined-hw.html [ ImageOnlyFailure ]
webkit.org/b/207586 css3/filters/effect-drop-shadow-hw.html [ ImageOnlyFailure ]
webkit.org/b/207586 css3/filters/effect-drop-shadow.html [ ImageOnlyFailure ]

webkit.org/b/261667 css3/masking/mask-reference-png-fragment.html [ ImageOnlyFailure ]

webkit.org/b/142175 js/promises-tests/promises-tests-2-1-2.html [ Pass Timeout ]

webkit.org/b/139840 fast/xmlhttprequest/xmlhttprequest-recursive-sync-event.html [ Pass Failure ]
Expand Down Expand Up @@ -2390,26 +2392,24 @@ imported/w3c/web-platform-tests/css/css-masking/clip-path/animations/clip-path-x
imported/w3c/web-platform-tests/css/css-masking/clip-path/clip-path-xywh-001.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/clip-path/clip-path-xywh-002.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/clip-path/clip-path-xywh-003.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-3f.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-3g.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-3h.html [ ImageOnlyFailure ]

webkit.org/b/229370 imported/w3c/web-platform-tests/css/css-masking/clip/clip-filter-order.html [ ImageOnlyFailure ]
webkit.org/b/229370 imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-url-local-mask.html [ ImageOnlyFailure ]
webkit.org/b/229370 imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-url-remote-mask.html [ ImageOnlyFailure ]

webkit.org/b/233543 imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-ib-split.html [ ImageOnlyFailure ]
webkit.org/b/233543 imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-ib-split-2.html [ ImageOnlyFailure ]

imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-clip-1.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-clip-2.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-composite-1c.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-composite-2c.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-1c.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-2.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-3i.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-6.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-image-data-url-image.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-mode-d.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-mode-to-mask-type.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-opacity-1d.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-masking/mask-image/mask-origin-3.html [ ImageOnlyFailure ]

# A support file for a test
Expand Down
32 changes: 32 additions & 0 deletions LayoutTests/css3/masking/mask-reference-png-fragment-expected.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
position: relative;
background-color: green;
width: 100px;
height: 100px;
}
.green-background {
background-color: green;
}
.blue-background {
background-color: blue;
}
.clip-path-top-half {
clip-path: inset(50px 0px 0px 0px);
}
.clip-path-bottom-half {
clip-path: inset(0px 0px 50px 0px);
}
</style>
<body>
<div class="container">
<div class="box green-background clip-path-top-half"></div>
</div>
<div class="container">
<div class="box blue-background clip-path-bottom-half"></div>
</div>
</body>
31 changes: 31 additions & 0 deletions LayoutTests/css3/masking/mask-reference-png-fragment.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
background-color: green;
width: 100px;
height: 100px;
}
.green-background {
background-color: green;
}
.blue-background {
background-color: blue;
}
.mask-bitmap-top-fragment {
mask-image: url(resources/red-100x100.png#xywh=0,0,100,50);
}
.mask-bitmap-bottom-fragment {
mask-image: url(resources/red-100x100.png#xywh=0,50,100,50);
}
</style>
<body>
<div class="container">
<div class="box green-background mask-bitmap-top-fragment"></div>
</div>
<div class="container">
<div class="box blue-background mask-bitmap-bottom-fragment"></div>
</div>
</body>
Binary file added LayoutTests/css3/masking/resources/red-100x100.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
width: 100px;
height: 100px;
opacity: 0.5;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.clip-path-star {
clip-path: path("M 50,2.5 65.5,33.8 100,38.8 75,63.1 80.9,97.6 50,81.3 19.1,97.6 25,63.1 0,38.8 34.5,33.8 Z");
}
.clip-path-heart {
clip-path: path("M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z");
}
</style>
<body>
<div class="container">
<div class="box blue-green-background clip-path-star"></div>
</div>
<div class="container">
<div class="box red-orange-background clip-path-heart"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<meta name="fuzzy" content="maxDifference=1-32; totalPixels=94-8183" />
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
background-color: green;
width: 100px;
height: 100px;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.mask-external-svg-fragment-star {
mask-image: url(resources/star-heart-fragments.svg#star);
}
.mask-external-svg-fragment-heart {
mask-image: url(resources/star-heart-fragments.svg#heart);
}
</style>
<body>
<div class="container">
<div class="box blue-green-background mask-external-svg-fragment-star"></div>
</div>
<div class="container">
<div class="box red-orange-background mask-external-svg-fragment-heart"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
width: 100px;
height: 100px;
opacity: 0.5;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.clip-path-star {
clip-path: path("M 50,2.5 65.5,33.8 100,38.8 75,63.1 80.9,97.6 50,81.3 19.1,97.6 25,63.1 0,38.8 34.5,33.8 Z");
}
.clip-path-heart {
clip-path: path("M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z");
}
</style>
<body>
<div class="container">
<div class="box blue-green-background clip-path-star"></div>
</div>
<div class="container">
<div class="box red-orange-background clip-path-heart"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<meta name="fuzzy" content="maxDifference=1-32; totalPixels=94-8183" />
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
width: 100px;
height: 100px;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.mask-external-svg-star {
mask-image: url(resources/star.svg);
}
.mask-external-svg-heart {
mask-image: url(resources/heart.svg);
}
</style>
<body>
<div class="container">
<div class="box blue-green-background mask-external-svg-star"></div>
</div>
<div class="container">
<div class="box red-orange-background mask-external-svg-heart"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
width: 100px;
height: 100px;
opacity: 0.5;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.clip-path-star {
clip-path: path("M 50,2.5 65.5,33.8 100,38.8 75,63.1 80.9,97.6 50,81.3 19.1,97.6 25,63.1 0,38.8 34.5,33.8 Z");
}
.clip-path-heart {
clip-path: path("M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z");
}
</style>
<body>
<div class="container">
<div class="box blue-green-background clip-path-star"></div>
</div>
<div class="container">
<div class="box red-orange-background clip-path-heart"></div>
</div>
</body>
32 changes: 32 additions & 0 deletions LayoutTests/http/tests/css/css-masking/mask-external-svg-mask.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<meta name="fuzzy" content="maxDifference=1-32; totalPixels=94-8183" />
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
background-color: green;
width: 100px;
height: 100px;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.mask-external-svg-mask-star {
mask-image: url(resources/star-heart-masks.svg#star);
}
.mask-external-svg-mask-heart {
mask-image: url(resources/star-heart-masks.svg#heart);
}
</style>
<body>
<div class="container">
<div class="box blue-green-background mask-external-svg-mask-star"></div>
</div>
<div class="container">
<div class="box red-orange-background mask-external-svg-mask-heart"></div>
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
width: 100px;
height: 100px;
opacity: 0.5;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.clip-path-star {
clip-path: path("M 50,2.5 65.5,33.8 100,38.8 75,63.1 80.9,97.6 50,81.3 19.1,97.6 25,63.1 0,38.8 34.5,33.8 Z");
}
.clip-path-heart {
clip-path: path("M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z");
}
</style>
<body>
<div class="container">
<div class="box blue-green-background clip-path-star"></div>
</div>
<div class="container">
<div class="box red-orange-background clip-path-heart"></div>
</div>
</body>
38 changes: 38 additions & 0 deletions LayoutTests/http/tests/css/css-masking/mask-inline-svg-image.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<meta name="fuzzy" content="maxDifference=1-32; totalPixels=94-8183" />
<style>
.container {
display: inline-block;
border: 1px solid black;
}
.box {
background-color: green;
width: 100px;
height: 100px;
}
.blue-green-background {
background-image: linear-gradient(blue 0%, blue 50%, green 50%, green 100%);
}
.red-orange-background {
background-image: linear-gradient(red 0%, red 50%, orange 50%, orange 100%);
}
.mask-inline-svg-star {
mask-image: url("data:image/svg+xml, \
<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'> \
<path d='M 50,2.5 65.5,33.8 100,38.8 75,63.1 80.9,97.6 50,81.3 19.1,97.6 25,63.1 0,38.8 34.5,33.8 Z' style='stroke:none; fill: red; opacity: 0.5;' /> \
</svg>");
}
.mask-inline-svg-heart {
mask-image: url("data:image/svg+xml, \
<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100'> \
<path d='M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z' style='stroke:none; fill: red; opacity: 0.5;'/> \
</svg>");
}
</style>
<body>
<div class="container">
<div class="box blue-green-background mask-inline-svg-star"></div>
</div>
<div class="container">
<div class="box red-orange-background mask-inline-svg-heart"></div>
</div>
</body>
Loading

0 comments on commit cbbe523

Please sign in to comment.