summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/SVG
diff options
context:
space:
mode:
authorMacDue <macdue@dueutil.tech>2023-04-22 18:51:00 +0100
committerAndreas Kling <kling@serenityos.org>2023-04-28 09:42:28 +0200
commitaa3464466ed79d255fdb9670627c25d053da6532 (patch)
treed2d60535bd3a953312d9e18caa081cd164693be9 /Userland/Libraries/LibWeb/SVG
parent89d3c6d71842765569aafd544ea11730d60fbc72 (diff)
downloadserenity-aa3464466ed79d255fdb9670627c25d053da6532.zip
LibWeb: Implement SVGLinearGradientElement (<linearGradient>)
This represents the SVG <linearGradient>. The actual gradient is converted to a Gfx::PaintStyle for use in SVG fills... There is a little guesswork in the implementation, but it seems to match Chrome/Firefox. Note: Still not hooked up to actual painting in this commit.
Diffstat (limited to 'Userland/Libraries/LibWeb/SVG')
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp173
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h56
-rw-r--r--Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.idl9
3 files changed, 238 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
new file mode 100644
index 0000000000..0ddfe0a0f5
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/SVG/AttributeNames.h>
+#include <LibWeb/SVG/AttributeParser.h>
+#include <LibWeb/SVG/SVGLinearGradientElement.h>
+#include <LibWeb/SVG/SVGStopElement.h>
+
+namespace Web::SVG {
+
+SVGLinearGradientElement::SVGLinearGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name)
+ : SVGGradientElement(document, qualified_name)
+{
+}
+
+JS::ThrowCompletionOr<void> SVGLinearGradientElement::initialize(JS::Realm& realm)
+{
+ MUST_OR_THROW_OOM(Base::initialize(realm));
+ set_prototype(&Bindings::ensure_web_prototype<Bindings::SVGLinearGradientElementPrototype>(realm, "SVGLinearGradientElement"));
+
+ return {};
+}
+
+void SVGLinearGradientElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value)
+{
+ SVGGradientElement::parse_attribute(name, value);
+
+ // FIXME: Should allow for `<number-percentage> | <length>` for x1, x2, y1, y2
+ if (name == SVG::AttributeNames::x1) {
+ m_x1 = AttributeParser::parse_number_percentage(value);
+ m_paint_style = nullptr;
+ } else if (name == SVG::AttributeNames::y1) {
+ m_y1 = AttributeParser::parse_number_percentage(value);
+ m_paint_style = nullptr;
+ } else if (name == SVG::AttributeNames::x2) {
+ m_x2 = AttributeParser::parse_number_percentage(value);
+ m_paint_style = nullptr;
+ } else if (name == SVG::AttributeNames::y2) {
+ m_y2 = AttributeParser::parse_number_percentage(value);
+ m_paint_style = nullptr;
+ }
+}
+
+// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute
+NumberPercentage SVGLinearGradientElement::start_x() const
+{
+ if (m_x1.has_value())
+ return *m_x1;
+ if (auto href = linear_gradient_xlink_href())
+ return href->start_x();
+ // If the attribute is not specified, the effect is as if a value of '0%' were specified.
+ return NumberPercentage::create_percentage(0);
+}
+
+// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
+NumberPercentage SVGLinearGradientElement::start_y() const
+{
+ if (m_y1.has_value())
+ return *m_y1;
+ if (auto href = linear_gradient_xlink_href())
+ return href->start_x();
+ // If the attribute is not specified, the effect is as if a value of '0%' were specified.
+ return NumberPercentage::create_percentage(0);
+}
+
+// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
+NumberPercentage SVGLinearGradientElement::end_x() const
+{
+ if (m_x2.has_value())
+ return *m_x2;
+ if (auto href = linear_gradient_xlink_href())
+ return href->start_x();
+ // If the attribute is not specified, the effect is as if a value of '100%' were specified.
+ return NumberPercentage::create_percentage(100);
+}
+
+// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
+NumberPercentage SVGLinearGradientElement::end_y() const
+{
+ if (m_y2.has_value())
+ return *m_y2;
+ if (auto href = linear_gradient_xlink_href())
+ return href->start_x();
+ // If the attribute is not specified, the effect is as if a value of '0%' were specified.
+ return NumberPercentage::create_percentage(0);
+}
+
+Optional<Gfx::PaintStyle const&> SVGLinearGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
+{
+ auto units = gradient_units();
+ // FIXME: Resolve percentages properly
+ Gfx::FloatPoint start_point {};
+ Gfx::FloatPoint end_point {};
+ // https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementGradientUnitsAttribute
+ if (units == GradientUnits::ObjectBoundingBox) {
+ // If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘x1’, ‘y1’, ‘x2’ and ‘y2’
+ // is established using the bounding box of the element to which the gradient is applied (see Object bounding
+ // 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() };
+ } else {
+ // GradientUnits::UserSpaceOnUse
+ // If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system
+ // that results from taking the current user coordinate system in place at the time when the gradient element
+ // is referenced (i.e., the user coordinate system for the element referencing the gradient element via a
+ // fill or stroke property) and then applying the transform specified by attribute ‘gradientTransform’.
+ // Percentages represent values relative to the current SVG viewport.
+ start_point = Gfx::FloatPoint {
+ start_x().resolve_relative_to(paint_context.viewport.width()),
+ start_y().resolve_relative_to(paint_context.viewport.height()),
+ };
+ end_point = Gfx::FloatPoint {
+ end_x().resolve_relative_to(paint_context.viewport.width()),
+ end_y().resolve_relative_to(paint_context.viewport.height()),
+ };
+ }
+
+ 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();
+ });
+ } 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));
+ return *m_paint_style;
+}
+
+JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x1() const
+{
+ TODO();
+}
+
+JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y1() const
+{
+ TODO();
+}
+
+JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x2() const
+{
+ TODO();
+}
+
+JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y2() const
+{
+ TODO();
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
new file mode 100644
index 0000000000..e9e5cc8047
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/SVG/AttributeParser.h>
+#include <LibWeb/SVG/SVGAnimatedLength.h>
+#include <LibWeb/SVG/SVGGradientElement.h>
+
+namespace Web::SVG {
+
+class SVGLinearGradientElement : public SVGGradientElement {
+ WEB_PLATFORM_OBJECT(SVGLinearGradientElement, SVGGradientElement);
+
+public:
+ virtual ~SVGLinearGradientElement() override = default;
+
+ virtual void parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) override;
+
+ virtual Optional<Gfx::PaintStyle const&> to_gfx_paint_style(SVGPaintContext const&) const override;
+
+ JS::NonnullGCPtr<SVGAnimatedLength> x1() const;
+ JS::NonnullGCPtr<SVGAnimatedLength> y1() const;
+ JS::NonnullGCPtr<SVGAnimatedLength> x2() const;
+ JS::NonnullGCPtr<SVGAnimatedLength> y2() const;
+
+protected:
+ SVGLinearGradientElement(DOM::Document&, DOM::QualifiedName);
+
+ virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+private:
+ JS::GCPtr<SVGLinearGradientElement const> linear_gradient_xlink_href() const
+ {
+ if (auto href = xlink_href(); href && is<SVGLinearGradientElement>(*href))
+ return &verify_cast<SVGLinearGradientElement>(*href);
+ return {};
+ }
+
+ NumberPercentage start_x() const;
+ NumberPercentage start_y() const;
+ NumberPercentage end_x() const;
+ NumberPercentage end_y() const;
+
+ Optional<NumberPercentage> m_x1;
+ Optional<NumberPercentage> m_y1;
+ Optional<NumberPercentage> m_x2;
+ Optional<NumberPercentage> m_y2;
+
+ mutable RefPtr<Gfx::SVGLinearGradientPaintStyle> m_paint_style;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.idl b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.idl
new file mode 100644
index 0000000000..49a233a9e4
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.idl
@@ -0,0 +1,9 @@
+#import <SVG/SVGGradientElement.idl>
+
+[Exposed=Window]
+interface SVGLinearGradientElement : SVGGradientElement {
+ [SameObject] readonly attribute SVGAnimatedLength x1;
+ [SameObject] readonly attribute SVGAnimatedLength y1;
+ [SameObject] readonly attribute SVGAnimatedLength x2;
+ [SameObject] readonly attribute SVGAnimatedLength y2;
+};