Преглед на файлове

LibWeb: Move some common SVG gradient functions into SVGGradientElement

These functions will also be used by SVG radial gradients.
MacDue преди 2 години
родител
ревизия
2826bd2b45

+ 29 - 0
Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp

@@ -49,6 +49,35 @@ Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform() const
     return {};
 }
 
+// The gradient transform, appropriately scaled and combined with the paint transform.
+Gfx::AffineTransform SVGGradientElement::gradient_paint_transform(SVGPaintContext const& paint_context) const
+{
+    auto transform = gradient_transform().value_or(Gfx::AffineTransform {});
+    if (gradient_units() == GradientUnits::ObjectBoundingBox) {
+        // Adjust transform to take place in the coordinate system defined by the bounding box:
+        return Gfx::AffineTransform { paint_context.transform }
+            .translate(paint_context.path_bounding_box.location())
+            .scale(paint_context.path_bounding_box.width(), paint_context.path_bounding_box.height())
+            .multiply(transform);
+    }
+    return Gfx::AffineTransform { paint_context.transform }.multiply(transform);
+}
+
+void SVGGradientElement::add_color_stops(Gfx::SVGGradientPaintStyle& paint_style) const
+{
+    for_each_color_stop([&](auto& stop) {
+        // https://svgwg.org/svg2-draft/pservers.html#StopNotes
+        // Gradient offset values less than 0 (or less than 0%) are rounded up to 0%.
+        // Gradient offset values greater than 1 (or greater than 100%) are rounded down to 100%.
+        float stop_offset = AK::clamp(stop.stop_offset().value(), 0.0f, 1.0f);
+        // FIXME: Each gradient offset value is required to be equal to or greater than the previous gradient
+        // stop's offset value. If a given gradient stop's offset value is not equal to or greater than all
+        // previous offset values, then the offset value is adjusted to be equal to the largest of all previous
+        // offset values.
+        paint_style.add_color_stop(stop_offset, stop.stop_color()).release_value_but_fixme_should_propagate_errors();
+    });
+}
+
 JS::GCPtr<SVGGradientElement const> SVGGradientElement::xlink_href() const
 {
     // FIXME: This entire function is an ad-hoc hack!

+ 4 - 0
Userland/Libraries/LibWeb/SVG/SVGGradientElement.h

@@ -41,6 +41,8 @@ protected:
 
     JS::GCPtr<SVGGradientElement const> xlink_href() const;
 
+    Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const;
+
     template<VoidFunction<SVGStopElement> Callback>
     void for_each_color_stop(Callback const& callback) const
     {
@@ -55,6 +57,8 @@ protected:
         }
     }
 
+    void add_color_stops(Gfx::SVGGradientPaintStyle&) const;
+
 private:
     Optional<GradientUnits> m_gradient_units = {};
     Optional<Gfx::AffineTransform> m_gradient_transform = {};

+ 5 - 19
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp

@@ -103,8 +103,8 @@ Optional<Gfx::PaintStyle const&> SVGLinearGradientElement::to_gfx_paint_style(SV
         // box units) and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent
         // values relative to the bounding box for the object.
         // Note: For gradientUnits="objectBoundingBox" both "100%" and "1" are treated the same.
-        start_point = paint_context.path_bounding_box.location() + Gfx::FloatPoint { start_x().value() * paint_context.path_bounding_box.width(), start_y().value() * paint_context.path_bounding_box.height() };
-        end_point = paint_context.path_bounding_box.location() + Gfx::FloatPoint { end_x().value() * paint_context.path_bounding_box.width(), end_y().value() * paint_context.path_bounding_box.height() };
+        start_point = { start_x().value(), start_y().value() };
+        end_point = { end_x().value(), end_y().value() };
     } else {
         // GradientUnits::UserSpaceOnUse
         // If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system
@@ -125,28 +125,14 @@ Optional<Gfx::PaintStyle const&> SVGLinearGradientElement::to_gfx_paint_style(SV
     if (!m_paint_style) {
         m_paint_style = Gfx::SVGLinearGradientPaintStyle::create(start_point, end_point)
                             .release_value_but_fixme_should_propagate_errors();
-        // FIXME: Update this if DOM changes?
-        for_each_color_stop([&](auto& stop) {
-            m_paint_style->add_color_stop(stop.stop_offset().value(), stop.stop_color()).release_value_but_fixme_should_propagate_errors();
-        });
+        // FIXME: Update stops in DOM changes:
+        add_color_stops(*m_paint_style);
     } else {
         m_paint_style->set_start_point(start_point);
         m_paint_style->set_end_point(end_point);
     }
 
-    auto gradient_affine_transform = gradient_transform().value_or(Gfx::AffineTransform {});
-
-    if (units == GradientUnits::ObjectBoundingBox) {
-        // Adjust transform to take place in the coordinate system defined by the bounding box:
-        gradient_affine_transform = Gfx::AffineTransform {}
-                                        .translate(paint_context.path_bounding_box.location())
-                                        .scale(paint_context.path_bounding_box.width(), paint_context.path_bounding_box.height())
-                                        .multiply(gradient_affine_transform)
-                                        .scale(1 / paint_context.path_bounding_box.width(), 1 / paint_context.path_bounding_box.height())
-                                        .translate(-paint_context.path_bounding_box.location());
-    }
-
-    m_paint_style->set_gradient_transform(Gfx::AffineTransform { paint_context.transform }.multiply(gradient_affine_transform));
+    m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context));
     return *m_paint_style;
 }