diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp index 1fcbf500b4..ad4c2ba96d 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp @@ -49,6 +49,35 @@ Optional 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::xlink_href() const { // FIXME: This entire function is an ad-hoc hack! diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h index d4e975132c..3d98e0a9fd 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h @@ -41,6 +41,8 @@ protected: JS::GCPtr xlink_href() const; + Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const; + template Callback> void for_each_color_stop(Callback const& callback) const { @@ -55,6 +57,8 @@ protected: } } + void add_color_stops(Gfx::SVGGradientPaintStyle&) const; + private: Optional m_gradient_units = {}; Optional m_gradient_transform = {}; diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp index 0ddfe0a0f5..45b8c19d45 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp @@ -103,8 +103,8 @@ Optional 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 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; }