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
|
|
@ -912,6 +912,7 @@ set(SOURCES
|
|||
SVG/SVGElement.cpp
|
||||
SVG/SVGEllipseElement.cpp
|
||||
SVG/SVGFEBlendElement.cpp
|
||||
SVG/SVGFEColorMatrixElement.cpp
|
||||
SVG/SVGFECompositeElement.cpp
|
||||
SVG/SVGFEFloodElement.cpp
|
||||
SVG/SVGFEGaussianBlurElement.cpp
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
#include <LibWeb/SVG/SVGDescElement.h>
|
||||
#include <LibWeb/SVG/SVGEllipseElement.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>
|
||||
|
|
@ -478,6 +479,8 @@ static GC::Ref<SVG::SVGElement> create_svg_element(JS::Realm& realm, Document& d
|
|||
return realm.create<SVG::SVGFECompositeElement>(document, move(qualified_name));
|
||||
if (local_name == SVG::TagNames::feFlood)
|
||||
return realm.create<SVG::SVGFEFloodElement>(document, move(qualified_name));
|
||||
if (local_name == SVG::TagNames::feColorMatrix)
|
||||
return realm.create<SVG::SVGFEColorMatrixElement>(document, move(qualified_name));
|
||||
if (local_name == SVG::TagNames::feGaussianBlur)
|
||||
return realm.create<SVG::SVGFEGaussianBlurElement>(document, move(qualified_name));
|
||||
if (local_name == SVG::TagNames::feImage)
|
||||
|
|
|
|||
|
|
@ -1098,6 +1098,7 @@ class SVGDescElement;
|
|||
class SVGElement;
|
||||
class SVGEllipseElement;
|
||||
class SVGFEBlendElement;
|
||||
class SVGFEColorMatrixElement;
|
||||
class SVGFECompositeElement;
|
||||
class SVGFEFloodElement;
|
||||
class SVGFEGaussianBlurElement;
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ libweb_js_bindings(SVG/SVGImageElement)
|
|||
libweb_js_bindings(SVG/SVGCircleElement)
|
||||
libweb_js_bindings(SVG/SVGEllipseElement)
|
||||
libweb_js_bindings(SVG/SVGFEBlendElement)
|
||||
libweb_js_bindings(SVG/SVGFEColorMatrixElement)
|
||||
libweb_js_bindings(SVG/SVGFECompositeElement)
|
||||
libweb_js_bindings(SVG/SVGFEFloodElement)
|
||||
libweb_js_bindings(SVG/SVGFEGaussianBlurElement)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin:0; background:white">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- hueRotate(90deg) of #cc3366 -> approx #665F00 -->
|
||||
<rect x="10" y="10" width="180" height="180" fill="#665F00"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin:0; background:white">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- luminanceToAlpha of red (alpha=0.2126) over white -> approx #C9C9C9 -->
|
||||
<rect x="10" y="10" width="180" height="180" fill="#C9C9C9"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin:0; background:white">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#336699"/>
|
||||
</svg>
|
||||
<!-- Identity matrix should be a no-op. -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin:0; background:white">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Inverted #336699 -> #CC9966 -->
|
||||
<rect x="10" y="10" width="180" height="180" fill="#CC9966"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body style="margin:0; background:white">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Approximate saturate(2) of #6d98cc -> #479DFF -->
|
||||
<rect x="10" y="10" width="180" height="180" fill="#479DFF"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
17
Tests/LibWeb/Ref/input/svg-fecolormatrix-huerotate.html
Normal file
17
Tests/LibWeb/Ref/input/svg-fecolormatrix-huerotate.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-fecolormatrix-huerotate-ref.html" />
|
||||
<meta name="fuzzy" content="maxDifference=0-2;totalPixels=0-12000">
|
||||
<style>
|
||||
html, body { margin: 0; background: white; }
|
||||
</style>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="cm_hue">
|
||||
<feColorMatrix type="hueRotate" values="90"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#cm_hue)">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#cc3366"/>
|
||||
</g>
|
||||
<!-- Core: feColorMatrix hueRotate in degrees -->
|
||||
</svg>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-fecolormatrix-luminance-to-alpha-ref.html" />
|
||||
<meta name="fuzzy" content="maxDifference=0-2;totalPixels=0-12000">
|
||||
<style>
|
||||
html, body { margin: 0; background: white; }
|
||||
</style>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="cm_luma">
|
||||
<feColorMatrix type="luminanceToAlpha"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- Red square filtered to alpha; will render as black with alpha equal to luminance -->
|
||||
<g filter="url(#cm_luma)">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#ff0000"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-fecolormatrix-matrix-identity-ref.html" />
|
||||
<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-10000">
|
||||
<style>
|
||||
html, body { margin: 0; background: white; }
|
||||
</style>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="cm_identity">
|
||||
<feColorMatrix type="matrix"
|
||||
values="1 0 0 0 0
|
||||
0 1 0 0 0
|
||||
0 0 1 0 0
|
||||
0 0 0 1 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#cm_identity)">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#336699"/>
|
||||
</g>
|
||||
Sorry, your browser does not support inline SVG.
|
||||
<!-- Core: feColorMatrix identity should be a visual no-op. -->
|
||||
</svg>
|
||||
22
Tests/LibWeb/Ref/input/svg-fecolormatrix-matrix-invert.html
Normal file
22
Tests/LibWeb/Ref/input/svg-fecolormatrix-matrix-invert.html
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-fecolormatrix-matrix-invert-ref.html" />
|
||||
<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-10000">
|
||||
<style>
|
||||
html, body { margin: 0; background: white; }
|
||||
</style>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="cm_invert">
|
||||
<!-- Invert: R' = 1 - R, etc. -->
|
||||
<feColorMatrix type="matrix"
|
||||
values="-1 0 0 0 1
|
||||
0 -1 0 0 1
|
||||
0 0 -1 0 1
|
||||
0 0 0 1 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#cm_invert)">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#336699"/>
|
||||
</g>
|
||||
<!-- Core: feColorMatrix matrix with bias in normalized 0..1. -->
|
||||
</svg>
|
||||
17
Tests/LibWeb/Ref/input/svg-fecolormatrix-saturate.html
Normal file
17
Tests/LibWeb/Ref/input/svg-fecolormatrix-saturate.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../expected/svg-fecolormatrix-saturate-ref.html" />
|
||||
<meta name="fuzzy" content="maxDifference=0-2;totalPixels=0-12000">
|
||||
<style>
|
||||
html, body { margin: 0; background: white; }
|
||||
</style>
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="cm_saturate">
|
||||
<feColorMatrix type="saturate" values="2"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g filter="url(#cm_saturate)">
|
||||
<rect x="10" y="10" width="180" height="180" fill="#6d98cc"/>
|
||||
</g>
|
||||
<!-- Core: feColorMatrix saturate -->
|
||||
</svg>
|
||||
|
|
@ -376,6 +376,7 @@ SVGDescElement
|
|||
SVGElement
|
||||
SVGEllipseElement
|
||||
SVGFEBlendElement
|
||||
SVGFEColorMatrixElement
|
||||
SVGFECompositeElement
|
||||
SVGFEFloodElement
|
||||
SVGFEGaussianBlurElement
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue