diff --git a/Tests/LibWeb/Screenshot/images/svg-gradient-spreadMethod-ref.png b/Tests/LibWeb/Screenshot/images/svg-gradient-spreadMethod-ref.png
index 1a141b4f9b7..7dead8f9ed8 100644
Binary files a/Tests/LibWeb/Screenshot/images/svg-gradient-spreadMethod-ref.png and b/Tests/LibWeb/Screenshot/images/svg-gradient-spreadMethod-ref.png differ
diff --git a/Tests/LibWeb/Screenshot/images/svg-gradient-userSpaceOnUse-ref.png b/Tests/LibWeb/Screenshot/images/svg-gradient-userSpaceOnUse-ref.png
new file mode 100644
index 00000000000..c849fa4fe56
Binary files /dev/null and b/Tests/LibWeb/Screenshot/images/svg-gradient-userSpaceOnUse-ref.png differ
diff --git a/Tests/LibWeb/Screenshot/images/svg-radialGradient-ref.png b/Tests/LibWeb/Screenshot/images/svg-radialGradient-ref.png
index 428658e0d2b..d5ae90402c9 100644
Binary files a/Tests/LibWeb/Screenshot/images/svg-radialGradient-ref.png and b/Tests/LibWeb/Screenshot/images/svg-radialGradient-ref.png differ
diff --git a/Tests/LibWeb/Screenshot/images/svg-text-effects-ref.png b/Tests/LibWeb/Screenshot/images/svg-text-effects-ref.png
index a77db2f562b..1abce3203ae 100644
Binary files a/Tests/LibWeb/Screenshot/images/svg-text-effects-ref.png and b/Tests/LibWeb/Screenshot/images/svg-text-effects-ref.png differ
diff --git a/Tests/LibWeb/Screenshot/reference/svg-gradient-userSpaceOnUse-ref.html b/Tests/LibWeb/Screenshot/reference/svg-gradient-userSpaceOnUse-ref.html
new file mode 100644
index 00000000000..5d8b972be61
--- /dev/null
+++ b/Tests/LibWeb/Screenshot/reference/svg-gradient-userSpaceOnUse-ref.html
@@ -0,0 +1,9 @@
+
+
diff --git a/Tests/LibWeb/Screenshot/svg-gradient-userSpaceOnUse.html b/Tests/LibWeb/Screenshot/svg-gradient-userSpaceOnUse.html
new file mode 100644
index 00000000000..1b0d2a4dba3
--- /dev/null
+++ b/Tests/LibWeb/Screenshot/svg-gradient-userSpaceOnUse.html
@@ -0,0 +1,11 @@
+
+
diff --git a/Userland/Libraries/LibGfx/PaintStyle.h b/Userland/Libraries/LibGfx/PaintStyle.h
index 073d8d2399e..9d227d2ae0a 100644
--- a/Userland/Libraries/LibGfx/PaintStyle.h
+++ b/Userland/Libraries/LibGfx/PaintStyle.h
@@ -324,9 +324,6 @@ public:
m_spread_method = spread_method;
}
- void set_inverse_transform(AffineTransform transform) { m_inverse_transform = transform; }
- void set_scale(float scale) { m_scale = scale; }
-
protected:
Optional const& scale_adjusted_inverse_gradient_transform() const { return m_inverse_transform; }
float gradient_transform_scale() const { return m_scale; }
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index a351798e2e5..cf90c6fde2f 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -591,7 +591,6 @@ set(SOURCES
Painting/MediaPaintable.cpp
Painting/NestedBrowsingContextPaintable.cpp
Painting/PaintContext.cpp
- Painting/PaintStyle.cpp
Painting/Paintable.cpp
Painting/PaintableBox.cpp
Painting/PaintableFragment.cpp
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
index 006ccd70e08..24647001120 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
@@ -937,43 +937,34 @@ static SkPaint paint_style_to_skia_paint(Painting::SVGGradientPaintStyle const&
positions.append(color_stop.position);
}
+ SkMatrix matrix;
+ matrix.setTranslate(bounding_rect.x(), bounding_rect.y());
+ if (auto gradient_transform = paint_style.gradient_transform(); gradient_transform.has_value())
+ matrix = matrix * to_skia_matrix(gradient_transform.value());
+
+ auto tile_mode = to_skia_tile_mode(paint_style.spread_method());
+
+ sk_sp shader;
if (is(paint_style)) {
auto const& linear_gradient_paint_style = static_cast(paint_style);
- SkMatrix matrix;
- auto scale = linear_gradient_paint_style.scale();
- auto start_point = linear_gradient_paint_style.start_point().scaled(scale);
- auto end_point = linear_gradient_paint_style.end_point().scaled(scale);
-
- start_point.translate_by(bounding_rect.location());
- end_point.translate_by(bounding_rect.location());
-
- Array points;
- points[0] = to_skia_point(start_point);
- points[1] = to_skia_point(end_point);
-
- auto shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), to_skia_tile_mode(paint_style.spread_method()), 0, &matrix);
- paint.setShader(shader);
+ Array points {
+ to_skia_point(linear_gradient_paint_style.start_point()),
+ to_skia_point(linear_gradient_paint_style.end_point()),
+ };
+ shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), tile_mode, 0, &matrix);
} else if (is(paint_style)) {
auto const& radial_gradient_paint_style = static_cast(paint_style);
- SkMatrix matrix;
- auto scale = radial_gradient_paint_style.scale();
+ auto start_center = to_skia_point(radial_gradient_paint_style.start_center());
+ auto end_center = to_skia_point(radial_gradient_paint_style.end_center());
- auto start_center = radial_gradient_paint_style.start_center().scaled(scale);
- auto end_center = radial_gradient_paint_style.end_center().scaled(scale);
- auto start_radius = radial_gradient_paint_style.start_radius() * scale;
- auto end_radius = radial_gradient_paint_style.end_radius() * scale;
+ auto start_radius = radial_gradient_paint_style.start_radius();
+ auto end_radius = radial_gradient_paint_style.end_radius();
- start_center.translate_by(bounding_rect.location());
- end_center.translate_by(bounding_rect.location());
-
- auto start_sk_point = to_skia_point(start_center);
- auto end_sk_point = to_skia_point(end_center);
-
- auto shader = SkGradientShader::MakeTwoPointConical(start_sk_point, start_radius, end_sk_point, end_radius, colors.data(), positions.data(), color_stops.size(), to_skia_tile_mode(paint_style.spread_method()), 0, &matrix);
- paint.setShader(shader);
+ shader = SkGradientShader::MakeTwoPointConical(start_center, start_radius, end_center, end_radius, colors.data(), positions.data(), color_stops.size(), tile_mode, 0, &matrix);
}
+ paint.setShader(shader);
return paint;
}
diff --git a/Userland/Libraries/LibWeb/Painting/PaintStyle.cpp b/Userland/Libraries/LibWeb/Painting/PaintStyle.cpp
deleted file mode 100644
index a5ebecc8763..00000000000
--- a/Userland/Libraries/LibWeb/Painting/PaintStyle.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2024, Aliaksandr Kalenik
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include
-
-namespace Web::Painting {
-
-void SVGGradientPaintStyle::set_gradient_transform(Gfx::AffineTransform transform)
-{
- // Note: The scaling is removed so enough points on the gradient line are generated.
- // Otherwise, if you scale a tiny path the gradient looks pixelated.
- m_scale = 1.0f;
- if (auto inverse = transform.inverse(); inverse.has_value()) {
- auto transform_scale = transform.scale();
- m_scale = max(transform_scale.x(), transform_scale.y());
- m_inverse_transform = Gfx::AffineTransform {}.scale(m_scale, m_scale).multiply(*inverse);
- } else {
- m_inverse_transform = OptionalNone {};
- }
-}
-
-}
diff --git a/Userland/Libraries/LibWeb/Painting/PaintStyle.h b/Userland/Libraries/LibWeb/Painting/PaintStyle.h
index 0a3aae9bd17..188a86af6e7 100644
--- a/Userland/Libraries/LibWeb/Painting/PaintStyle.h
+++ b/Userland/Libraries/LibWeb/Painting/PaintStyle.h
@@ -20,19 +20,17 @@ struct ColorStop {
class SVGGradientPaintStyle : public RefCounted {
public:
- void set_gradient_transform(Gfx::AffineTransform transform);
-
enum class SpreadMethod {
Pad,
Repeat,
Reflect
};
- void set_spread_method(SpreadMethod spread_method) { m_spread_method = spread_method; }
+ Optional const& gradient_transform() const { return m_gradient_transform; }
+ void set_gradient_transform(Gfx::AffineTransform transform) { m_gradient_transform = transform; }
- Optional const& scale_adjusted_inverse_gradient_transform() const { return m_inverse_transform; }
- float gradient_transform_scale() const { return m_scale; }
SpreadMethod spread_method() const { return m_spread_method; }
+ void set_spread_method(SpreadMethod spread_method) { m_spread_method = spread_method; }
void add_color_stop(float position, Color color, Optional transition_hint = {})
{
@@ -49,16 +47,13 @@ public:
ReadonlySpan color_stops() const { return m_color_stops; }
Optional repeat_length() const { return m_repeat_length; }
- float scale() const { return m_scale; }
-
virtual ~SVGGradientPaintStyle() {};
protected:
Vector m_color_stops;
Optional m_repeat_length;
- Optional m_inverse_transform {};
- float m_scale { 1.0f };
+ Optional m_gradient_transform {};
SpreadMethod m_spread_method { SpreadMethod::Pad };
};
diff --git a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp
index cdee8fe5b03..1b1045c3ce5 100644
--- a/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/SVGPathPaintable.cpp
@@ -106,7 +106,6 @@ void SVGPathPaintable::paint(PaintContext& context, PaintPhase phase) const
SVG::SVGPaintContext paint_context {
.viewport = svg_viewport,
.path_bounding_box = computed_path()->bounding_box(),
- .transform = paint_transform
};
auto fill_opacity = graphics_element.fill_opacity().value_or(1);
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
index 9b1186fef9d..f3bd4904c0a 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
+++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
@@ -83,15 +83,21 @@ Optional SVGGradientElement::gradient_transform_impl(HashT
// 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);
+ Gfx::AffineTransform gradient_paint_transform;
+ auto const& bounding_box = paint_context.path_bounding_box;
+
+ if (gradient_units() == SVGUnits::ObjectBoundingBox) {
+ // Scale points from 0..1 to bounding box coordinates:
+ gradient_paint_transform.scale(bounding_box.width(), bounding_box.height());
+ } else {
+ // Translate points from viewport to bounding box coordinates:
+ gradient_paint_transform.translate(paint_context.viewport.location() - bounding_box.location());
}
- return Gfx::AffineTransform { paint_context.transform }.multiply(transform);
+
+ if (auto transform = gradient_transform(); transform.has_value())
+ gradient_paint_transform.multiply(transform.value());
+
+ return gradient_paint_transform;
}
void SVGGradientElement::add_color_stops(Painting::SVGGradientPaintStyle& paint_style) const
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
index bbc05d9421a..8365179e7b7 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
+++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
@@ -19,7 +19,6 @@ namespace Web::SVG {
struct SVGPaintContext {
Gfx::FloatRect viewport;
Gfx::FloatRect path_bounding_box;
- Gfx::AffineTransform transform;
};
inline Painting::SVGGradientPaintStyle::SpreadMethod to_painting_spread_method(SpreadMethod spread_method)