From 96a35767b61121ab1ea9201e639f832ed5c9d773 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 16 Nov 2024 03:25:48 +0300 Subject: [PATCH] LibWeb: Implement `mask-image` CSS property support Implemented by reusing AddMask display list item that was initially added for `background-clip` property. Progress on flashlight effect on https://null.com/games/athena-crisis --- Libraries/LibWeb/CSS/ComputedValues.h | 3 + Libraries/LibWeb/CSS/Properties.json | 15 +++ Libraries/LibWeb/Layout/Node.cpp | 8 +- Libraries/LibWeb/Painting/PaintableBox.cpp | 4 + Libraries/LibWeb/Painting/StackingContext.cpp | 9 ++ .../data/transparent-100x50-blue-100x50.png | Bin 0 -> 233 bytes .../mask-image/mask-image-1-ref.html | 19 +++ .../css-masking/mask-image/mask-image-1a.html | 28 +++++ ...upported-properties-and-default-values.txt | 7 +- ...eclaration-has-indexed-property-getter.txt | 113 +++++++++--------- .../css/getComputedStyle-print-all.txt | 1 + 11 files changed, 149 insertions(+), 58 deletions(-) create mode 100644 Tests/LibWeb/Ref/data/transparent-100x50-blue-100x50.png create mode 100644 Tests/LibWeb/Ref/expected/wpt-import/css/css-masking/mask-image/mask-image-1-ref.html create mode 100644 Tests/LibWeb/Ref/input/wpt-import/css/css-masking/mask-image/mask-image-1a.html diff --git a/Libraries/LibWeb/CSS/ComputedValues.h b/Libraries/LibWeb/CSS/ComputedValues.h index 4b00b72335e..3c87c141603 100644 --- a/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Libraries/LibWeb/CSS/ComputedValues.h @@ -486,6 +486,7 @@ public: Color stop_color() const { return m_noninherited.stop_color; } float stop_opacity() const { return m_noninherited.stop_opacity; } CSS::TextAnchor text_anchor() const { return m_inherited.text_anchor; } + RefPtr mask_image() const { return m_noninherited.mask_image; } Optional const& mask() const { return m_noninherited.mask; } CSS::MaskType mask_type() const { return m_noninherited.mask_type; } Optional const& clip_path() const { return m_noninherited.clip_path; } @@ -681,6 +682,7 @@ protected: Optional mask; CSS::MaskType mask_type { InitialValues::mask_type() }; Optional clip_path; + RefPtr mask_image; LengthPercentage cx { InitialValues::cx() }; LengthPercentage cy { InitialValues::cy() }; @@ -838,6 +840,7 @@ public: void set_outline_width(CSS::Length value) { m_noninherited.outline_width = value; } void set_mask(MaskReference value) { m_noninherited.mask = value; } void set_mask_type(CSS::MaskType value) { m_noninherited.mask_type = value; } + void set_mask_image(CSS::AbstractImageStyleValue const& value) { m_noninherited.mask_image = value; } void set_clip_path(ClipPathReference value) { m_noninherited.clip_path = value; } void set_clip_rule(CSS::ClipRule value) { m_inherited.clip_rule = value; } diff --git a/Libraries/LibWeb/CSS/Properties.json b/Libraries/LibWeb/CSS/Properties.json index f7f737870fe..80b76be9503 100644 --- a/Libraries/LibWeb/CSS/Properties.json +++ b/Libraries/LibWeb/CSS/Properties.json @@ -95,6 +95,9 @@ "-webkit-mask": { "legacy-alias-for": "mask" }, + "-webkit-mask-image": { + "legacy-alias-for": "mask-image" + }, "-webkit-order": { "legacy-alias-for": "order" }, @@ -1859,6 +1862,18 @@ ], "initial": "none" }, + "mask-image": { + "animation-type": "discrete", + "inherited": false, + "affects-layout": false, + "valid-types": [ + "image" + ], + "valid-identifiers": [ + "none" + ], + "initial": "none" + }, "mask-type": { "animation-type": "discrete", "inherited": false, diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 778f963f32f..c2b99f1ea92 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -199,7 +199,7 @@ bool Node::establishes_stacking_context() const // - perspective // - clip-path // - mask / mask-image / mask-border - if (computed_values().mask().has_value() || computed_values().clip_path().has_value()) + if (computed_values().mask().has_value() || computed_values().clip_path().has_value() || computed_values().mask_image()) return true; return computed_values().opacity() < 1.0f; @@ -829,6 +829,12 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) else if (stroke_width.is_percentage()) computed_values.set_stroke_width(CSS::LengthPercentage { stroke_width.as_percentage().percentage() }); + if (auto const& mask_image = computed_style.property(CSS::PropertyID::MaskImage); mask_image.is_abstract_image()) { + auto const& abstract_image = mask_image.as_abstract_image(); + computed_values.set_mask_image(abstract_image); + const_cast(abstract_image).load_any_resources(document()); + } + if (auto mask_type = computed_style.mask_type(); mask_type.has_value()) computed_values.set_mask_type(*mask_type); diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 824e1ab0250..aaff8189b74 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -1177,6 +1177,10 @@ void PaintableBox::resolve_paint_properties() if (background_layers) { m_resolved_background = resolve_background_layers(*background_layers, *this, background_color, background_rect, normalized_border_radii_data()); }; + + if (auto mask_image = computed_values.mask_image()) { + mask_image->resolve_for_size(layout_node_with_style_and_box_metrics(), absolute_padding_box_rect().size()); + } } void PaintableWithLines::resolve_paint_properties() diff --git a/Libraries/LibWeb/Painting/StackingContext.cpp b/Libraries/LibWeb/Painting/StackingContext.cpp index 396ccc93b62..41d630e35c8 100644 --- a/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Libraries/LibWeb/Painting/StackingContext.cpp @@ -329,6 +329,15 @@ void StackingContext::paint(PaintContext& context) const } context.display_list_recorder().push_stacking_context(push_stacking_context_params); + if (auto mask_image = computed_values.mask_image()) { + auto mask_display_list = DisplayList::create(); + DisplayListRecorder display_list_recorder(*mask_display_list); + auto mask_painting_context = context.clone(display_list_recorder); + auto mask_rect_in_device_pixels = context.enclosing_device_rect(paintable_box().absolute_padding_box_rect()); + mask_image->paint(mask_painting_context, { {}, mask_rect_in_device_pixels.size() }, CSS::ImageRendering::Auto); + context.display_list_recorder().add_mask(mask_display_list, mask_rect_in_device_pixels.to_type()); + } + if (auto masking_area = paintable_box().get_masking_area(); masking_area.has_value()) { if (masking_area->is_empty()) return; diff --git a/Tests/LibWeb/Ref/data/transparent-100x50-blue-100x50.png b/Tests/LibWeb/Ref/data/transparent-100x50-blue-100x50.png new file mode 100644 index 0000000000000000000000000000000000000000..f451746654636fdc56b6fdc307ff2f2d12e09b3c GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^DIm + + + + CSS Masking: mask-image: mask layer image + + + + + +
+ + diff --git a/Tests/LibWeb/Ref/input/wpt-import/css/css-masking/mask-image/mask-image-1a.html b/Tests/LibWeb/Ref/input/wpt-import/css/css-masking/mask-image/mask-image-1a.html new file mode 100644 index 00000000000..980c3fb9974 --- /dev/null +++ b/Tests/LibWeb/Ref/input/wpt-import/css/css-masking/mask-image/mask-image-1a.html @@ -0,0 +1,28 @@ + + + + + CSS Masking: mask-image: mask layer image + + + + + + + + +
+ + + + diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt index 74c231cd7bf..e61ef9be224 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-all-supported-properties-and-default-values.txt @@ -1,6 +1,6 @@ All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle: 'cssText': '' -'length': '201' +'length': '202' 'parentRule': 'null' 'cssFloat': 'none' 'WebkitAlignContent': 'normal' @@ -99,6 +99,9 @@ All supported properties and their default values exposed from CSSStyleDeclarati 'WebkitMask': 'none' 'webkitMask': 'none' '-webkit-mask': 'none' +'WebkitMaskImage': 'none' +'webkitMaskImage': 'none' +'-webkit-mask-image': 'none' 'WebkitOrder': '0' 'webkitOrder': '0' '-webkit-order': '0' @@ -403,6 +406,8 @@ All supported properties and their default values exposed from CSSStyleDeclarati 'marginTop': '8px' 'margin-top': '8px' 'mask': 'none' +'maskImage': 'none' +'mask-image': 'none' 'maskType': 'luminance' 'mask-type': 'luminance' 'mathDepth': '0' diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt index fdb821b2e54..b080dde96f0 100644 --- a/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt +++ b/Tests/LibWeb/Text/expected/css/CSSStyleDeclaration-has-indexed-property-getter.txt @@ -145,62 +145,63 @@ All properties associated with getComputedStyle(document.body): "142": "margin-right", "143": "margin-top", "144": "mask", - "145": "mask-type", - "146": "max-height", - "147": "max-inline-size", - "148": "max-width", - "149": "min-height", - "150": "min-inline-size", - "151": "min-width", - "152": "object-fit", - "153": "object-position", - "154": "opacity", - "155": "order", - "156": "outline-color", - "157": "outline-offset", - "158": "outline-style", - "159": "outline-width", - "160": "overflow-x", - "161": "overflow-y", - "162": "padding-block-end", - "163": "padding-block-start", - "164": "padding-bottom", - "165": "padding-inline-end", - "166": "padding-inline-start", - "167": "padding-left", - "168": "padding-right", - "169": "padding-top", - "170": "position", - "171": "r", - "172": "right", - "173": "rotate", - "174": "row-gap", - "175": "rx", - "176": "ry", - "177": "scrollbar-gutter", - "178": "scrollbar-width", - "179": "stop-color", - "180": "stop-opacity", - "181": "table-layout", - "182": "text-decoration-color", - "183": "text-decoration-style", - "184": "text-decoration-thickness", - "185": "text-overflow", - "186": "top", - "187": "transform", - "188": "transform-box", - "189": "transform-origin", - "190": "transition-delay", - "191": "transition-duration", - "192": "transition-property", - "193": "transition-timing-function", - "194": "unicode-bidi", - "195": "user-select", - "196": "vertical-align", - "197": "width", - "198": "x", - "199": "y", - "200": "z-index" + "145": "mask-image", + "146": "mask-type", + "147": "max-height", + "148": "max-inline-size", + "149": "max-width", + "150": "min-height", + "151": "min-inline-size", + "152": "min-width", + "153": "object-fit", + "154": "object-position", + "155": "opacity", + "156": "order", + "157": "outline-color", + "158": "outline-offset", + "159": "outline-style", + "160": "outline-width", + "161": "overflow-x", + "162": "overflow-y", + "163": "padding-block-end", + "164": "padding-block-start", + "165": "padding-bottom", + "166": "padding-inline-end", + "167": "padding-inline-start", + "168": "padding-left", + "169": "padding-right", + "170": "padding-top", + "171": "position", + "172": "r", + "173": "right", + "174": "rotate", + "175": "row-gap", + "176": "rx", + "177": "ry", + "178": "scrollbar-gutter", + "179": "scrollbar-width", + "180": "stop-color", + "181": "stop-opacity", + "182": "table-layout", + "183": "text-decoration-color", + "184": "text-decoration-style", + "185": "text-decoration-thickness", + "186": "text-overflow", + "187": "top", + "188": "transform", + "189": "transform-box", + "190": "transform-origin", + "191": "transition-delay", + "192": "transition-duration", + "193": "transition-property", + "194": "transition-timing-function", + "195": "unicode-bidi", + "196": "user-select", + "197": "vertical-align", + "198": "width", + "199": "x", + "200": "y", + "201": "z-index" } All properties associated with document.body.style by default: {} diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index 215d5b2e1c9..124a50719a9 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -143,6 +143,7 @@ margin-left: 8px margin-right: 8px margin-top: 8px mask: none +mask-image: none mask-type: luminance max-height: none max-inline-size: none