From 42a183720b3a525076e9a6fd8c1284f9aa83ba5b Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Tue, 6 Jun 2023 20:40:10 +0100 Subject: [PATCH] LibWeb/SVG: Support url() in the stroke attribute This allows you to draw gradients in strokes, for example. --- Base/res/html/misc/svg-gradients.html | 11 +++++++++ .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 1 + Userland/Libraries/LibWeb/CSS/Properties.json | 4 +++- Userland/Libraries/LibWeb/Forward.h | 1 + Userland/Libraries/LibWeb/Layout/Node.cpp | 6 +++-- .../LibWeb/Painting/SVGGeometryPaintable.cpp | 14 ++++++++--- .../LibWeb/SVG/SVGGraphicsElement.cpp | 23 ++++++++++++++----- .../Libraries/LibWeb/SVG/SVGGraphicsElement.h | 3 +++ 8 files changed, 51 insertions(+), 12 deletions(-) diff --git a/Base/res/html/misc/svg-gradients.html b/Base/res/html/misc/svg-gradients.html index b01e7b6644..23a4eaa826 100644 --- a/Base/res/html/misc/svg-gradients.html +++ b/Base/res/html/misc/svg-gradients.html @@ -132,3 +132,14 @@ +
+Stroke linear gradient + transform
+ + + + + + + + + diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 03bdde4a1d..e365d7eef9 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -7453,6 +7453,7 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property return parsed_value.release_nonnull(); return ParseError ::SyntaxError; case PropertyID::Fill: + case PropertyID::Stroke: if (component_values.size() == 1) { if (auto parsed_url = FIXME_TRY(parse_url_value(component_values.first()))) return parsed_url.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 016d76245c..42a12bd1ab 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -1663,8 +1663,10 @@ "affects-layout": false, "inherited": true, "initial": "none", + "__comment": "FIXME: Use `paint` as the type, once we have a PaintStyleValue and generic parsing for it.", "valid-types": [ - "color" + "color", + "url" ], "valid-identifiers": [ "none" diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index e262e3cff1..6167d011f8 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -155,6 +155,7 @@ class StyleSheetList; class StyleValue; class StyleValueList; class Supports; +class SVGPaint; class TextDecorationStyleValue; class Time; class TimeOrCalculated; diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 57b63f0d08..ccc74bb40a 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -681,9 +681,11 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) computed_values.set_fill(fill->to_color(*this)); else if (fill->is_url()) computed_values.set_fill(fill->as_url().url()); - // TODO: Allow url()s for strokes - if (auto stroke = computed_style.property(CSS::PropertyID::Stroke); stroke->has_color()) + auto stroke = computed_style.property(CSS::PropertyID::Stroke); + if (stroke->has_color()) computed_values.set_stroke(stroke->to_color(*this)); + else if (stroke->is_url()) + computed_values.set_stroke(stroke->as_url().url()); if (auto stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color->has_color()) computed_values.set_stop_color(stop_color->to_color(*this)); auto stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth); diff --git a/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp index 931b8f6211..3cd9071f1f 100644 --- a/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/SVGGeometryPaintable.cpp @@ -114,12 +114,20 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const } auto stroke_opacity = geometry_element.stroke_opacity().value_or(svg_context.stroke_opacity()); - if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()).with_opacity(stroke_opacity); stroke_color.alpha() > 0) { + + // Note: This is assuming .x_scale() == .y_scale() (which it does currently). + float stroke_thickness = geometry_element.stroke_width().value_or(svg_context.stroke_width()) * viewbox_scale; + + if (auto paint_style = geometry_element.stroke_paint_style(paint_context); paint_style.has_value()) { + painter.stroke_path( + path, + *paint_style, + stroke_thickness); + } else if (auto stroke_color = geometry_element.stroke_color().value_or(svg_context.stroke_color()).with_opacity(stroke_opacity); stroke_color.alpha() > 0) { painter.stroke_path( path, stroke_color, - // Note: This is assuming .x_scale() == .y_scale() (which it does currently). - geometry_element.stroke_width().value_or(svg_context.stroke_width()) * viewbox_scale); + stroke_thickness); } } diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index 54e1528b6e..50195e1d3f 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -40,15 +40,12 @@ void SVGGraphicsElement::parse_attribute(DeprecatedFlyString const& name, Deprec } } -Optional SVGGraphicsElement::fill_paint_style(SVGPaintContext const& paint_context) const +Optional SVGGraphicsElement::svg_paint_computed_value_to_gfx_paint_style(SVGPaintContext const& paint_context, Optional const& paint_value) const { // FIXME: This entire function is an ad-hoc hack: - if (!layout_node()) + if (!paint_value.has_value() || !paint_value->is_url()) return {}; - auto& fill = layout_node()->computed_values().fill(); - if (!fill.has_value() || !fill->is_url()) - return {}; - auto& url = fill->as_url(); + auto& url = paint_value->as_url(); auto gradient = document().get_element_by_id(url.fragment()); if (!gradient) return {}; @@ -57,6 +54,20 @@ Optional SVGGraphicsElement::fill_paint_style(SVGPaintCo return {}; } +Optional SVGGraphicsElement::fill_paint_style(SVGPaintContext const& paint_context) const +{ + if (!layout_node()) + return {}; + return svg_paint_computed_value_to_gfx_paint_style(paint_context, layout_node()->computed_values().fill()); +} + +Optional SVGGraphicsElement::stroke_paint_style(SVGPaintContext const& paint_context) const +{ + if (!layout_node()) + return {}; + return svg_paint_computed_value_to_gfx_paint_style(paint_context, layout_node()->computed_values().stroke()); +} + Gfx::AffineTransform transform_from_transform_list(ReadonlySpan transform_list) { Gfx::AffineTransform affine_transform; diff --git a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h index e3192e5eda..c0d1fd9b62 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h @@ -43,12 +43,15 @@ public: Gfx::AffineTransform get_transform() const; Optional fill_paint_style(SVGPaintContext const&) const; + Optional stroke_paint_style(SVGPaintContext const&) const; protected: SVGGraphicsElement(DOM::Document&, DOM::QualifiedName); virtual JS::ThrowCompletionOr initialize(JS::Realm&) override; + Optional svg_paint_computed_value_to_gfx_paint_style(SVGPaintContext const& paint_context, Optional const& paint_value) const; + Gfx::AffineTransform m_transform = {}; };