diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index d70167d809..f9a0ca0035 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -907,6 +907,7 @@ set(SOURCES SVG/SVGAnimationElement.cpp SVG/SVGCircleElement.cpp SVG/SVGClipPathElement.cpp + SVG/SVGComponentTransferFunctionElement.cpp SVG/SVGDecodedImageData.cpp SVG/SVGDefsElement.cpp SVG/SVGDescElement.cpp diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 30013aabfc..7785c97268 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -1098,6 +1098,7 @@ class SVGAnimatedRect; class SVGAnimationElement; class SVGCircleElement; class SVGClipPathElement; +class SVGComponentTransferFunctionElement; class SVGDecodedImageData; class SVGDefsElement; class SVGDescElement; diff --git a/Libraries/LibWeb/SVG/AttributeNames.h b/Libraries/LibWeb/SVG/AttributeNames.h index 80eab21a02..69b070bac5 100644 --- a/Libraries/LibWeb/SVG/AttributeNames.h +++ b/Libraries/LibWeb/SVG/AttributeNames.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021-2023, Andreas Kling + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +12,7 @@ namespace Web::SVG::AttributeNames { #define ENUMERATE_SVG_ATTRIBUTES \ + __ENUMERATE_SVG_ATTRIBUTE(amplitude, "amplitude") \ __ENUMERATE_SVG_ATTRIBUTE(attributeName, "attributeName") \ __ENUMERATE_SVG_ATTRIBUTE(attributeType, "attributeType") \ __ENUMERATE_SVG_ATTRIBUTE(baseFrequency, "baseFrequency") \ @@ -28,6 +30,7 @@ namespace Web::SVG::AttributeNames { __ENUMERATE_SVG_ATTRIBUTE(dy, "dy") \ __ENUMERATE_SVG_ATTRIBUTE(edgeMode, "edgeMode") \ __ENUMERATE_SVG_ATTRIBUTE(fill, "fill") \ + __ENUMERATE_SVG_ATTRIBUTE(exponent, "exponent") \ __ENUMERATE_SVG_ATTRIBUTE(filterUnits, "filterUnits") \ __ENUMERATE_SVG_ATTRIBUTE(fr, "fr") \ __ENUMERATE_SVG_ATTRIBUTE(fx, "fx") \ @@ -39,6 +42,7 @@ namespace Web::SVG::AttributeNames { __ENUMERATE_SVG_ATTRIBUTE(href, "href") \ __ENUMERATE_SVG_ATTRIBUTE(in, "in") \ __ENUMERATE_SVG_ATTRIBUTE(in2, "in2") \ + __ENUMERATE_SVG_ATTRIBUTE(intercept, "intercept") \ __ENUMERATE_SVG_ATTRIBUTE(kernelMatrix, "kernelMatrix") \ __ENUMERATE_SVG_ATTRIBUTE(kernelUnitLength, "kernelUnitLength") \ __ENUMERATE_SVG_ATTRIBUTE(k1, "k1") \ @@ -81,6 +85,7 @@ namespace Web::SVG::AttributeNames { __ENUMERATE_SVG_ATTRIBUTE(result, "result") \ __ENUMERATE_SVG_ATTRIBUTE(rx, "rx") \ __ENUMERATE_SVG_ATTRIBUTE(ry, "ry") \ + __ENUMERATE_SVG_ATTRIBUTE(slope, "slope") \ __ENUMERATE_SVG_ATTRIBUTE(specularConstant, "specularConstant") \ __ENUMERATE_SVG_ATTRIBUTE(specularExponent, "specularExponent") \ __ENUMERATE_SVG_ATTRIBUTE(spreadMethod, "spreadMethod") \ diff --git a/Libraries/LibWeb/SVG/AttributeParser.cpp b/Libraries/LibWeb/SVG/AttributeParser.cpp index 977ef3846c..d3b4dd8090 100644 --- a/Libraries/LibWeb/SVG/AttributeParser.cpp +++ b/Libraries/LibWeb/SVG/AttributeParser.cpp @@ -560,6 +560,27 @@ Optional AttributeParser::parse_spread_method(StringView input) return {}; } +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-tablevalues +Vector AttributeParser::parse_table_values(StringView input) +{ + Vector table_values; + + AttributeParser parser { input }; + while (!parser.done()) { + parser.parse_whitespace(); + auto table_value = parser.parse_nonnegative_number(); + if (table_value.is_error()) + return {}; + + table_values.append(table_value.release_value()); + parser.parse_whitespace(); + if (parser.match(',')) + parser.consume(); + } + + return table_values; +} + // https://drafts.csswg.org/css-transforms/#svg-syntax Optional> AttributeParser::parse_transform() { diff --git a/Libraries/LibWeb/SVG/AttributeParser.h b/Libraries/LibWeb/SVG/AttributeParser.h index 703376860d..f9260349b8 100644 --- a/Libraries/LibWeb/SVG/AttributeParser.h +++ b/Libraries/LibWeb/SVG/AttributeParser.h @@ -148,6 +148,7 @@ public: static Optional parse_preserve_aspect_ratio(StringView input); static Optional parse_units(StringView input); static Optional parse_spread_method(StringView input); + static Vector parse_table_values(StringView); static Optional parse_viewbox(StringView input); private: diff --git a/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.cpp b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.cpp new file mode 100644 index 0000000000..b76883c53c --- /dev/null +++ b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2025, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Web::SVG { + +SVGComponentTransferFunctionElement::SVGComponentTransferFunctionElement(DOM::Document& document, DOM::QualifiedName qualified_name) + : SVGElement(document, move(qualified_name)) +{ +} + +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-type +static SVGComponentTransferFunctionElement::Type parse_type(Optional const& value) +{ + if (value == "identity"sv) + return SVGComponentTransferFunctionElement::Type::Identity; + if (value == "table"sv) + return SVGComponentTransferFunctionElement::Type::Table; + if (value == "discrete"sv) + return SVGComponentTransferFunctionElement::Type::Discrete; + if (value == "linear"sv) + return SVGComponentTransferFunctionElement::Type::Linear; + if (value == "gamma"sv) + return SVGComponentTransferFunctionElement::Type::Gamma; + + return SVGComponentTransferFunctionElement::Type::Unknown; +} + +void SVGComponentTransferFunctionElement::attribute_changed(FlyString const& name, Optional const& old_value, + Optional const& value, Optional const& namespace_) +{ + Base::attribute_changed(name, old_value, value, namespace_); + + // FIXME: Support reflection instead of invalidating the enumeration. + if (name == AttributeNames::type) + m_type = {}; + + // FIXME: Support reflection instead of invalidating the list. + if (name == AttributeNames::tableValues) + m_table_values = {}; +} + +void SVGComponentTransferFunctionElement::initialize(JS::Realm& realm) +{ + WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGComponentTransferFunctionElement); + Base::initialize(realm); +} + +void SVGComponentTransferFunctionElement::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_type); + visitor.visit(m_table_values); + visitor.visit(m_slope); + visitor.visit(m_intercept); + visitor.visit(m_amplitude); + visitor.visit(m_exponent); + visitor.visit(m_offset); +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-type +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-type +GC::Ref SVGComponentTransferFunctionElement::type() +{ + if (!m_type) + m_type = SVGAnimatedEnumeration::create(realm(), to_underlying(type_from_attribute())); + return *m_type; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-tablevalues +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-tablevalues +GC::Ref SVGComponentTransferFunctionElement::table_values() +{ + if (!m_table_values) { + auto numbers = AttributeParser::parse_table_values(get_attribute_value(AttributeNames::tableValues)); + + Vector> items; + items.ensure_capacity(numbers.size()); + for (auto number : numbers) + items.unchecked_append(SVGNumber::create(realm(), number, SVGNumber::ReadOnly::Yes)); + + auto number_list = SVGNumberList::create(realm(), move(items), ReadOnlyList::Yes); + m_table_values = SVGAnimatedNumberList::create(realm(), number_list); + } + return *m_table_values; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-slope +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-slope +GC::Ref SVGComponentTransferFunctionElement::slope() +{ + if (!m_slope) + m_slope = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::slope, {}, {} }, 1.f); + return *m_slope; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-intercept +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-intercept +GC::Ref SVGComponentTransferFunctionElement::intercept() +{ + if (!m_intercept) + m_intercept = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::intercept, {}, {} }, 0.f); + return *m_intercept; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-amplitude +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-amplitude +GC::Ref SVGComponentTransferFunctionElement::amplitude() +{ + if (!m_amplitude) + m_amplitude = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::amplitude, {}, {} }, 1.f); + return *m_amplitude; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-exponent +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-exponent +GC::Ref SVGComponentTransferFunctionElement::exponent() +{ + if (!m_exponent) + m_exponent = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::exponent, {}, {} }, 1.f); + return *m_exponent; +} + +// https://www.w3.org/TR/filter-effects-1/#dom-svgcomponenttransferfunctionelement-offset +// https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fecomponenttransfer-offset +GC::Ref SVGComponentTransferFunctionElement::offset() +{ + if (!m_offset) + m_offset = SVGAnimatedNumber::create(realm(), *this, DOM::QualifiedName { AttributeNames::offset, {}, {} }, 0.f); + return *m_offset; +} + +SVGComponentTransferFunctionElement::Type SVGComponentTransferFunctionElement::type_from_attribute() const +{ + return parse_type(get_attribute_value(AttributeNames::type)); +} + +} diff --git a/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.h b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.h new file mode 100644 index 0000000000..97832e0503 --- /dev/null +++ b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025, Jelle Raaijmakers + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::SVG { + +// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGComponentTransferFunctionElement +class SVGComponentTransferFunctionElement + : public SVGElement { + WEB_PLATFORM_OBJECT(SVGComponentTransferFunctionElement, SVGElement); + +public: + enum class Type : u8 { + Unknown = 0, + Identity = 1, + Table = 2, + Discrete = 3, + Linear = 4, + Gamma = 5, + }; + + virtual ~SVGComponentTransferFunctionElement() override = default; + + virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; + + GC::Ref type(); + GC::Ref table_values(); + GC::Ref slope(); + GC::Ref intercept(); + GC::Ref amplitude(); + GC::Ref exponent(); + GC::Ref offset(); + +protected: + SVGComponentTransferFunctionElement(DOM::Document&, DOM::QualifiedName); + + virtual void initialize(JS::Realm&) override; + +private: + virtual void visit_edges(Cell::Visitor&) override; + + Type type_from_attribute() const; + + GC::Ptr m_type; + GC::Ptr m_table_values; + GC::Ptr m_slope; + GC::Ptr m_intercept; + GC::Ptr m_amplitude; + GC::Ptr m_exponent; + GC::Ptr m_offset; +}; + +} diff --git a/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.idl b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.idl new file mode 100644 index 0000000000..6a604b3075 --- /dev/null +++ b/Libraries/LibWeb/SVG/SVGComponentTransferFunctionElement.idl @@ -0,0 +1,22 @@ +#import + +// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGComponentTransferFunctionElement +[Exposed=Window] +interface SVGComponentTransferFunctionElement : SVGElement { + + // Component Transfer Types + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0; + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1; + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE = 2; + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3; + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4; + const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5; + + readonly attribute SVGAnimatedEnumeration type; + readonly attribute SVGAnimatedNumberList tableValues; + readonly attribute SVGAnimatedNumber slope; + readonly attribute SVGAnimatedNumber intercept; + readonly attribute SVGAnimatedNumber amplitude; + readonly attribute SVGAnimatedNumber exponent; + readonly attribute SVGAnimatedNumber offset; +}; diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index d247d8a60c..fb3acb7a19 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -378,6 +378,7 @@ libweb_js_bindings(SVG/SVGAnimatedString) libweb_js_bindings(SVG/SVGAnimatedTransformList) libweb_js_bindings(SVG/SVGAnimationElement) libweb_js_bindings(SVG/SVGClipPathElement) +libweb_js_bindings(SVG/SVGComponentTransferFunctionElement) libweb_js_bindings(SVG/SVGDefsElement) libweb_js_bindings(SVG/SVGDescElement) libweb_js_bindings(SVG/SVGElement) diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index 130e843487..acdb3e99e4 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -372,6 +372,7 @@ SVGAnimatedTransformList SVGAnimationElement SVGCircleElement SVGClipPathElement +SVGComponentTransferFunctionElement SVGDefsElement SVGDescElement SVGElement