From 6c9069fa5d3344f5237b6f734f19c0563fcf6dce Mon Sep 17 00:00:00 2001 From: MacDue Date: Sun, 12 May 2024 20:19:43 +0100 Subject: [PATCH] LibWeb: Implement the SVG `clip-rule` attribute This controls the fill rule used when rasterizing `` elements. --- .../reference/images/svg-clip-rule-ref.png | Bin 0 -> 1004 bytes .../Ref/reference/svg-clip-rule-ref.html | 9 ++++++++ Tests/LibWeb/Ref/svg-clip-rule.html | 18 +++++++++++++++ .../css/getComputedStyle-print-all.txt | 3 ++- .../Libraries/LibWeb/CSS/ComputedValues.h | 6 +++++ Userland/Libraries/LibWeb/CSS/Properties.json | 7 ++++++ .../Libraries/LibWeb/CSS/StyleProperties.cpp | 6 +++++ .../Libraries/LibWeb/CSS/StyleProperties.h | 1 + Userland/Libraries/LibWeb/Layout/Node.cpp | 3 +++ .../LibWeb/Painting/SVGPathPaintable.cpp | 3 +-- .../Libraries/LibWeb/SVG/AttributeParser.h | 2 ++ .../LibWeb/SVG/SVGGraphicsElement.cpp | 21 ++++++++++++++---- .../Libraries/LibWeb/SVG/SVGGraphicsElement.h | 3 ++- 13 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 Tests/LibWeb/Ref/reference/images/svg-clip-rule-ref.png create mode 100644 Tests/LibWeb/Ref/reference/svg-clip-rule-ref.html create mode 100644 Tests/LibWeb/Ref/svg-clip-rule.html diff --git a/Tests/LibWeb/Ref/reference/images/svg-clip-rule-ref.png b/Tests/LibWeb/Ref/reference/images/svg-clip-rule-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..356d78902650412faa4384a739163664614a710e GIT binary patch literal 1004 zcmVcu>V+C|M>X-xVZoSz`$T&|Nm%c z7#RQm7#P67|NqFyXlVZ+ApdxH|1dEB|1dD%;QyeY|8Q{s|Dd4A$p8O%c>f?EP*DFU zDF6RRNdHh!|2R1R@bLd|aD;^a`T76<@$vtCeS+aog8%>m>PbXFRA}DqnQK$pKoo{o z6x=O^)Fy!v0ue+iQBj1}-v9s4Dg-9Zkvg5w7>LyAGy$S?8u$qirPFJg0ND%yI8Os_ z5E@;ifw$&?OPU64)*_ede2ir}!K-cBd}}l(PkJ$0kC+t(J}W;LcD8=;&Jc*WRN5P|cRy zgM^I$Q0D`N(CbzMbq>V)CC%h~;;K8kbNlV-Tk8VA0hpzJDROAkLc!hzE{5>x_6ibpJar!dIB!33JX_F>sNwpB3XQJE)(<0{u8Th}Je{e7 z7ZaTp8eOFp;n}VBPLJ<>(o0nO${;^qb$2>0+LBG)9o0^EfGyeN+)?Z_@F82W$-1N1 z>4alTHW_!+I-T=u$tLa2jh;q}w(#6hdONf%vxVo*OubQ#EgW}#DF7E0wt((*R2!WJ zqMtvxBR{-73D^R-BR{;YL~JR#Bi-pn%$A}%vYm#aw&dNB?ezB_x$ph`N18Ng(xgd~ aca;BS!*T#u!*fso0000 + * { + margin: 0; + } + body { + background-color: white; + } + + diff --git a/Tests/LibWeb/Ref/svg-clip-rule.html b/Tests/LibWeb/Ref/svg-clip-rule.html new file mode 100644 index 0000000000..8c51024a4f --- /dev/null +++ b/Tests/LibWeb/Ref/svg-clip-rule.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt index ebd6eac572..d0a23a150e 100644 --- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt @@ -48,6 +48,7 @@ caption-side: top clear: none clip: auto clip-path: none +clip-rule: nonzero color: rgb(0, 0, 0) column-count: auto column-gap: auto @@ -84,7 +85,7 @@ grid-row-start: auto grid-template-areas: grid-template-columns: grid-template-rows: -height: 1462px +height: 1479px image-rendering: auto inline-size: auto inset-block-end: auto diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h index 8beb4f8f02..5989040f1a 100644 --- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h @@ -27,6 +27,8 @@ namespace Web::CSS { +using ClipRule = FillRule; + struct FlexBasisContent { }; using FlexBasis = Variant; @@ -134,6 +136,7 @@ public: static float opacity() { return 1.0f; } static float fill_opacity() { return 1.0f; } static CSS::FillRule fill_rule() { return CSS::FillRule::Nonzero; } + static CSS::ClipRule clip_rule() { return CSS::ClipRule::Nonzero; } static float stroke_opacity() { return 1.0f; } static float stop_opacity() { return 1.0f; } static CSS::TextAnchor text_anchor() { return CSS::TextAnchor::Start; } @@ -430,6 +433,7 @@ public: 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; } + CSS::ClipRule clip_rule() const { return m_inherited.clip_rule; } LengthPercentage const& cx() const { return m_noninherited.cx; } LengthPercentage const& cy() const { return m_noninherited.cy; } @@ -505,6 +509,7 @@ protected: float stroke_opacity { InitialValues::stroke_opacity() }; LengthPercentage stroke_width { Length::make_px(1) }; CSS::TextAnchor text_anchor { InitialValues::text_anchor() }; + CSS::ClipRule clip_rule { InitialValues::clip_rule() }; Vector text_shadow; @@ -731,6 +736,7 @@ public: void set_mask(MaskReference value) { m_noninherited.mask = value; } void set_mask_type(CSS::MaskType value) { m_noninherited.mask_type = value; } void set_clip_path(ClipPathReference value) { m_noninherited.clip_path = value; } + void set_clip_rule(CSS::ClipRule value) { m_inherited.clip_rule = value; } void set_cx(LengthPercentage cx) { m_noninherited.cx = cx; } void set_cy(LengthPercentage cy) { m_noninherited.cy = cy; } diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index d7e165c53d..640d44dab3 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -716,6 +716,13 @@ ], "initial": "none" }, + "clip-rule": { + "affects-layout": false, + "animation-type": "discrete", + "inherited": true, + "initial": "nonzero", + "valid-types": [ "fill-rule" ] + }, "color": { "affects-layout": false, "animation-type": "by-computed-value", diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 8069860490..d4f61e74fb 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -336,6 +336,12 @@ Optional StyleProperties::fill_rule() const return value_id_to_fill_rule(value->to_identifier()); } +Optional StyleProperties::clip_rule() const +{ + auto value = property(CSS::PropertyID::ClipRule); + return value_id_to_fill_rule(value->to_identifier()); +} + Optional StyleProperties::flex_direction() const { auto value = property(CSS::PropertyID::FlexDirection); diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index 8647885368..6782dfd46a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -148,6 +148,7 @@ public: float fill_opacity() const; float stroke_opacity() const; Optional fill_rule() const; + Optional clip_rule() const; Gfx::Font const& first_available_computed_font() const { return m_font_list->first(); } diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 3a5f4a4ec9..e9cef051b6 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -804,6 +804,9 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) if (auto clip_path = computed_style.property(CSS::PropertyID::ClipPath); clip_path->is_url()) computed_values.set_clip_path(clip_path->as_url().url()); + if (auto clip_rule = computed_style.clip_rule(); clip_rule.has_value()) + computed_values.set_clip_rule(*clip_rule); + if (auto fill_rule = computed_style.fill_rule(); fill_rule.has_value()) computed_values.set_fill_rule(*fill_rule); diff --git a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp index 12a61c9e6a..22597d63bc 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp @@ -101,8 +101,7 @@ void SVGPathPaintable::paint(PaintContext& context, PaintPhase phase) const context.recording_painter().fill_path({ .path = closed_path(), .color = Color::Black, - // FIXME: Support clip-rule. - .winding_rule = Gfx::Painter::WindingRule::Nonzero, + .winding_rule = to_gfx_winding_rule(graphics_element.clip_rule().value_or(SVG::ClipRule::Nonzero)), .translation = offset, }); return; diff --git a/Userland/Libraries/LibWeb/SVG/AttributeParser.h b/Userland/Libraries/LibWeb/SVG/AttributeParser.h index bc506863e6..7fe794fe2a 100644 --- a/Userland/Libraries/LibWeb/SVG/AttributeParser.h +++ b/Userland/Libraries/LibWeb/SVG/AttributeParser.h @@ -137,6 +137,8 @@ enum class FillRule { Evenodd }; +using ClipRule = FillRule; + enum class TextAnchor { Start, Middle, diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index 01693ede0a..50a3f3856a 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -158,6 +158,7 @@ void SVGGraphicsElement::apply_presentational_hints(CSS::StyleProperties& style) NamedPropertyID(CSS::PropertyID::Mask), NamedPropertyID(CSS::PropertyID::MaskType), NamedPropertyID(CSS::PropertyID::ClipPath), + NamedPropertyID(CSS::PropertyID::ClipRule), }; CSS::Parser::ParsingContext parsing_context { document(), CSS::Parser::ParsingContext::Mode::SVGPresentationAttribute }; @@ -172,11 +173,9 @@ void SVGGraphicsElement::apply_presentational_hints(CSS::StyleProperties& style) }); } -Optional SVGGraphicsElement::fill_rule() const +static FillRule to_svg_fill_rule(CSS::FillRule fill_rule) { - if (!layout_node()) - return {}; - switch (layout_node()->computed_values().fill_rule()) { + switch (fill_rule) { case CSS::FillRule::Nonzero: return FillRule::Nonzero; case CSS::FillRule::Evenodd: @@ -186,6 +185,20 @@ Optional SVGGraphicsElement::fill_rule() const } } +Optional SVGGraphicsElement::fill_rule() const +{ + if (!layout_node()) + return {}; + return to_svg_fill_rule(layout_node()->computed_values().fill_rule()); +} + +Optional SVGGraphicsElement::clip_rule() const +{ + if (!layout_node()) + return {}; + return to_svg_fill_rule(layout_node()->computed_values().clip_rule()); +} + Optional SVGGraphicsElement::fill_color() const { if (!layout_node()) diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h index cf6f3f772b..9448abd26a 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -35,11 +35,12 @@ public: virtual void attribute_changed(FlyString const& name, Optional const& value) override; Optional fill_color() const; - Optional fill_rule() const; Optional stroke_color() const; Optional stroke_width() const; Optional fill_opacity() const; Optional stroke_opacity() const; + Optional fill_rule() const; + Optional clip_rule() const; float visible_stroke_width() const {