Prechádzať zdrojové kódy

LibWeb/SVG: Support url() in the stroke attribute

This allows you to draw gradients in strokes, for example.
Luke Wilde 2 rokov pred
rodič
commit
42a183720b

+ 11 - 0
Base/res/html/misc/svg-gradients.html

@@ -132,3 +132,14 @@
   </defs>
   </defs>
   <rect x="115" y="15" width="170" height="110" fill="url(#grad7)" transform="rotate(45 200 70)" />
   <rect x="115" y="15" width="170" height="110" fill="url(#grad7)" transform="rotate(45 200 70)" />
 </svg>
 </svg>
+<br>
+<b>Stroke linear gradient + transform</b><br>
+<svg height="150" width="400">
+  <defs>
+    <linearGradient id="grad7" x1="0" y1="0" x2="70%" y2="0">
+      <stop offset="0" stop-color="blue"/>
+      <stop offset="1" stop-color="magenta"/>
+    </linearGradient>
+  </defs>
+  <rect x="115" y="15" width="170" height="110" stroke="url(#grad7)" fill="none" transform="rotate(45 200 70)" />
+</svg>

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -7453,6 +7453,7 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
             return parsed_value.release_nonnull();
             return parsed_value.release_nonnull();
         return ParseError ::SyntaxError;
         return ParseError ::SyntaxError;
     case PropertyID::Fill:
     case PropertyID::Fill:
+    case PropertyID::Stroke:
         if (component_values.size() == 1) {
         if (component_values.size() == 1) {
             if (auto parsed_url = FIXME_TRY(parse_url_value(component_values.first())))
             if (auto parsed_url = FIXME_TRY(parse_url_value(component_values.first())))
                 return parsed_url.release_nonnull();
                 return parsed_url.release_nonnull();

+ 3 - 1
Userland/Libraries/LibWeb/CSS/Properties.json

@@ -1663,8 +1663,10 @@
     "affects-layout": false,
     "affects-layout": false,
     "inherited": true,
     "inherited": true,
     "initial": "none",
     "initial": "none",
+    "__comment": "FIXME: Use `paint` as the type, once we have a PaintStyleValue and generic parsing for it.",
     "valid-types": [
     "valid-types": [
-      "color"
+      "color",
+      "url"
     ],
     ],
     "valid-identifiers": [
     "valid-identifiers": [
       "none"
       "none"

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -155,6 +155,7 @@ class StyleSheetList;
 class StyleValue;
 class StyleValue;
 class StyleValueList;
 class StyleValueList;
 class Supports;
 class Supports;
+class SVGPaint;
 class TextDecorationStyleValue;
 class TextDecorationStyleValue;
 class Time;
 class Time;
 class TimeOrCalculated;
 class TimeOrCalculated;

+ 4 - 2
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));
         computed_values.set_fill(fill->to_color(*this));
     else if (fill->is_url())
     else if (fill->is_url())
         computed_values.set_fill(fill->as_url().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));
         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())
     if (auto stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color->has_color())
         computed_values.set_stop_color(stop_color->to_color(*this));
         computed_values.set_stop_color(stop_color->to_color(*this));
     auto stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
     auto stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);

+ 11 - 3
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());
     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(
         painter.stroke_path(
             path,
             path,
             stroke_color,
             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);
     }
     }
 }
 }
 
 

+ 17 - 6
Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp

@@ -40,15 +40,12 @@ void SVGGraphicsElement::parse_attribute(DeprecatedFlyString const& name, Deprec
     }
     }
 }
 }
 
 
-Optional<Gfx::PaintStyle const&> SVGGraphicsElement::fill_paint_style(SVGPaintContext const& paint_context) const
+Optional<Gfx::PaintStyle const&> SVGGraphicsElement::svg_paint_computed_value_to_gfx_paint_style(SVGPaintContext const& paint_context, Optional<CSS::SVGPaint> const& paint_value) const
 {
 {
     // FIXME: This entire function is an ad-hoc hack:
     // FIXME: This entire function is an ad-hoc hack:
-    if (!layout_node())
+    if (!paint_value.has_value() || !paint_value->is_url())
         return {};
         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());
     auto gradient = document().get_element_by_id(url.fragment());
     if (!gradient)
     if (!gradient)
         return {};
         return {};
@@ -57,6 +54,20 @@ Optional<Gfx::PaintStyle const&> SVGGraphicsElement::fill_paint_style(SVGPaintCo
     return {};
     return {};
 }
 }
 
 
+Optional<Gfx::PaintStyle const&> 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<Gfx::PaintStyle const&> 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> transform_list)
 Gfx::AffineTransform transform_from_transform_list(ReadonlySpan<Transform> transform_list)
 {
 {
     Gfx::AffineTransform affine_transform;
     Gfx::AffineTransform affine_transform;

+ 3 - 0
Userland/Libraries/LibWeb/SVG/SVGGraphicsElement.h

@@ -43,12 +43,15 @@ public:
     Gfx::AffineTransform get_transform() const;
     Gfx::AffineTransform get_transform() const;
 
 
     Optional<Gfx::PaintStyle const&> fill_paint_style(SVGPaintContext const&) const;
     Optional<Gfx::PaintStyle const&> fill_paint_style(SVGPaintContext const&) const;
+    Optional<Gfx::PaintStyle const&> stroke_paint_style(SVGPaintContext const&) const;
 
 
 protected:
 protected:
     SVGGraphicsElement(DOM::Document&, DOM::QualifiedName);
     SVGGraphicsElement(DOM::Document&, DOM::QualifiedName);
 
 
     virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
     virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
 
 
+    Optional<Gfx::PaintStyle const&> svg_paint_computed_value_to_gfx_paint_style(SVGPaintContext const& paint_context, Optional<CSS::SVGPaint> const& paint_value) const;
+
     Gfx::AffineTransform m_transform = {};
     Gfx::AffineTransform m_transform = {};
 };
 };