mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Implement SVGFEColorMatrixElement and feColorMatrix
This commit is contained in:
parent
b50b89b4a8
commit
5ee1031b89
Notes:
github-actions[bot]
2025-10-23 11:37:46 +00:00
Author: https://github.com/shlyakpavel
Commit: 5ee1031b89
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6262
Reviewed-by: https://github.com/gmta ✅
Reviewed-by: https://github.com/konradekk
21 changed files with 367 additions and 0 deletions
|
|
@ -94,6 +94,7 @@ namespace Web::SVG::AttributeNames {
|
|||
__ENUMERATE_SVG_ATTRIBUTE(targetY, "targetY") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(textLength, "textLength") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(type, "type") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(values, "values") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(version, "version") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(viewBox, "viewBox") \
|
||||
__ENUMERATE_SVG_ATTRIBUTE(viewTarget, "viewTarget") \
|
||||
|
|
|
|||
69
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.cpp
Normal file
69
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Pavel Shliak <shlyakpavel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/SVGFEColorMatrixElementPrototype.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedEnumeration.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedString.h>
|
||||
#include <LibWeb/SVG/SVGFEColorMatrixElement.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(SVGFEColorMatrixElement);
|
||||
|
||||
SVGFEColorMatrixElement::SVGFEColorMatrixElement(DOM::Document& document, DOM::QualifiedName qualified_name)
|
||||
: SVGElement(document, qualified_name)
|
||||
{
|
||||
}
|
||||
|
||||
void SVGFEColorMatrixElement::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGFEColorMatrixElement);
|
||||
Base::initialize(realm);
|
||||
}
|
||||
|
||||
void SVGFEColorMatrixElement::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
SVGFilterPrimitiveStandardAttributes::visit_edges(visitor);
|
||||
visitor.visit(m_in1);
|
||||
visitor.visit(m_values);
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedString> SVGFEColorMatrixElement::in1()
|
||||
{
|
||||
if (!m_in1)
|
||||
m_in1 = SVGAnimatedString::create(realm(), *this, AttributeNames::in);
|
||||
return *m_in1;
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedEnumeration> SVGFEColorMatrixElement::type() const
|
||||
{
|
||||
// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEColorMatrixElement
|
||||
// Map the 'type' attribute to the IDL enumeration values.
|
||||
// Defaults to MATRIX when omitted.
|
||||
auto type_attribute = attribute(AttributeNames::type).value_or(String {});
|
||||
|
||||
u16 enum_value = SVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_UNKNOWN;
|
||||
if (type_attribute.is_empty() || type_attribute.equals_ignoring_ascii_case("matrix"sv))
|
||||
enum_value = SVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_MATRIX;
|
||||
else if (type_attribute.equals_ignoring_ascii_case("saturate"sv))
|
||||
enum_value = SVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_SATURATE;
|
||||
else if (type_attribute.equals_ignoring_ascii_case("hueRotate"sv))
|
||||
enum_value = SVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_HUEROTATE;
|
||||
else if (type_attribute.equals_ignoring_ascii_case("luminanceToAlpha"sv))
|
||||
enum_value = SVGFEColorMatrixElement::SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA;
|
||||
|
||||
return SVGAnimatedEnumeration::create(realm(), enum_value);
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedString> SVGFEColorMatrixElement::values()
|
||||
{
|
||||
if (!m_values)
|
||||
m_values = SVGAnimatedString::create(realm(), *this, AttributeNames::values);
|
||||
return *m_values;
|
||||
}
|
||||
|
||||
}
|
||||
47
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.h
Normal file
47
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Pavel Shliak <shlyakpavel@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/SVG/SVGElement.h>
|
||||
#include <LibWeb/SVG/SVGFilterPrimitiveStandardAttributes.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
||||
class SVGAnimatedEnumeration;
|
||||
class SVGAnimatedString;
|
||||
|
||||
// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEColorMatrixElement
|
||||
class SVGFEColorMatrixElement final
|
||||
: public SVGElement
|
||||
, public SVGFilterPrimitiveStandardAttributes<SVGFEColorMatrixElement> {
|
||||
WEB_PLATFORM_OBJECT(SVGFEColorMatrixElement, SVGElement);
|
||||
GC_DECLARE_ALLOCATOR(SVGFEColorMatrixElement);
|
||||
|
||||
public:
|
||||
virtual ~SVGFEColorMatrixElement() override = default;
|
||||
|
||||
static constexpr unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
|
||||
static constexpr unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
|
||||
static constexpr unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
|
||||
static constexpr unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
|
||||
static constexpr unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
|
||||
|
||||
// IDL attributes
|
||||
GC::Ref<SVGAnimatedString> in1();
|
||||
GC::Ref<SVGAnimatedEnumeration> type() const;
|
||||
GC::Ref<SVGAnimatedString> values();
|
||||
|
||||
private:
|
||||
SVGFEColorMatrixElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
GC::Ptr<SVGAnimatedString> m_in1;
|
||||
GC::Ptr<SVGAnimatedString> m_values;
|
||||
};
|
||||
|
||||
}
|
||||
23
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.idl
Normal file
23
Libraries/LibWeb/SVG/SVGFEColorMatrixElement.idl
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#import <SVG/SVGAnimatedString.idl>
|
||||
#import <SVG/SVGAnimatedEnumeration.idl>
|
||||
#import <SVG/SVGElement.idl>
|
||||
#import <SVG/SVGFilterPrimitiveStandardAttributes.idl>
|
||||
|
||||
// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEColorMatrixElement
|
||||
[Exposed=Window]
|
||||
interface SVGFEColorMatrixElement : SVGElement {
|
||||
|
||||
// Color Matrix Types
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
|
||||
|
||||
readonly attribute SVGAnimatedString in1;
|
||||
readonly attribute SVGAnimatedEnumeration type;
|
||||
// FIXME: Use SVGAnimatedNumberList when implemented.
|
||||
readonly attribute SVGAnimatedString values;
|
||||
};
|
||||
|
||||
SVGFEColorMatrixElement includes SVGFilterPrimitiveStandardAttributes;
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/StringConversions.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibWeb/Bindings/SVGFilterElementPrototype.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/SVG/SVGFEBlendElement.h>
|
||||
#include <LibWeb/SVG/SVGFEColorMatrixElement.h>
|
||||
#include <LibWeb/SVG/SVGFECompositeElement.h>
|
||||
#include <LibWeb/SVG/SVGFEFloodElement.h>
|
||||
#include <LibWeb/SVG/SVGFEGaussianBlurElement.h>
|
||||
|
|
@ -140,6 +142,83 @@ Optional<Gfx::Filter> SVGFilterElement::gfx_filter(Layout::NodeWithStyle const&
|
|||
|
||||
root_filter = Gfx::Filter::blur(radius_x, radius_y, input);
|
||||
update_result_map(*blur_primitive);
|
||||
} else if (auto* colormatrix_primitive = as_if<SVGFEColorMatrixElement>(node)) {
|
||||
auto in_attr = colormatrix_primitive->in1()->base_val();
|
||||
auto input = resolve_input_filter(in_attr);
|
||||
|
||||
auto type_value = colormatrix_primitive->attribute(AttributeNames::type).value_or(String {});
|
||||
auto values_value = colormatrix_primitive->attribute(AttributeNames::values).value_or(String {});
|
||||
|
||||
// Default type is "matrix" per spec.
|
||||
if (type_value.is_empty() || type_value.equals_ignoring_ascii_case("matrix"sv)) {
|
||||
// Parse up to 20 numbers; if we don't get a full 4x5, skip applying.
|
||||
float matrix[20] = { 0 };
|
||||
size_t count = 0;
|
||||
|
||||
StringView sv = values_value;
|
||||
auto skip_leading_whitespace = [&] {
|
||||
sv = sv.trim_whitespace(AK::TrimMode::Left);
|
||||
};
|
||||
auto consume_comma_and_whitespace = [&] {
|
||||
if (!sv.is_empty() && sv[0] == ',')
|
||||
sv = sv.substring_view(1);
|
||||
skip_leading_whitespace();
|
||||
};
|
||||
|
||||
skip_leading_whitespace();
|
||||
while (!sv.is_empty() && count < 20) {
|
||||
// Parse the next number without trimming (we already trimmed on the left).
|
||||
auto result = AK::parse_first_number<float>(sv, AK::TrimWhitespace::No);
|
||||
if (!result.has_value())
|
||||
break;
|
||||
matrix[count++] = result->value;
|
||||
// Advance exactly past the number just parsed, then consume optional comma + whitespace.
|
||||
sv = sv.substring_view(result->characters_parsed);
|
||||
consume_comma_and_whitespace();
|
||||
}
|
||||
|
||||
if (count == 20) {
|
||||
root_filter = Gfx::Filter::color_matrix(matrix, input);
|
||||
update_result_map(*colormatrix_primitive);
|
||||
} else {
|
||||
// If invalid or missing, treat as identity (no-op) if we already have an input.
|
||||
if (input.has_value()) {
|
||||
root_filter = input;
|
||||
update_result_map(*colormatrix_primitive);
|
||||
}
|
||||
}
|
||||
} else if (type_value.equals_ignoring_ascii_case("saturate"sv)) {
|
||||
// values: single number s (1 = original)
|
||||
float s = 1.0f;
|
||||
if (!values_value.is_empty()) {
|
||||
if (auto parsed = AK::parse_number<float>(values_value, AK::TrimWhitespace::Yes); parsed.has_value())
|
||||
s = *parsed;
|
||||
}
|
||||
root_filter = Gfx::Filter::saturate(s, input);
|
||||
update_result_map(*colormatrix_primitive);
|
||||
} else if (type_value.equals_ignoring_ascii_case("hueRotate"sv)) {
|
||||
// values: angle in degrees
|
||||
float angle_degrees = 0.0f;
|
||||
if (!values_value.is_empty()) {
|
||||
if (auto parsed = AK::parse_number<float>(values_value, AK::TrimWhitespace::Yes); parsed.has_value())
|
||||
angle_degrees = *parsed;
|
||||
}
|
||||
root_filter = Gfx::Filter::hue_rotate(angle_degrees, input);
|
||||
update_result_map(*colormatrix_primitive);
|
||||
} else if (type_value.equals_ignoring_ascii_case("luminanceToAlpha"sv)) {
|
||||
// values ignored; convert luminance to alpha and zero RGB.
|
||||
float matrix[20] = {
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0.2126f, 0.7152f, 0.0722f, 0, 0
|
||||
};
|
||||
root_filter = Gfx::Filter::color_matrix(matrix, input);
|
||||
update_result_map(*colormatrix_primitive);
|
||||
} else {
|
||||
// Unknown 'type' value on feColorMatrix; skip creating a filter and log.
|
||||
dbgln("SVGFEColorMatrixElement: Unknown type '{}' — skipping filter primitive", type_value);
|
||||
}
|
||||
} else if (auto* image_primitive = as_if<SVGFEImageElement>(node)) {
|
||||
auto bitmap = image_primitive->current_image_bitmap({});
|
||||
if (!bitmap)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Web::SVG::TagNames {
|
|||
__ENUMERATE_SVG_TAG(desc) \
|
||||
__ENUMERATE_SVG_TAG(ellipse) \
|
||||
__ENUMERATE_SVG_TAG(feBlend) \
|
||||
__ENUMERATE_SVG_TAG(feColorMatrix) \
|
||||
__ENUMERATE_SVG_TAG(feComposite) \
|
||||
__ENUMERATE_SVG_TAG(feFlood) \
|
||||
__ENUMERATE_SVG_TAG(feGaussianBlur) \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue